Compare commits

..

4 Commits

Author SHA1 Message Date
akwizgran
b6b15fe657 Only cache attachment items that include size. 2019-06-19 13:30:06 +01:00
akwizgran
f3bbc7179e Move deletion of unsent attachments into task. 2019-06-19 13:30:06 +01:00
akwizgran
9abe32ab4b Refactor attachment code to reduce mutable state. 2019-06-19 13:30:06 +01:00
akwizgran
d07b98eae1 Code cleanups, javadoc. 2019-06-19 13:30:05 +01:00
774 changed files with 5784 additions and 16045 deletions

View File

@@ -17,7 +17,7 @@ test:
script: script:
- ./gradlew --no-daemon -Djava.security.egd=file:/dev/urandom animalSnifferMain animalSnifferTest - ./gradlew --no-daemon -Djava.security.egd=file:/dev/urandom animalSnifferMain animalSnifferTest
- ./gradlew --no-daemon -Djava.security.egd=file:/dev/urandom check compileOfficialDebugAndroidTestSources compileScreenshotDebugAndroidTestSources - ./gradlew --no-daemon -Djava.security.egd=file:/dev/urandom test
after_script: after_script:
# these file change every time but should not be cached # these file change every time but should not be cached

View File

@@ -1,10 +1,16 @@
<component name="ProjectCodeStyleConfiguration"> <component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173"> <code_scheme name="Project" version="173">
<option name="RIGHT_MARGIN" value="100" />
<AndroidXmlCodeStyleSettings> <AndroidXmlCodeStyleSettings>
<option name="ARRANGEMENT_SETTINGS_MIGRATED_TO_191" value="true" /> <option name="USE_CUSTOM_SETTINGS" value="true" />
</AndroidXmlCodeStyleSettings> </AndroidXmlCodeStyleSettings>
<JavaCodeStyleSettings> <JavaCodeStyleSettings>
<option name="ANNOTATION_PARAMETER_WRAP" value="1" /> <option name="ANNOTATION_PARAMETER_WRAP" value="1" />
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
<option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
<value />
</option>
<option name="IMPORT_LAYOUT_TABLE"> <option name="IMPORT_LAYOUT_TABLE">
<value> <value>
<package name="android" withSubpackages="true" static="false" /> <package name="android" withSubpackages="true" static="false" />
@@ -71,6 +77,7 @@
</indentOptions> </indentOptions>
</codeStyleSettings> </codeStyleSettings>
<codeStyleSettings language="XML"> <codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions> <indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" /> <option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="USE_TAB_CHARACTER" value="true" /> <option name="USE_TAB_CHARACTER" value="true" />
@@ -83,8 +90,7 @@
<match> <match>
<AND> <AND>
<NAME>xmlns:android</NAME> <NAME>xmlns:android</NAME>
<XML_ATTRIBUTE /> <XML_NAMESPACE>Namespace:</XML_NAMESPACE>
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND> </AND>
</match> </match>
</rule> </rule>
@@ -94,8 +100,7 @@
<match> <match>
<AND> <AND>
<NAME>xmlns:.*</NAME> <NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE /> <XML_NAMESPACE>Namespace:</XML_NAMESPACE>
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND> </AND>
</match> </match>
<order>BY_NAME</order> <order>BY_NAME</order>
@@ -106,7 +111,6 @@
<match> <match>
<AND> <AND>
<NAME>.*:id</NAME> <NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE> <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND> </AND>
</match> </match>
@@ -117,7 +121,6 @@
<match> <match>
<AND> <AND>
<NAME>.*:name</NAME> <NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE> <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND> </AND>
</match> </match>
@@ -128,7 +131,6 @@
<match> <match>
<AND> <AND>
<NAME>name</NAME> <NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE> <XML_NAMESPACE>^$</XML_NAMESPACE>
</AND> </AND>
</match> </match>
@@ -139,7 +141,6 @@
<match> <match>
<AND> <AND>
<NAME>style</NAME> <NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE> <XML_NAMESPACE>^$</XML_NAMESPACE>
</AND> </AND>
</match> </match>
@@ -150,7 +151,6 @@
<match> <match>
<AND> <AND>
<NAME>.*</NAME> <NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE> <XML_NAMESPACE>^$</XML_NAMESPACE>
</AND> </AND>
</match> </match>
@@ -161,12 +161,64 @@
<rule> <rule>
<match> <match>
<AND> <AND>
<NAME>.*</NAME> <NAME>.*:layout_width</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE> <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND> </AND>
</match> </match>
<order>ANDROID_ATTRIBUTE_ORDER</order> </rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_height</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_.*</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:width</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:height</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule> </rule>
</section> </section>
<section> <section>
@@ -174,7 +226,6 @@
<match> <match>
<AND> <AND>
<NAME>.*</NAME> <NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE> <XML_NAMESPACE>.*</XML_NAMESPACE>
</AND> </AND>
</match> </match>

View File

@@ -5,31 +5,23 @@ apply plugin: 'witness'
apply from: 'witness.gradle' apply from: 'witness.gradle'
android { android {
compileSdkVersion 29 compileSdkVersion 28
buildToolsVersion '29.0.2' buildToolsVersion '28.0.3'
defaultConfig { defaultConfig {
minSdkVersion 16 minSdkVersion 14
targetSdkVersion 28 targetSdkVersion 26
versionCode 10207 versionCode 10107
versionName "1.2.7" versionName "1.1.7"
consumerProguardFiles 'proguard-rules.txt' consumerProguardFiles 'proguard-rules.txt'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
} }
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
} }
lintOptions {
// FIXME
warning "LintError"
warning "InvalidPackage"
warning "MissingPermission"
warning "InlinedApi", "ObsoleteSdkInt", "Override", "NewApi", "UnusedAttribute"
}
} }
configurations { configurations {
@@ -38,10 +30,10 @@ configurations {
dependencies { dependencies {
implementation project(path: ':bramble-core', configuration: 'default') implementation project(path: ':bramble-core', configuration: 'default')
tor 'org.briarproject:tor-android:0.3.5.10@zip' tor 'org.briarproject:tor-android:0.3.5.8@zip'
tor 'org.briarproject:obfs4proxy-android:0.0.11-2@zip' tor 'org.briarproject:obfs4proxy-android:0.0.9@zip'
annotationProcessor 'com.google.dagger:dagger-compiler:2.24' annotationProcessor 'com.google.dagger:dagger-compiler:2.22.1'
compileOnly 'javax.annotation:jsr250-api:1.0' compileOnly 'javax.annotation:jsr250-api:1.0'
@@ -67,8 +59,6 @@ task unpackTorBinaries {
copy { copy {
from configurations.tor.collect { zipTree(it) } from configurations.tor.collect { zipTree(it) }
into torBinariesDir into torBinariesDir
// TODO: Remove after next Tor upgrade, which won't include non-PIE binaries
include 'geoip.zip', '*_pie.zip'
} }
} }
dependsOn cleanTorBinaries dependsOn cleanTorBinaries

View File

@@ -11,14 +11,4 @@ public interface BrambleAndroidEagerSingletons {
void inject(AndroidNetworkModule.EagerSingletons init); void inject(AndroidNetworkModule.EagerSingletons init);
void inject(ReportingModule.EagerSingletons init); void inject(ReportingModule.EagerSingletons init);
class Helper {
public static void injectEagerSingletons(
BrambleAndroidEagerSingletons c) {
c.inject(new AndroidBatteryModule.EagerSingletons());
c.inject(new AndroidNetworkModule.EagerSingletons());
c.inject(new ReportingModule.EagerSingletons());
}
}
} }

View File

@@ -18,4 +18,10 @@ import dagger.Module;
SocksModule.class SocksModule.class
}) })
public class BrambleAndroidModule { public class BrambleAndroidModule {
public static void initEagerSingletons(BrambleAndroidEagerSingletons c) {
c.inject(new AndroidBatteryModule.EagerSingletons());
c.inject(new AndroidNetworkModule.EagerSingletons());
c.inject(new ReportingModule.EagerSingletons());
}
} }

View File

@@ -12,16 +12,13 @@ import org.briarproject.bramble.api.identity.IdentityManager;
import java.io.File; import java.io.File;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import javax.inject.Inject; import javax.inject.Inject;
import static android.os.Build.VERSION.SDK_INT; import static android.os.Build.VERSION.SDK_INT;
import static java.util.Arrays.asList;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static org.briarproject.bramble.util.IoUtils.deleteFileOrDir; import static org.briarproject.bramble.util.IoUtils.deleteFileOrDir;
import static org.briarproject.bramble.util.LogUtils.logFileOrDir; import static org.briarproject.bramble.util.LogUtils.logFileOrDir;
@@ -32,11 +29,7 @@ class AndroidAccountManager extends AccountManagerImpl
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(AndroidAccountManager.class.getName()); Logger.getLogger(AndroidAccountManager.class.getName());
/** private static final String PREF_DB_KEY = "key";
* Directories that shouldn't be deleted when deleting the user's account.
*/
private static final List<String> PROTECTED_DIR_NAMES =
asList("cache", "code_cache", "lib", "shared_prefs");
protected final Context appContext; protected final Context appContext;
private final SharedPreferences prefs; private final SharedPreferences prefs;
@@ -60,6 +53,36 @@ class AndroidAccountManager extends AccountManagerImpl
return exists; return exists;
} }
// Locking: stateChangeLock
@Override
@Nullable
protected String loadEncryptedDatabaseKey() {
String key = getDatabaseKeyFromPreferences();
if (key == null) key = super.loadEncryptedDatabaseKey();
else migrateDatabaseKeyToFile(key);
return key;
}
// Locking: stateChangeLock
@Nullable
private String getDatabaseKeyFromPreferences() {
String key = prefs.getString(PREF_DB_KEY, null);
if (key == null) LOG.info("No database key in preferences");
else LOG.info("Found database key in preferences");
return key;
}
// Locking: stateChangeLock
private void migrateDatabaseKeyToFile(String key) {
if (storeEncryptedDatabaseKey(key)) {
if (prefs.edit().remove(PREF_DB_KEY).commit())
LOG.info("Database key migrated to file");
else LOG.warning("Database key not removed from preferences");
} else {
LOG.warning("Database key not migrated to file");
}
}
@Override @Override
public void deleteAccount() { public void deleteAccount() {
synchronized (stateChangeLock) { synchronized (stateChangeLock) {
@@ -82,14 +105,14 @@ class AndroidAccountManager extends AccountManagerImpl
return PreferenceManager.getDefaultSharedPreferences(appContext); return PreferenceManager.getDefaultSharedPreferences(appContext);
} }
@GuardedBy("stateChangeLock") // Locking: stateChangeLock
private void deleteAppData(SharedPreferences... clear) { private void deleteAppData(SharedPreferences... clear) {
// Clear and commit shared preferences // Clear and commit shared preferences
for (SharedPreferences prefs : clear) { for (SharedPreferences prefs : clear) {
if (!prefs.edit().clear().commit()) if (!prefs.edit().clear().commit())
LOG.warning("Could not clear shared preferences"); LOG.warning("Could not clear shared preferences");
} }
// Delete files, except protected directories // Delete files, except lib and shared_prefs directories
Set<File> files = new HashSet<>(); Set<File> files = new HashSet<>();
File dataDir = getDataDir(); File dataDir = getDataDir();
@Nullable @Nullable
@@ -98,12 +121,14 @@ class AndroidAccountManager extends AccountManagerImpl
LOG.warning("Could not list files in app data dir"); LOG.warning("Could not list files in app data dir");
} else { } else {
for (File file : fileArray) { for (File file : fileArray) {
if (!PROTECTED_DIR_NAMES.contains(file.getName())) { String name = file.getName();
if (!name.equals("lib") && !name.equals("shared_prefs")) {
files.add(file); files.add(file);
} }
} }
} }
files.add(appContext.getFilesDir()); files.add(appContext.getFilesDir());
files.add(appContext.getCacheDir());
addIfNotNull(files, appContext.getExternalCacheDir()); addIfNotNull(files, appContext.getExternalCacheDir());
if (SDK_INT >= 19) { if (SDK_INT >= 19) {
for (File file : appContext.getExternalCacheDirs()) { for (File file : appContext.getExternalCacheDirs()) {
@@ -115,16 +140,12 @@ class AndroidAccountManager extends AccountManagerImpl
addIfNotNull(files, file); addIfNotNull(files, file);
} }
} }
// Clear the cache directory but don't delete it
File cacheDir = appContext.getCacheDir();
File[] children = cacheDir.listFiles();
if (children != null) files.addAll(asList(children));
for (File file : files) { for (File file : files) {
if (LOG.isLoggable(INFO)) {
LOG.info("Deleting " + file.getAbsolutePath());
}
deleteFileOrDir(file); deleteFileOrDir(file);
} }
// Recreate the cache dir as some OpenGL drivers expect it to exist
if (!new File(dataDir, "cache").mkdirs())
LOG.warning("Could not recreate cache dir");
} }
private File getDataDir() { private File getDataDir() {

View File

@@ -32,7 +32,6 @@ 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;
import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.ConnectivityManager.TYPE_WIFI;
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.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.MINUTES;
@@ -77,9 +76,9 @@ class AndroidNetworkManager implements NetworkManager, Service {
filter.addAction(ACTION_SCREEN_ON); filter.addAction(ACTION_SCREEN_ON);
filter.addAction(ACTION_SCREEN_OFF); filter.addAction(ACTION_SCREEN_OFF);
filter.addAction(WIFI_AP_STATE_CHANGED_ACTION); filter.addAction(WIFI_AP_STATE_CHANGED_ACTION);
filter.addAction(WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
if (SDK_INT >= 23) filter.addAction(ACTION_DEVICE_IDLE_MODE_CHANGED); if (SDK_INT >= 23) filter.addAction(ACTION_DEVICE_IDLE_MODE_CHANGED);
appContext.registerReceiver(networkStateReceiver, filter); appContext.registerReceiver(networkStateReceiver, filter);
} }
@Override @Override
@@ -137,8 +136,7 @@ class AndroidNetworkManager implements NetworkManager, Service {
} }
private boolean isApEvent(@Nullable String action) { private boolean isApEvent(@Nullable String action) {
return WIFI_AP_STATE_CHANGED_ACTION.equals(action) || return WIFI_AP_STATE_CHANGED_ACTION.equals(action);
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action);
} }
} }
} }

View File

@@ -9,7 +9,6 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.Backoff;
@@ -25,6 +24,7 @@ import java.io.IOException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
@@ -47,10 +47,7 @@ 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.DEVICE_TYPE_LE;
import static android.bluetooth.BluetoothDevice.EXTRA_DEVICE; import static android.bluetooth.BluetoothDevice.EXTRA_DEVICE;
import static android.os.Build.VERSION.SDK_INT;
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;
@@ -77,12 +74,11 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
private volatile BluetoothAdapter adapter = null; private volatile BluetoothAdapter adapter = null;
AndroidBluetoothPlugin(BluetoothConnectionLimiter connectionLimiter, AndroidBluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
TimeoutMonitor timeoutMonitor, Executor ioExecutor, Executor ioExecutor, AndroidExecutor androidExecutor,
SecureRandom secureRandom, AndroidExecutor androidExecutor, Context appContext, SecureRandom secureRandom, Clock clock,
Context appContext, Clock clock, Backoff backoff, Backoff backoff, PluginCallback callback, int maxLatency) {
PluginCallback callback, int maxLatency, int maxIdleTime) { super(connectionLimiter, ioExecutor, secureRandom, backoff, callback,
super(connectionLimiter, timeoutMonitor, ioExecutor, secureRandom, maxLatency);
backoff, callback, maxLatency, maxIdleTime);
this.androidExecutor = androidExecutor; this.androidExecutor = androidExecutor;
this.appContext = appContext; this.appContext = appContext;
this.clock = clock; this.clock = clock;
@@ -174,10 +170,9 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
return wrapSocket(ss.accept()); return wrapSocket(ss.accept());
} }
private DuplexTransportConnection wrapSocket(BluetoothSocket s) private DuplexTransportConnection wrapSocket(BluetoothSocket s) {
throws IOException { return new AndroidBluetoothTransportConnection(this,
return new AndroidBluetoothTransportConnection(this, connectionLimiter, connectionLimiter, s);
timeoutMonitor, s);
} }
@Override @Override
@@ -245,15 +240,11 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
break; break;
} else if (ACTION_FOUND.equals(action)) { } else if (ACTION_FOUND.equals(action)) {
BluetoothDevice d = i.getParcelableExtra(EXTRA_DEVICE); BluetoothDevice d = i.getParcelableExtra(EXTRA_DEVICE);
// Ignore Bluetooth LE devices String address = d.getAddress();
if (SDK_INT < 18 || d.getType() != DEVICE_TYPE_LE) { if (LOG.isLoggable(INFO))
String address = d.getAddress(); LOG.info("Discovered " + scrubMacAddress(address));
if (LOG.isLoggable(INFO)) if (!addresses.contains(address))
LOG.info("Discovered " + addresses.add(address);
scrubMacAddress(address));
if (!addresses.contains(address))
addresses.add(address);
}
} }
now = clock.currentTimeMillis(); now = clock.currentTimeMillis();
} }
@@ -269,7 +260,7 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
appContext.unregisterReceiver(receiver); appContext.unregisterReceiver(receiver);
} }
// Shuffle the addresses so we don't always try the same one first // Shuffle the addresses so we don't always try the same one first
shuffle(addresses); Collections.shuffle(addresses);
return addresses; return addresses;
} }

View File

@@ -3,7 +3,6 @@ package org.briarproject.bramble.plugin.bluetooth;
import android.content.Context; import android.content.Context;
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.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;
@@ -26,7 +25,6 @@ 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;
@@ -37,20 +35,18 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
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;
public AndroidBluetoothPluginFactory(Executor ioExecutor, public AndroidBluetoothPluginFactory(Executor ioExecutor,
AndroidExecutor androidExecutor, Context appContext, AndroidExecutor androidExecutor, Context appContext,
SecureRandom secureRandom, EventBus eventBus, Clock clock, SecureRandom secureRandom, EventBus eventBus, Clock clock,
TimeoutMonitor timeoutMonitor, BackoffFactory backoffFactory) { BackoffFactory backoffFactory) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.androidExecutor = androidExecutor; this.androidExecutor = androidExecutor;
this.appContext = appContext; this.appContext = appContext;
this.secureRandom = secureRandom; this.secureRandom = secureRandom;
this.eventBus = eventBus; this.eventBus = eventBus;
this.clock = clock; this.clock = clock;
this.timeoutMonitor = timeoutMonitor;
this.backoffFactory = backoffFactory; this.backoffFactory = backoffFactory;
} }
@@ -67,13 +63,12 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
@Override @Override
public DuplexPlugin createPlugin(PluginCallback callback) { public DuplexPlugin createPlugin(PluginCallback callback) {
BluetoothConnectionLimiter connectionLimiter = BluetoothConnectionLimiter connectionLimiter =
new BluetoothConnectionLimiterImpl(eventBus, clock); new BluetoothConnectionLimiterImpl();
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);
AndroidBluetoothPlugin plugin = new AndroidBluetoothPlugin( AndroidBluetoothPlugin plugin = new AndroidBluetoothPlugin(
connectionLimiter, timeoutMonitor, ioExecutor, secureRandom, connectionLimiter, ioExecutor, androidExecutor, appContext,
androidExecutor, appContext, clock, backoff, secureRandom, clock, backoff, callback, MAX_LATENCY);
callback, MAX_LATENCY, MAX_IDLE_TIME);
eventBus.addListener(plugin); eventBus.addListener(plugin);
return plugin; return plugin;
} }

View File

@@ -2,7 +2,6 @@ package org.briarproject.bramble.plugin.bluetooth;
import android.bluetooth.BluetoothSocket; import android.bluetooth.BluetoothSocket;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Plugin; import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.duplex.AbstractDuplexTransportConnection; import org.briarproject.bramble.api.plugin.duplex.AbstractDuplexTransportConnection;
@@ -11,33 +10,24 @@ 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;
AndroidBluetoothTransportConnection(Plugin plugin, AndroidBluetoothTransportConnection(Plugin plugin,
BluetoothConnectionLimiter connectionLimiter, BluetoothConnectionLimiter connectionManager,
TimeoutMonitor timeoutMonitor, BluetoothSocket socket) BluetoothSocket socket) {
throws IOException {
super(plugin); super(plugin);
this.connectionLimiter = connectionLimiter; this.connectionManager = connectionManager;
this.socket = socket; this.socket = socket;
in = timeoutMonitor.createTimeoutInputStream(
socket.getInputStream(), plugin.getMaxIdleTime() * 2);
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
@@ -50,7 +40,7 @@ class AndroidBluetoothTransportConnection
try { try {
socket.close(); socket.close();
} finally { } finally {
connectionLimiter.connectionClosed(this, exception); connectionManager.connectionClosed(this);
} }
} }
} }

View File

@@ -19,7 +19,7 @@ import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.Socket; import java.net.Socket;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.List; import java.util.Collection;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -40,6 +40,19 @@ class AndroidLanTcpPlugin extends LanTcpPlugin implements EventListener {
private static final Logger LOG = private static final Logger LOG =
getLogger(AndroidLanTcpPlugin.class.getName()); getLogger(AndroidLanTcpPlugin.class.getName());
private static final byte[] WIFI_AP_ADDRESS_BYTES =
{(byte) 192, (byte) 168, 43, 1};
private static final InetAddress WIFI_AP_ADDRESS;
static {
try {
WIFI_AP_ADDRESS = InetAddress.getByAddress(WIFI_AP_ADDRESS_BYTES);
} catch (UnknownHostException e) {
// Should only be thrown if the address has an illegal length
throw new AssertionError(e);
}
}
private final Executor connectionStatusExecutor; private final Executor connectionStatusExecutor;
private final ConnectivityManager connectivityManager; private final ConnectivityManager connectivityManager;
@Nullable @Nullable
@@ -49,9 +62,8 @@ class AndroidLanTcpPlugin extends LanTcpPlugin implements EventListener {
AndroidLanTcpPlugin(Executor ioExecutor, Context appContext, AndroidLanTcpPlugin(Executor ioExecutor, Context appContext,
Backoff backoff, PluginCallback callback, int maxLatency, Backoff backoff, PluginCallback callback, int maxLatency,
int maxIdleTime, int connectionTimeout) { int maxIdleTime) {
super(ioExecutor, backoff, callback, maxLatency, maxIdleTime, super(ioExecutor, 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);
@@ -67,7 +79,6 @@ class AndroidLanTcpPlugin extends LanTcpPlugin implements EventListener {
@Override @Override
public void start() { public void start() {
if (used.getAndSet(true)) throw new IllegalStateException(); if (used.getAndSet(true)) throw new IllegalStateException();
initialisePortProperty();
running = true; running = true;
updateConnectionStatus(); updateConnectionStatus();
} }
@@ -84,19 +95,16 @@ class AndroidLanTcpPlugin extends LanTcpPlugin implements EventListener {
} }
@Override @Override
protected List<InetAddress> getUsableLocalInetAddresses() { protected Collection<InetAddress> getLocalIpAddresses() {
// If the device doesn't have wifi, don't open any sockets // If the device doesn't have wifi, don't open any sockets
if (wifiManager == null) return emptyList(); if (wifiManager == null) return emptyList();
// If we're connected to a wifi network, return its address // If we're connected to a wifi network, use that network
WifiInfo info = wifiManager.getConnectionInfo(); WifiInfo info = wifiManager.getConnectionInfo();
if (info != null && info.getIpAddress() != 0) { if (info != null && info.getIpAddress() != 0)
return singletonList(intToInetAddress(info.getIpAddress())); return singletonList(intToInetAddress(info.getIpAddress()));
}
// If we're running an access point, return its address // If we're running an access point, return its address
for (InetAddress addr : getLocalInetAddresses()) { if (super.getLocalIpAddresses().contains(WIFI_AP_ADDRESS))
if (addr.equals(WIFI_AP_ADDRESS)) return singletonList(addr); return singletonList(WIFI_AP_ADDRESS);
if (addr.equals(WIFI_DIRECT_AP_ADDRESS)) return singletonList(addr);
}
// No suitable addresses // No suitable addresses
return emptyList(); return emptyList();
} }
@@ -136,9 +144,8 @@ class AndroidLanTcpPlugin extends LanTcpPlugin implements EventListener {
private void updateConnectionStatus() { private void updateConnectionStatus() {
connectionStatusExecutor.execute(() -> { connectionStatusExecutor.execute(() -> {
if (!running) return; if (!running) return;
List<InetAddress> addrs = getUsableLocalInetAddresses(); Collection<InetAddress> addrs = getLocalIpAddresses();
if (addrs.contains(WIFI_AP_ADDRESS) if (addrs.contains(WIFI_AP_ADDRESS)) {
|| addrs.contains(WIFI_DIRECT_AP_ADDRESS)) {
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

View File

@@ -21,11 +21,10 @@ import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID;
@NotNullByDefault @NotNullByDefault
public class AndroidLanTcpPluginFactory implements DuplexPluginFactory { public class AndroidLanTcpPluginFactory implements DuplexPluginFactory {
private static final int MAX_LATENCY = 30_000; // 30 seconds private static final int MAX_LATENCY = 30 * 1000; // 30 seconds
private static final int MAX_IDLE_TIME = 30_000; // 30 seconds private static final int MAX_IDLE_TIME = 30 * 1000; // 30 seconds
private static final int CONNECTION_TIMEOUT = 3_000; // 3 seconds private static final int MIN_POLLING_INTERVAL = 60 * 1000; // 1 minute
private static final int MIN_POLLING_INTERVAL = 60_000; // 1 minute private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 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; private final Executor ioExecutor;
@@ -56,8 +55,7 @@ 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,
appContext, backoff, callback, MAX_LATENCY, MAX_IDLE_TIME, appContext, backoff, callback, MAX_LATENCY, MAX_IDLE_TIME);
CONNECTION_TIMEOUT);
eventBus.addListener(plugin); eventBus.addListener(plugin);
return plugin; return plugin;
} }

View File

@@ -1,6 +1,7 @@
package org.briarproject.bramble.plugin.tor; package org.briarproject.bramble.plugin.tor;
import android.content.Context; import android.content.Context;
import android.os.Build;
import org.briarproject.bramble.api.battery.BatteryManager; import org.briarproject.bramble.api.battery.BatteryManager;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
@@ -88,15 +89,9 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
// Check that we have a Tor binary for this architecture // Check that we have a Tor binary for this architecture
String architecture = null; String architecture = null;
for (String abi : AndroidUtils.getSupportedArchitectures()) { for (String abi : AndroidUtils.getSupportedArchitectures()) {
if (abi.startsWith("x86_64")) { if (abi.startsWith("x86")) {
architecture = "x86_64";
break;
} else if (abi.startsWith("x86")) {
architecture = "x86"; architecture = "x86";
break; break;
} else if (abi.startsWith("arm64")) {
architecture = "arm64";
break;
} else if (abi.startsWith("armeabi")) { } else if (abi.startsWith("armeabi")) {
architecture = "arm"; architecture = "arm";
break; break;
@@ -106,8 +101,8 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
LOG.info("Tor is not supported on this architecture"); LOG.info("Tor is not supported on this architecture");
return null; return null;
} }
// Use position-independent executable // Use position-independent executable for SDK >= 16
architecture += "_pie"; if (Build.VERSION.SDK_INT >= 16) architecture += "_pie";
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);

View File

@@ -23,7 +23,6 @@ import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static android.content.Context.WIFI_SERVICE; import static android.content.Context.WIFI_SERVICE;
import static android.os.Build.VERSION.SDK_INT;
import static android.provider.Settings.Secure.ANDROID_ID; import static android.provider.Settings.Secure.ANDROID_ID;
@Immutable @Immutable
@@ -75,7 +74,8 @@ 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(); if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT <= 18)
applyOpenSslFix();
StrictMode.setThreadPolicy(tp); StrictMode.setThreadPolicy(tp);
} }

View File

@@ -6,25 +6,15 @@ import android.content.Context;
import android.os.Build; import android.os.Build;
import android.provider.Settings; import android.provider.Settings;
import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.File; import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import javax.annotation.Nullable;
import static android.content.Context.MODE_PRIVATE; import static android.content.Context.MODE_PRIVATE;
import static android.os.Build.VERSION.SDK_INT; import static android.os.Build.VERSION.SDK_INT;
import static java.util.Arrays.asList;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
@NotNullByDefault
public class AndroidUtils { public class AndroidUtils {
// Fake Bluetooth address returned by BluetoothAdapter on API 23 and later // Fake Bluetooth address returned by BluetoothAdapter on API 23 and later
@@ -32,10 +22,11 @@ public class AndroidUtils {
private static final String STORED_REPORTS = "dev-reports"; private static final String STORED_REPORTS = "dev-reports";
@SuppressWarnings("deprecation")
public static Collection<String> getSupportedArchitectures() { public static Collection<String> getSupportedArchitectures() {
List<String> abis = new ArrayList<>(); List<String> abis = new ArrayList<>();
if (SDK_INT >= 21) { if (SDK_INT >= 21) {
abis.addAll(asList(Build.SUPPORTED_ABIS)); abis.addAll(Arrays.asList(Build.SUPPORTED_ABIS));
} else { } else {
abis.add(Build.CPU_ABI); abis.add(Build.CPU_ABI);
if (Build.CPU_ABI2 != null) abis.add(Build.CPU_ABI2); if (Build.CPU_ABI2 != null) abis.add(Build.CPU_ABI2);
@@ -45,76 +36,25 @@ public class AndroidUtils {
public static String getBluetoothAddress(Context ctx, public static String getBluetoothAddress(Context ctx,
BluetoothAdapter adapter) { BluetoothAdapter adapter) {
return getBluetoothAddressAndMethod(ctx, adapter).getFirst();
}
public static Pair<String, String> getBluetoothAddressAndMethod(Context ctx,
BluetoothAdapter adapter) {
// Return the adapter's address if it's valid and not fake // Return the adapter's address if it's valid and not fake
@SuppressLint("HardwareIds") @SuppressLint("HardwareIds")
String address = adapter.getAddress(); String address = adapter.getAddress();
if (isValidBluetoothAddress(address)) { if (isValidBluetoothAddress(address)) return address;
return new Pair<>(address, "adapter");
}
// Return the address from settings if it's valid and not fake // Return the address from settings if it's valid and not fake
address = Settings.Secure.getString(ctx.getContentResolver(), address = Settings.Secure.getString(ctx.getContentResolver(),
"bluetooth_address"); "bluetooth_address");
if (isValidBluetoothAddress(address)) { if (isValidBluetoothAddress(address)) return address;
return new Pair<>(address, "settings");
}
// Try to get the address via reflection
address = getBluetoothAddressByReflection(adapter);
if (isValidBluetoothAddress(address)) {
return new Pair<>(requireNonNull(address), "reflection");
}
// Let the caller know we can't find the address // Let the caller know we can't find the address
return new Pair<>("", ""); return "";
} }
public static boolean isValidBluetoothAddress(@Nullable String address) { private static boolean isValidBluetoothAddress(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);
} }
@Nullable
private static String getBluetoothAddressByReflection(
BluetoothAdapter adapter) {
try {
Field mServiceField =
adapter.getClass().getDeclaredField("mService");
mServiceField.setAccessible(true);
Object mService = mServiceField.get(adapter);
// mService may be null when Bluetooth is disabled
if (mService == null) throw new NoSuchFieldException();
Method getAddressMethod =
mService.getClass().getMethod("getAddress");
return (String) getAddressMethod.invoke(mService);
} catch (NoSuchFieldException e) {
return null;
} catch (IllegalAccessException e) {
return null;
} catch (NoSuchMethodException e) {
return null;
} catch (InvocationTargetException e) {
return null;
} catch (SecurityException e) {
return null;
}
}
public static File getReportDir(Context ctx) { public static File getReportDir(Context ctx) {
return ctx.getDir(STORED_REPORTS, MODE_PRIVATE); return ctx.getDir(STORED_REPORTS, MODE_PRIVATE);
} }
/**
* 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() {
if (SDK_INT < 24) return new String[] {"image/jpeg", "image/png"};
else return new String[] {"image/jpeg", "image/png", "image/gif"};
}
} }

View File

@@ -16,10 +16,13 @@ import org.junit.Test;
import java.io.File; import java.io.File;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.assertTrue;
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory; import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getTestDirectory; import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
import static org.briarproject.bramble.util.StringUtils.toHexString;
public class AndroidAccountManagerTest extends BrambleMockTestCase { public class AndroidAccountManagerTest extends BrambleMockTestCase {
@@ -37,8 +40,11 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
private final Application app; private final Application app;
private final ApplicationInfo applicationInfo; private final ApplicationInfo applicationInfo;
private final String encryptedKeyHex = toHexString(getRandomBytes(123));
private final File testDir = getTestDirectory(); private final File testDir = getTestDirectory();
private final File keyDir = new File(testDir, "key"); private final File keyDir = new File(testDir, "key");
private final File keyFile = new File(keyDir, "db.key");
private final File keyBackupFile = new File(keyDir, "db.key.bak");
private final File dbDir = new File(testDir, "db"); private final File dbDir = new File(testDir, "db");
private AndroidAccountManager accountManager; private AndroidAccountManager accountManager;
@@ -69,12 +75,33 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
}; };
} }
@Test
public void testDbKeyIsMigratedFromPreferencesToFile() {
context.checking(new Expectations() {{
oneOf(prefs).getString("key", null);
will(returnValue(encryptedKeyHex));
oneOf(prefs).edit();
will(returnValue(editor));
oneOf(editor).remove("key");
will(returnValue(editor));
oneOf(editor).commit();
will(returnValue(true));
}});
assertFalse(keyFile.exists());
assertFalse(keyBackupFile.exists());
assertEquals(encryptedKeyHex,
accountManager.loadEncryptedDatabaseKey());
assertTrue(keyFile.exists());
assertTrue(keyBackupFile.exists());
}
@Test @Test
public void testDeleteAccountClearsSharedPrefsAndDeletesFiles() public void testDeleteAccountClearsSharedPrefsAndDeletesFiles()
throws Exception { throws Exception {
// Directories 'code_cache', 'lib' and 'shared_prefs' should be spared // Directories 'lib' and 'shared_prefs' should be spared
File codeCacheDir = new File(testDir, "code_cache");
File codeCacheFile = new File(codeCacheDir, "file");
File libDir = new File(testDir, "lib"); File libDir = new File(testDir, "lib");
File libFile = new File(libDir, "file"); File libFile = new File(libDir, "file");
File sharedPrefsDir = new File(testDir, "shared_prefs"); File sharedPrefsDir = new File(testDir, "shared_prefs");
@@ -113,8 +140,6 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
assertTrue(dbDir.mkdirs()); assertTrue(dbDir.mkdirs());
assertTrue(keyDir.mkdirs()); assertTrue(keyDir.mkdirs());
assertTrue(codeCacheDir.mkdirs());
assertTrue(codeCacheFile.createNewFile());
assertTrue(libDir.mkdirs()); assertTrue(libDir.mkdirs());
assertTrue(libFile.createNewFile()); assertTrue(libFile.createNewFile());
assertTrue(sharedPrefsDir.mkdirs()); assertTrue(sharedPrefsDir.mkdirs());
@@ -130,8 +155,6 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
assertFalse(dbDir.exists()); assertFalse(dbDir.exists());
assertFalse(keyDir.exists()); assertFalse(keyDir.exists());
assertTrue(codeCacheDir.exists());
assertTrue(codeCacheFile.exists());
assertTrue(libDir.exists()); assertTrue(libDir.exists());
assertTrue(libFile.exists()); assertTrue(libFile.exists());
assertTrue(sharedPrefsDir.exists()); assertTrue(sharedPrefsDir.exists());

View File

@@ -1,46 +1,44 @@
dependencyVerification { dependencyVerification {
verify = [ verify = [
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861', 'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
'com.android.tools.analytics-library:protos:26.5.1:protos-26.5.1.jar:8dde1130725461fe827f2a343d353f2b51e8870661fc860d7d5ebddb097ead4e', 'com.android.tools.analytics-library:protos:26.4.0:protos-26.4.0.jar:ad760915586797d39319f402837b378bff3bb4ed583e3e0c48c965631fb2135f',
'com.android.tools.analytics-library:shared:26.5.1:shared-26.5.1.jar:ccc2f3b00ec17b11401610ba68553544fc8fc517120e84439ac6eb86b875e18d', 'com.android.tools.analytics-library:shared:26.4.0:shared-26.4.0.jar:1332106a905d48909c81268c9e414946de3e83487db394c6073b0a9b5c3d0ed2',
'com.android.tools.analytics-library:tracker:26.5.1:tracker-26.5.1.jar:3a76984c0fe2e847ca7a8b35b4780ef0447a9d1666946cb8e60466318e0ab5ae', 'com.android.tools.analytics-library:tracker:26.4.0:tracker-26.4.0.jar:d0020cfbfd4cd75935f2972d6a24089840d4a10df6f3ef2a796093217dd37796',
'com.android.tools.build:aapt2-proto:0.4.0:aapt2-proto-0.4.0.jar:fac0435e08898f89eeeb9ca236bea707155ff816c12205ced285ad53604133ca', 'com.android.tools.build:apksig:3.4.0:apksig-3.4.0.jar:91d5a1866139c69756280355a6f61b4d619d0516841580114f45a10f2177327e',
'com.android.tools.build:apksig:3.5.1:apksig-3.5.1.jar:1fd33e7f009a2a0da766cfeec4211a09f548034b015c289a66d75dd8a9302f4a', 'com.android.tools.build:apkzlib:3.4.0:apkzlib-3.4.0.jar:8653c85f5fdf1dde840e8b8af7396aeb79c34b66e541b5860059616006535592',
'com.android.tools.build:apkzlib:3.5.1:apkzlib-3.5.1.jar:9f330167cbe973b7db407692f74f4f6453b7ffa5f2048934b06280c2ceee60fa', 'com.android.tools.build:builder-model:3.4.0:builder-model-3.4.0.jar:a88f138124a9f016a70bcb4760359a502f65c7deed56507ee4014f4dd9ea853b',
'com.android.tools.build:builder-model:3.5.1:builder-model-3.5.1.jar:39ea3c82b76b6e0c9f9fa88d93e0edc1dd4a0f1dfae0ef6fbf2d451da47e5450', 'com.android.tools.build:builder-test-api:3.4.0:builder-test-api-3.4.0.jar:31089ab1ec19ca7687a010867d2f3807513c805b8226979706f4247b5d4df26f',
'com.android.tools.build:builder-test-api:3.5.1:builder-test-api-3.5.1.jar:a1b59305584cbcaa078fdc9cfb80871012755b822dd32e8da19add6f7bbcb762', 'com.android.tools.build:builder:3.4.0:builder-3.4.0.jar:476221b5203a7f50089bf185ed95000a34b6f5020ef0a17815afd58606922679',
'com.android.tools.build:builder:3.5.1:builder-3.5.1.jar:e3a8d382434c5f60990730c4719fc814e85a898a33a1e96c1df8d627d3c6eea6', 'com.android.tools.build:gradle-api:3.4.0:gradle-api-3.4.0.jar:215eca38f6719213c2f492b4d622cdd11676c66c9871f8a2aed0c66d00175628',
'com.android.tools.build:gradle-api:3.5.1:gradle-api-3.5.1.jar:be9b41859bace11998f66b04ed944f87e413f3ad6da3c4665587699da125addc', 'com.android.tools.build:manifest-merger:26.4.0:manifest-merger-26.4.0.jar:29e45e690dedd165035e97c21c2ca94d0bd4ec16b6b210daa26669a582b6f220',
'com.android.tools.build:manifest-merger:26.5.1:manifest-merger-26.5.1.jar:dcad9ecb967251f4d750f55a4204a2b400e8fbfe5cb930a1d0d5dbe10ae8bdfc', 'com.android.tools.ddms:ddmlib:26.4.0:ddmlib-26.4.0.jar:93f56fe4630c3166adbd6c51d7bb602d96abb91b07ba5b1165fdcd071e88c940',
'com.android.tools.ddms:ddmlib:26.5.1:ddmlib-26.5.1.jar:b081aef2a4ed3f4d47cae4cdb128469735f25a114e026d37123bf9ffdec742a8', 'com.android.tools.external.com-intellij:intellij-core:26.4.0:intellij-core-26.4.0.jar:30cb0e879d4424de9677a50b537fb628636b4a50f5470af5e52437980c41421f',
'com.android.tools.external.com-intellij:intellij-core:26.5.1:intellij-core-26.5.1.jar:20eced30adc124805bd93488d9cd9d3e33e6bf7b48e9fe5a703d4983f894d450', 'com.android.tools.external.com-intellij:kotlin-compiler:26.4.0:kotlin-compiler-26.4.0.jar:dd1fe225c31a0e012dc025336363a5b783e2c5c20ffb69e77f8f57e89420d998',
'com.android.tools.external.com-intellij:kotlin-compiler:26.5.1:kotlin-compiler-26.5.1.jar:5aed762dd54875b77ae7018d97c05756ff0c5b9fd02ec595dd396ccd14cc22cb', 'com.android.tools.external.org-jetbrains:uast:26.4.0:uast-26.4.0.jar:f25f3285b775a983327583ff6584dea54e447813ef69e0ce08b05a45b5f4aab0',
'com.android.tools.external.org-jetbrains:uast:26.5.1:uast-26.5.1.jar:4bc8653d6c0943f40fee963a149e36c6baa45683d2530968a13f5007e3c40740', 'com.android.tools.layoutlib:layoutlib-api:26.4.0:layoutlib-api-26.4.0.jar:52128f5cf293b224072be361919bfd416e59480ab7264ddcdbbf046b0d7a12e3',
'com.android.tools.layoutlib:layoutlib-api:26.5.1:layoutlib-api-26.5.1.jar:88732f11396c427273e515d23042e35633f4fe4295528a99b866aa2adf0efd9c', 'com.android.tools.lint:lint-api:26.4.0:lint-api-26.4.0.jar:fdb8fca8ae4c254f438338d03d72605e00ed106f2d5550405af41ca1c8509401',
'com.android.tools.lint:lint-api:26.5.1:lint-api-26.5.1.jar:ec33fcd72bfaf70dd841e03fbfd93f109c2e575aec146067c606689c3972f0de', 'com.android.tools.lint:lint-checks:26.4.0:lint-checks-26.4.0.jar:4ff52d40488cd3e22b9c6b2eb67784e0c3269d0b42ef9d17689cd75a7b2bceb4',
'com.android.tools.lint:lint-checks:26.5.1:lint-checks-26.5.1.jar:a1b9607d484aaae7a71dcecdc76f8003d8239af226c776894a2cf63f9e6c60d7', 'com.android.tools.lint:lint-gradle-api:26.4.0:lint-gradle-api-26.4.0.jar:714b7a85c7d2aa10daeab16e969fe7530c659d0728a7f24021da456870418d0f',
'com.android.tools.lint:lint-gradle-api:26.5.1:lint-gradle-api-26.5.1.jar:82453fd98a8394cc84ed995c04d2cd744abd1d6589403427ba7eef53115406f3', 'com.android.tools.lint:lint-gradle:26.4.0:lint-gradle-26.4.0.jar:b8c130d273f522388734457e1b96790f41528fcec6fda9e8eaa4e4d95a07cfbb',
'com.android.tools.lint:lint-gradle:26.5.1:lint-gradle-26.5.1.jar:59465b56cf7db77c656d5f8195d721c3d48b6bdd0502d774de335bfe4baff00b', 'com.android.tools.lint:lint:26.4.0:lint-26.4.0.jar:83aa062fb0405b60ed358d858c8c2955e1bae44a455b498068c6a60988755f00',
'com.android.tools.lint:lint:26.5.1:lint-26.5.1.jar:336e4b04ec6f8b0f25879131b7a7862d77df83a1879ee5b71be26128755f8e2e', 'com.android.tools:annotations:26.4.0:annotations-26.4.0.jar:a7955b8e19c3a2a861d6faa43a58b7c0d46ea9112188ee3e235c6f9f439ecc1a',
'com.android.tools:annotations:26.5.1:annotations-26.5.1.jar:2c43c82f8c59d8f7a61e3239e1a2dc9f69dc342ec09af9b7c9f69b25337c0b6e', 'com.android.tools:common:26.4.0:common-26.4.0.jar:ea40b94b3c1284ea7700f011388e2906a8363a66abd902891722b3c557984852',
'com.android.tools:common:26.5.1:common-26.5.1.jar:eccfa54486ed54c4e3123cc42195d023bd0dd21bcd2f0e4868e8c6fc70f8ef6b', 'com.android.tools:dvlib:26.4.0:dvlib-26.4.0.jar:23af89c535b01ba36ceed1b6b309b672814eba624e643cd7dedf0519edad50cc',
'com.android.tools:dvlib:26.5.1:dvlib-26.5.1.jar:46f93ad498b4756e7d867d2fe38c38890a80e7407a4ae459e4a8c8d5c5aeacfe', 'com.android.tools:repository:26.4.0:repository-26.4.0.jar:3d1763ab46199374dc6d94129bba11c70f1d5857e2c81a3ac4898abca40b176b',
'com.android.tools:repository:26.5.1:repository-26.5.1.jar:2b3ee791aa4c3e8ce60498c161a27ca7228816fc630eed4d9f25f2f36a106dce', 'com.android.tools:sdk-common:26.4.0:sdk-common-26.4.0.jar:78a522525b30ffc6b7bf1299c831d24ce385f68a9f4878f8f752e9baefa31b0f',
'com.android.tools:sdk-common:26.5.1:sdk-common-26.5.1.jar:365f749676c3574676fd465177c8a492f340816db2b520d6ed114d3b6e77bea7', 'com.android.tools:sdklib:26.4.0:sdklib-26.4.0.jar:b854c23892013a326d761cf071c72cf3e038ed0469d10f4a356829fa56e4c132',
'com.android.tools:sdklib:26.5.1:sdklib-26.5.1.jar:007da104afb27c8c682a1628023fe9ec438249c8d15ef0fd6624c5bb8e23b696', 'com.google.code.findbugs:jsr305:1.3.9:jsr305-1.3.9.jar:905721a0eea90a81534abb7ee6ef4ea2e5e645fa1def0a5cd88402df1b46c9ed',
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7', 'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
'com.google.code.gson:gson:2.8.5:gson-2.8.5.jar:233a0149fc365c9f6edbd683cfe266b19bdc773be98eabdaf6b3c924b48e7d81', 'com.google.code.gson:gson:2.8.0:gson-2.8.0.jar:c6221763bd79c4f1c3dc7f750b5f29a0bb38b367b81314c4f71896e340c40825',
'com.google.dagger:dagger-compiler:2.24:dagger-compiler-2.24.jar:3c5afb955fb188da485cb2c048eff37dce0e1530b9780a0f2f7187d16d1ccc1f', 'com.google.dagger:dagger-compiler:2.22.1:dagger-compiler-2.22.1.jar:e5f28302cbe70a79d3620cddebfb8ec0736814f3980ffe1e673bfe3342f507d3',
'com.google.dagger:dagger-producers:2.24:dagger-producers-2.24.jar:f10f45b95191954d5d6b043fca9e62fb621d21bf70634b8f8476c7988b504c3a', 'com.google.dagger:dagger-producers:2.22.1:dagger-producers-2.22.1.jar:f834a0082014213a68ff06a0f048d750178d02196c58b0b15beb367d32b97e35',
'com.google.dagger:dagger-spi:2.24:dagger-spi-2.24.jar:c038445d14dbcb4054e61bf49e05009edf26fce4fdc7ec1a9db544784f68e718', 'com.google.dagger:dagger-spi:2.22.1:dagger-spi-2.22.1.jar:4b0b922793b3bcb91b99fabb75dba77c68afd7ae4c5f0c4fd6ba681f0a291c7d',
'com.google.dagger:dagger:2.24:dagger-2.24.jar:550a6e46a6dfcdf1d764887b6090cea94f783327e50e5c73754f18facfc70b64', 'com.google.dagger:dagger:2.22.1:dagger-2.22.1.jar:329d4340f24c4f5717af016c097e90668bfea2a5376e6aa9964b01cef3fd241a',
'com.google.errorprone:error_prone_annotations:2.2.0:error_prone_annotations-2.2.0.jar:6ebd22ca1b9d8ec06d41de8d64e0596981d9607b42035f9ed374f9de271a481a', 'com.google.errorprone:error_prone_annotations:2.1.3:error_prone_annotations-2.1.3.jar:03d0329547c13da9e17c634d1049ea2ead093925e290567e1a364fd6b1fc7ff8',
'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30', 'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30',
'com.google.googlejavaformat:google-java-format:1.5:google-java-format-1.5.jar:aa19ad7850fb85178aa22f2fddb163b84d6ce4d0035872f30d4408195ca1144e', 'com.google.googlejavaformat:google-java-format:1.5:google-java-format-1.5.jar:aa19ad7850fb85178aa22f2fddb163b84d6ce4d0035872f30d4408195ca1144e',
'com.google.guava:failureaccess:1.0.1:failureaccess-1.0.1.jar:a171ee4c734dd2da837e4b16be9df4661afab72a41adaf31eb84dfdaf936ca26', 'com.google.guava:guava:25.0-jre:guava-25.0-jre.jar:3fd4341776428c7e0e5c18a7c10de129475b69ab9d30aeafbb5c277bb6074fa9',
'com.google.guava:guava:27.0.1-jre:guava-27.0.1-jre.jar:e1c814fd04492a27c38e0317eabeaa1b3e950ec8010239e400fe90ad6c9107b4', 'com.google.guava:guava:26.0-jre:guava-26.0-jre.jar:a0e9cabad665bc20bcd2b01f108e5fc03f756e13aea80abaadb9f407033bea2c',
'com.google.guava:guava:27.1-jre:guava-27.1-jre.jar:4a5aa70cc968a4d137e599ad37553e5cfeed2265e8c193476d7119036c536fe7',
'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava:listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar:b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99',
'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:2994a7eb78f2710bd3d3bfb639b2c94e219cedac0d4d084d516e78c16dddecf6', 'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:2994a7eb78f2710bd3d3bfb639b2c94e219cedac0d4d084d516e78c16dddecf6',
'com.google.jimfs:jimfs:1.1:jimfs-1.1.jar:c4828e28d7c0a930af9387510b3bada7daa5c04d7c25a75c7b8b081f1c257ddd', 'com.google.jimfs:jimfs:1.1:jimfs-1.1.jar:c4828e28d7c0a930af9387510b3bada7daa5c04d7c25a75c7b8b081f1c257ddd',
'com.google.protobuf:protobuf-java:3.4.0:protobuf-java-3.4.0.jar:dce7e66b32456a1b1198da0caff3a8acb71548658391e798c79369241e6490a4', 'com.google.protobuf:protobuf-java:3.4.0:protobuf-java-3.4.0.jar:dce7e66b32456a1b1198da0caff3a8acb71548658391e798c79369241e6490a4',
@@ -57,7 +55,6 @@ dependencyVerification {
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff', 'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'javax.xml.bind:jaxb-api:2.2.12-b140109.1041:jaxb-api-2.2.12-b140109.1041.jar:b5e60cd8b7b5ff01ce4a74c5dd008f4fbd14ced3495d0b47b85cfedc182211f2', 'javax.xml.bind:jaxb-api:2.2.12-b140109.1041:jaxb-api-2.2.12-b140109.1041.jar:b5e60cd8b7b5ff01ce4a74c5dd008f4fbd14ced3495d0b47b85cfedc182211f2',
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a', 'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
'net.sf.jopt-simple:jopt-simple:4.9:jopt-simple-4.9.jar:26c5856e954b5f864db76f13b86919b59c6eecf9fd930b96baa8884626baf2f5', 'net.sf.jopt-simple:jopt-simple:4.9:jopt-simple-4.9.jar:26c5856e954b5f864db76f13b86919b59c6eecf9fd930b96baa8884626baf2f5',
'net.sf.kxml:kxml2:2.3.0:kxml2-2.3.0.jar:f264dd9f79a1fde10ce5ecc53221eff24be4c9331c830b7d52f2f08a7b633de2', 'net.sf.kxml:kxml2:2.3.0:kxml2-2.3.0.jar:f264dd9f79a1fde10ce5ecc53221eff24be4c9331c830b7d52f2f08a7b633de2',
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d', 'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
@@ -69,22 +66,22 @@ dependencyVerification {
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8', 'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
'org.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca', 'org.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca',
'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349', 'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349',
'org.briarproject:obfs4proxy-android:0.0.11-2:obfs4proxy-android-0.0.11-2.zip:57e55cbe87aa2aac210fdbb6cd8cdeafe15f825406a08ebf77a8b787aa2c6a8a', 'org.briarproject:obfs4proxy-android:0.0.9:obfs4proxy-android-0.0.9.zip:9b7e9181535ea8d8bbe8ae6338e08cf4c5fc1e357a779393e0ce49586d459ae0',
'org.briarproject:tor-android:0.3.5.10:tor-android-0.3.5.10.zip:edd83bf557fcff2105eaa0bdb3f607a6852ebe7360920929ae3039dd5f4774c5', 'org.briarproject:tor-android:0.3.5.8:tor-android-0.3.5.8.zip:42a13a6f185be1a62f42e3f30ce66a3c099ac5ec890a65e7593111b65b44a54a',
'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d', 'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d',
'org.checkerframework:checker-qual:2.5.2:checker-qual-2.5.2.jar:64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a', 'org.checkerframework:checker-qual:2.5.2:checker-qual-2.5.2.jar:64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a',
'org.codehaus.groovy:groovy-all:2.4.15:groovy-all-2.4.15.jar:51d6c4e71782e85674239189499854359d380fb75e1a703756e3aaa5b98a5af0', 'org.codehaus.groovy:groovy-all:2.4.15:groovy-all-2.4.15.jar:51d6c4e71782e85674239189499854359d380fb75e1a703756e3aaa5b98a5af0',
'org.codehaus.mojo:animal-sniffer-annotations:1.17:animal-sniffer-annotations-1.17.jar:92654f493ecfec52082e76354f0ebf87648dc3d5cec2e3c3cdb947c016747a53', 'org.codehaus.mojo:animal-sniffer-annotations:1.14:animal-sniffer-annotations-1.14.jar:2068320bd6bad744c3673ab048f67e30bef8f518996fa380033556600669905d',
'org.glassfish.jaxb:jaxb-core:2.2.11:jaxb-core-2.2.11.jar:37bcaee8ebb04362c8352a5bf6221b86967ecdab5164c696b10b9a2bb587b2aa', 'org.glassfish.jaxb:jaxb-core:2.2.11:jaxb-core-2.2.11.jar:37bcaee8ebb04362c8352a5bf6221b86967ecdab5164c696b10b9a2bb587b2aa',
'org.glassfish.jaxb:jaxb-runtime:2.2.11:jaxb-runtime-2.2.11.jar:a874f2351cfba8e2946be3002d10c18a6da8f21b52ba2acf52f2b85d5520ed70', 'org.glassfish.jaxb:jaxb-runtime:2.2.11:jaxb-runtime-2.2.11.jar:a874f2351cfba8e2946be3002d10c18a6da8f21b52ba2acf52f2b85d5520ed70',
'org.glassfish.jaxb:txw2:2.2.11:txw2-2.2.11.jar:272a3ccad45a4511351920cd2a8633c53cab8d5220c7a92954da5526bb5eafea', 'org.glassfish.jaxb:txw2:2.2.11:txw2-2.2.11.jar:272a3ccad45a4511351920cd2a8633c53cab8d5220c7a92954da5526bb5eafea',
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9', 'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c', 'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
'org.jetbrains.kotlin:kotlin-reflect:1.3.50:kotlin-reflect-1.3.50.jar:64583199ea5a54aefd1bd1595288925f784226ee562d1dd279011c6075b3d7a4', 'org.jetbrains.kotlin:kotlin-reflect:1.3.21:kotlin-reflect-1.3.21.jar:a3065c822633191e0a3e3ee12a29bec234fc4b2864a6bb87ef48cce3e9e0c26a',
'org.jetbrains.kotlin:kotlin-stdlib-common:1.3.50:kotlin-stdlib-common-1.3.50.jar:8ce678e88e4ba018b66dacecf952471e4d7dfee156a8a819760a5a5ff29d323c', 'org.jetbrains.kotlin:kotlin-stdlib-common:1.3.21:kotlin-stdlib-common-1.3.21.jar:cea61f7b611895e64f58569a9757fc0ab0d582f107211e1930e0ce2a0add52a7',
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.50:kotlin-stdlib-jdk7-1.3.50.jar:9a026639e76212f8d57b86d55b075394c2e009f1979110751d34c05c5f75d57b', 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.21:kotlin-stdlib-jdk7-1.3.21.jar:a87875604fd42140da6938ae4d35ee61081f4482536efc6d2615b8b626a198af',
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.50:kotlin-stdlib-jdk8-1.3.50.jar:1b351fb6e09c14b55525c74c1f4cf48942eae43c348b7bc764a5e6e423d4da0c', 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.21:kotlin-stdlib-jdk8-1.3.21.jar:5823ed66ac122a1c55442ebca5a209a843ccd87f562edc31a787f3d2e47f74d4',
'org.jetbrains.kotlin:kotlin-stdlib:1.3.50:kotlin-stdlib-1.3.50.jar:e6f05746ee0366d0b52825a090fac474dcf44082c9083bbb205bd16976488d6c', 'org.jetbrains.kotlin:kotlin-stdlib:1.3.21:kotlin-stdlib-1.3.21.jar:38ba2370d9f06f50433e06b2ca775b94473c2e2785f410926079ab793c72b034',
'org.jetbrains.trove4j:trove4j:20160824:trove4j-20160824.jar:1917871c8deb468307a584680c87a44572f5a8b0b98c6d397fc0f5f86596dbe7', 'org.jetbrains.trove4j:trove4j:20160824:trove4j-20160824.jar:1917871c8deb468307a584680c87a44572f5a8b0b98c6d397fc0f5f86596dbe7',
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478', 'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c', 'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',

View File

@@ -7,7 +7,7 @@ apply plugin: 'witness'
apply from: 'witness.gradle' apply from: 'witness.gradle'
dependencies { dependencies {
implementation "com.google.dagger:dagger:2.24" implementation "com.google.dagger:dagger:2.22.1"
implementation 'com.google.code.findbugs:jsr305:3.0.2' implementation 'com.google.code.findbugs:jsr305:3.0.2'
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'

View File

@@ -6,4 +6,6 @@ package org.briarproject.bramble.api;
public interface FeatureFlags { public interface FeatureFlags {
boolean shouldEnableImageAttachments(); boolean shouldEnableImageAttachments();
boolean shouldEnableRemoteContacts();
} }

View File

@@ -1,6 +1,5 @@
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.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -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

@@ -4,10 +4,8 @@ import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.Pair; import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.UnsupportedVersionException; import org.briarproject.bramble.api.UnsupportedVersionException;
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.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.NoSuchContactException; import org.briarproject.bramble.api.db.NoSuchContactException;
import org.briarproject.bramble.api.db.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;
@@ -119,14 +117,9 @@ public interface ContactManager {
* @throws FormatException If the link is invalid * @throws FormatException If the link is invalid
* @throws GeneralSecurityException If the pending contact's handshake * @throws GeneralSecurityException If the pending contact's handshake
* public key is invalid * 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(String link, String alias) PendingContact addPendingContact(String link, String alias)
throws DbException, FormatException, GeneralSecurityException, throws DbException, FormatException, GeneralSecurityException;
ContactExistsException, PendingContactExistsException;
/** /**
* Returns the pending contact with the given ID. * Returns the pending contact with the given ID.

View File

@@ -132,33 +132,17 @@ public interface CryptoComponent {
* storage. The encryption and authentication keys are derived from the * storage. The encryption and authentication keys are derived from the
* given password. The ciphertext will be decryptable using the same * given password. The ciphertext will be decryptable using the same
* password after the app restarts. * password after the app restarts.
*
* @param keyStrengthener Used to strengthen the password-based key. If
* null, the password-based key will not be strengthened
*/ */
byte[] encryptWithPassword(byte[] plaintext, String password, byte[] encryptWithPassword(byte[] plaintext, String password);
@Nullable KeyStrengthener keyStrengthener);
/** /**
* Decrypts and authenticates the given ciphertext that has been read from * Decrypts and authenticates the given ciphertext that has been read from
* storage. The encryption and authentication keys are derived from the * storage. The encryption and authentication keys are derived from the
* given password. * given password. Returns null if the ciphertext cannot be decrypted and
*
* @param keyStrengthener Used to strengthen the password-based key. If
* null, or if strengthening was not used when encrypting the ciphertext,
* the password-based key will not be strengthened
* @throws DecryptionException If the ciphertext cannot be decrypted and
* authenticated (for example, if the password is wrong). * authenticated (for example, if the password is wrong).
*/ */
byte[] decryptWithPassword(byte[] ciphertext, String password, @Nullable
@Nullable KeyStrengthener keyStrengthener) byte[] decryptWithPassword(byte[] ciphertext, String password);
throws DecryptionException;
/**
* Returns true if the given ciphertext was encrypted using a strengthened
* key. The validity of the ciphertext is not checked.
*/
boolean isEncryptedWithStrengthenedKey(byte[] ciphertext);
/** /**
* Encrypts the given plaintext to the given public key. * Encrypts the given plaintext to the given public key.

View File

@@ -1,17 +0,0 @@
package org.briarproject.bramble.api.crypto;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
public class DecryptionException extends Exception {
private final DecryptionResult result;
public DecryptionException(DecryptionResult result) {
this.result = result;
}
public DecryptionResult getDecryptionResult() {
return result;
}
}

View File

@@ -1,29 +0,0 @@
package org.briarproject.bramble.api.crypto;
/**
* The result of a password-based decryption operation.
*/
public enum DecryptionResult {
/**
* Decryption succeeded.
*/
SUCCESS,
/**
* Decryption failed because the format of the ciphertext was invalid.
*/
INVALID_CIPHERTEXT,
/**
* Decryption failed because the {@link KeyStrengthener} used for
* encryption was not available for decryption.
*/
KEY_STRENGTHENER_ERROR,
/**
* Decryption failed because the password used for decryption did not match
* the password used for encryption.
*/
INVALID_PASSWORD
}

View File

@@ -1,23 +0,0 @@
package org.briarproject.bramble.api.crypto;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
/**
* Interface for strengthening a password-based key, for example by using a
* key stored in a key management service or hardware security module.
*/
@NotNullByDefault
public interface KeyStrengthener {
/**
* Returns true if the strengthener has been initialised.
*/
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
boolean isInitialised();
/**
* Initialises the strengthener if necessary and returns a strong key
* derived from the given key.
*/
SecretKey strengthenKey(SecretKey k);
}

View File

@@ -83,7 +83,7 @@ public interface DatabaseComponent extends TransactionManager {
/** /**
* Stores a pending contact. * Stores a pending contact.
*/ */
void addPendingContact(Transaction txn, PendingContact p, AuthorId local) void addPendingContact(Transaction txn, PendingContact p)
throws DbException; throws DbException;
/** /**

View File

@@ -1,29 +1,13 @@
package org.briarproject.bramble.api.db; package org.briarproject.bramble.api.db;
import org.briarproject.bramble.api.crypto.KeyStrengthener;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.File; import java.io.File;
import javax.annotation.Nullable;
@NotNullByDefault @NotNullByDefault
public interface DatabaseConfig { public interface DatabaseConfig {
/**
* Returns the directory where the database stores its data.
*/
File getDatabaseDirectory(); File getDatabaseDirectory();
/**
* Returns the directory where the encrypted database key is stored.
*/
File getDatabaseKeyDirectory(); File getDatabaseKeyDirectory();
/**
* Returns a {@link KeyStrengthener} for strengthening the encryption of
* the database key, or null if no strengthener should be used.
*/
@Nullable
KeyStrengthener getKeyStrengthener();
} }

View File

@@ -1,21 +1,9 @@
package org.briarproject.bramble.api.db; package org.briarproject.bramble.api.db;
import org.briarproject.bramble.api.contact.PendingContact;
/** /**
* Thrown when a duplicate pending contact is added to the database. This * Thrown when a duplicate pending contact is added to the database. This
* exception may occur due to concurrent updates and does not indicate a * exception may occur due to concurrent updates and does not indicate a
* database error. * database error.
*/ */
public class PendingContactExistsException extends DbException { public class PendingContactExistsException extends DbException {
private final PendingContact pendingContact;
public PendingContactExistsException(PendingContact pendingContact) {
this.pendingContact = pendingContact;
}
public PendingContact getPendingContact() {
return pendingContact;
}
} }

View File

@@ -1,15 +0,0 @@
package org.briarproject.bramble.api.io;
import java.io.InputStream;
public interface TimeoutMonitor {
/**
* Returns an {@link InputStream} that wraps the given stream and allows
* read timeouts to be detected.
*
* @param timeoutMs The read timeout in milliseconds. Timeouts will be
* detected eventually but are not guaranteed to be detected immediately.
*/
InputStream createTimeoutInputStream(InputStream in, long timeoutMs);
}

View File

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

View File

@@ -4,7 +4,6 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Plugin; import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.TransportConnectionReader; import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter; import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.properties.TransportProperties;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@@ -15,8 +14,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
public abstract class AbstractDuplexTransportConnection public abstract class AbstractDuplexTransportConnection
implements DuplexTransportConnection { implements DuplexTransportConnection {
protected final TransportProperties remote = new TransportProperties();
private final Plugin plugin; private final Plugin plugin;
private final Reader reader; private final Reader reader;
private final Writer writer; private final Writer writer;
@@ -47,11 +44,6 @@ public abstract class AbstractDuplexTransportConnection
return writer; return writer;
} }
@Override
public TransportProperties getRemoteProperties() {
return remote;
}
private class Reader implements TransportConnectionReader { private class Reader implements TransportConnectionReader {
@Override @Override

View File

@@ -3,7 +3,6 @@ package org.briarproject.bramble.api.plugin.duplex;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportConnectionReader; import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter; import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.properties.TransportProperties;
/** /**
* An interface for reading and writing data over a duplex transport. The * An interface for reading and writing data over a duplex transport. The
@@ -24,10 +23,4 @@ public interface DuplexTransportConnection {
* for writing to the connection. * for writing to the connection.
*/ */
TransportConnectionWriter getWriter(); TransportConnectionWriter getWriter();
/**
* Returns a possibly empty set of {@link TransportProperties} describing
* the remote peer.
*/
TransportProperties getRemoteProperties();
} }

View File

@@ -11,28 +11,4 @@ public interface TransportPropertyConstants {
* The maximum length of a property's key or value in UTF-8 bytes. * The maximum length of a property's key or value in UTF-8 bytes.
*/ */
int MAX_PROPERTY_LENGTH = 100; int MAX_PROPERTY_LENGTH = 100;
/**
* Message metadata key for the transport ID of a local or remote update,
* as a BDF string.
*/
String MSG_KEY_TRANSPORT_ID = "transportId";
/**
* Message metadata key for the version number of a local or remote update,
* as a BDF long.
*/
String MSG_KEY_VERSION = "version";
/**
* Message metadata key for whether an update is local or remote, as a BDF
* boolean.
*/
String MSG_KEY_LOCAL = "local";
/**
* Group metadata key for any discovered transport properties of the
* contact, as a BDF dictionary.
*/
String GROUP_KEY_DISCOVERED = "discovered";
} }

View File

@@ -34,14 +34,6 @@ public interface TransportPropertyManager {
void addRemoteProperties(Transaction txn, ContactId c, void addRemoteProperties(Transaction txn, ContactId c,
Map<TransportId, TransportProperties> props) throws DbException; Map<TransportId, TransportProperties> props) throws DbException;
/**
* Stores the given properties discovered from an incoming transport
* connection. They will be overridden by any properties received while
* adding the contact or synced from the contact.
*/
void addRemotePropertiesFromConnection(ContactId c, TransportId t,
TransportProperties props) throws DbException;
/** /**
* Returns the local transport properties for all transports. * Returns the local transport properties for all transports.
*/ */

View File

@@ -2,7 +2,6 @@ package org.briarproject.bramble.api.sync;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.transport.StreamWriter; import org.briarproject.bramble.api.transport.StreamWriter;
import java.io.InputStream; import java.io.InputStream;
@@ -12,9 +11,9 @@ public interface SyncSessionFactory {
SyncSession createIncomingSession(ContactId c, InputStream in); SyncSession createIncomingSession(ContactId c, InputStream in);
SyncSession createSimplexOutgoingSession(ContactId c, TransportId t, SyncSession createSimplexOutgoingSession(ContactId c, int maxLatency,
int maxLatency, StreamWriter streamWriter); StreamWriter streamWriter);
SyncSession createDuplexOutgoingSession(ContactId c, TransportId t, SyncSession createDuplexOutgoingSession(ContactId c, int maxLatency,
int maxLatency, int maxIdleTime, StreamWriter streamWriter); int maxIdleTime, StreamWriter streamWriter);
} }

View File

@@ -1,26 +0,0 @@
package org.briarproject.bramble.api.sync.event;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import javax.annotation.concurrent.Immutable;
/**
* An event that is broadcast when all sync connections using a given
* transport should be closed.
*/
@Immutable
@NotNullByDefault
public class CloseSyncConnectionsEvent extends Event {
private final TransportId transportId;
public CloseSyncConnectionsEvent(TransportId transportId) {
this.transportId = transportId;
}
public TransportId getTransportId() {
return transportId;
}
}

View File

@@ -1,63 +0,0 @@
package org.briarproject.bramble.api.versioning;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.ClientId;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class ClientVersion implements Comparable<ClientVersion> {
private final ClientMajorVersion majorVersion;
private final int minorVersion;
public ClientVersion(ClientMajorVersion majorVersion,
int minorVersion) {
this.majorVersion = majorVersion;
this.minorVersion = minorVersion;
}
public ClientVersion(ClientId clientId, int majorVersion,
int minorVersion) {
this(new ClientMajorVersion(clientId, majorVersion), minorVersion);
}
public ClientMajorVersion getClientMajorVersion() {
return majorVersion;
}
public ClientId getClientId() {
return majorVersion.getClientId();
}
public int getMajorVersion() {
return majorVersion.getMajorVersion();
}
public int getMinorVersion() {
return minorVersion;
}
@Override
public boolean equals(Object o) {
if (o instanceof ClientVersion) {
ClientVersion cv = (ClientVersion) o;
return majorVersion.equals(cv.majorVersion)
&& minorVersion == cv.minorVersion;
}
return false;
}
@Override
public int hashCode() {
return majorVersion.hashCode();
}
@Override
public int compareTo(ClientVersion cv) {
int compare = majorVersion.compareTo(cv.majorVersion);
if (compare != 0) return compare;
return minorVersion - cv.minorVersion;
}
}

View File

@@ -1,34 +0,0 @@
package org.briarproject.bramble.api.versioning.event;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.versioning.ClientVersion;
import javax.annotation.concurrent.Immutable;
/**
* An event that is broadcast when we receive a client versioning update from
* a contact.
*/
@Immutable
@NotNullByDefault
public class ClientVersionUpdatedEvent extends Event {
private final ContactId contactId;
private final ClientVersion clientVersion;
public ClientVersionUpdatedEvent(ContactId contactId,
ClientVersion clientVersion) {
this.contactId = contactId;
this.clientVersion = clientVersion;
}
public ContactId getContactId() {
return contactId;
}
public ClientVersion getClientVersion() {
return clientVersion;
}
}

View File

@@ -117,10 +117,4 @@ public class IoUtils {
throw new IOException(e); throw new IOException(e);
} }
} }
public static boolean isNonEmptyDirectory(File f) {
if (!f.isDirectory()) return false;
File[] children = f.listFiles();
return children != null && children.length > 0;
}
} }

View File

@@ -2,7 +2,7 @@ dependencyVerification {
verify = [ verify = [
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861', 'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7', 'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
'com.google.dagger:dagger:2.24:dagger-2.24.jar:550a6e46a6dfcdf1d764887b6090cea94f783327e50e5c73754f18facfc70b64', 'com.google.dagger:dagger:2.22.1:dagger-2.22.1.jar:329d4340f24c4f5717af016c097e90668bfea2a5376e6aa9964b01cef3fd241a',
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff', 'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a', 'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d', 'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',

View File

@@ -17,7 +17,7 @@ dependencies {
implementation 'org.whispersystems:curve25519-java:0.5.0' implementation 'org.whispersystems:curve25519-java:0.5.0'
implementation 'org.briarproject:jtorctl:0.3' implementation 'org.briarproject:jtorctl:0.3'
annotationProcessor 'com.google.dagger:dagger-compiler:2.24' annotationProcessor 'com.google.dagger:dagger-compiler:2.22.1'
testImplementation project(path: ':bramble-api', configuration: 'testOutput') testImplementation project(path: ':bramble-api', configuration: 'testOutput')
testImplementation 'org.hsqldb:hsqldb:2.3.5' // The last version that supports Java 1.6 testImplementation 'org.hsqldb:hsqldb:2.3.5' // The last version that supports Java 1.6
@@ -26,7 +26,7 @@ dependencies {
testImplementation "org.jmock:jmock-junit4:2.8.2" testImplementation "org.jmock:jmock-junit4:2.8.2"
testImplementation "org.jmock:jmock-legacy:2.8.2" testImplementation "org.jmock:jmock-legacy:2.8.2"
testAnnotationProcessor 'com.google.dagger:dagger-compiler:2.24' testAnnotationProcessor 'com.google.dagger:dagger-compiler:2.22.1'
signature 'org.codehaus.mojo.signature:java16:1.1@signature' signature 'org.codehaus.mojo.signature:java16:1.1@signature'
} }

View File

@@ -39,21 +39,18 @@ public interface BrambleCoreEagerSingletons {
void inject(VersioningModule.EagerSingletons init); void inject(VersioningModule.EagerSingletons init);
class Helper { default void injectBrambleCoreEagerSingletons() {
inject(new ContactModule.EagerSingletons());
public static void injectEagerSingletons(BrambleCoreEagerSingletons c) { inject(new CryptoExecutorModule.EagerSingletons());
c.inject(new ContactModule.EagerSingletons()); inject(new DatabaseExecutorModule.EagerSingletons());
c.inject(new CryptoExecutorModule.EagerSingletons()); inject(new IdentityModule.EagerSingletons());
c.inject(new DatabaseExecutorModule.EagerSingletons()); inject(new LifecycleModule.EagerSingletons());
c.inject(new IdentityModule.EagerSingletons()); inject(new RendezvousModule.EagerSingletons());
c.inject(new LifecycleModule.EagerSingletons()); inject(new PluginModule.EagerSingletons());
c.inject(new RendezvousModule.EagerSingletons()); inject(new PropertiesModule.EagerSingletons());
c.inject(new PluginModule.EagerSingletons()); inject(new SystemModule.EagerSingletons());
c.inject(new PropertiesModule.EagerSingletons()); inject(new TransportModule.EagerSingletons());
c.inject(new SystemModule.EagerSingletons()); inject(new ValidationModule.EagerSingletons());
c.inject(new TransportModule.EagerSingletons()); inject(new VersioningModule.EagerSingletons());
c.inject(new ValidationModule.EagerSingletons());
c.inject(new VersioningModule.EagerSingletons());
}
} }
} }

View File

@@ -9,7 +9,6 @@ import org.briarproject.bramble.db.DatabaseExecutorModule;
import org.briarproject.bramble.db.DatabaseModule; import org.briarproject.bramble.db.DatabaseModule;
import org.briarproject.bramble.event.EventModule; import org.briarproject.bramble.event.EventModule;
import org.briarproject.bramble.identity.IdentityModule; import org.briarproject.bramble.identity.IdentityModule;
import org.briarproject.bramble.io.IoModule;
import org.briarproject.bramble.keyagreement.KeyAgreementModule; import org.briarproject.bramble.keyagreement.KeyAgreementModule;
import org.briarproject.bramble.lifecycle.LifecycleModule; import org.briarproject.bramble.lifecycle.LifecycleModule;
import org.briarproject.bramble.plugin.PluginModule; import org.briarproject.bramble.plugin.PluginModule;
@@ -36,7 +35,6 @@ import dagger.Module;
DatabaseExecutorModule.class, DatabaseExecutorModule.class,
EventModule.class, EventModule.class,
IdentityModule.class, IdentityModule.class,
IoModule.class,
KeyAgreementModule.class, KeyAgreementModule.class,
LifecycleModule.class, LifecycleModule.class,
PluginModule.class, PluginModule.class,
@@ -52,4 +50,8 @@ import dagger.Module;
VersioningModule.class VersioningModule.class
}) })
public class BrambleCoreModule { public class BrambleCoreModule {
public static void initEagerSingletons(BrambleCoreEagerSingletons c) {
c.injectBrambleCoreEagerSingletons();
}
} }

View File

@@ -2,8 +2,6 @@ package org.briarproject.bramble.account;
import org.briarproject.bramble.api.account.AccountManager; import org.briarproject.bramble.api.account.AccountManager;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.DecryptionException;
import org.briarproject.bramble.api.crypto.KeyStrengthener;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseConfig; import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.identity.Identity; import org.briarproject.bramble.api.identity.Identity;
@@ -18,15 +16,12 @@ import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.crypto.DecryptionResult.INVALID_CIPHERTEXT;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.StringUtils.fromHexString; import static org.briarproject.bramble.util.StringUtils.fromHexString;
import static org.briarproject.bramble.util.StringUtils.toHexString; import static org.briarproject.bramble.util.StringUtils.toHexString;
@@ -73,10 +68,9 @@ class AccountManagerImpl implements AccountManager {
return databaseKey; return databaseKey;
} }
// Package access for testing // Locking: stateChangeLock
@GuardedBy("stateChangeLock")
@Nullable @Nullable
String loadEncryptedDatabaseKey() { protected String loadEncryptedDatabaseKey() {
String key = readDbKeyFromFile(dbKeyFile); String key = readDbKeyFromFile(dbKeyFile);
if (key == null) { if (key == null) {
LOG.info("No database key in primary file"); LOG.info("No database key in primary file");
@@ -89,7 +83,7 @@ class AccountManagerImpl implements AccountManager {
return key; return key;
} }
@GuardedBy("stateChangeLock") // Locking: stateChangeLock
@Nullable @Nullable
private String readDbKeyFromFile(File f) { private String readDbKeyFromFile(File f) {
if (!f.exists()) { if (!f.exists()) {
@@ -98,7 +92,7 @@ class AccountManagerImpl implements AccountManager {
} }
try { try {
BufferedReader reader = new BufferedReader(new InputStreamReader( BufferedReader reader = new BufferedReader(new InputStreamReader(
new FileInputStream(f), Charset.forName("UTF-8"))); new FileInputStream(f), "UTF-8"));
String key = reader.readLine(); String key = reader.readLine();
reader.close(); reader.close();
return key; return key;
@@ -108,9 +102,8 @@ class AccountManagerImpl implements AccountManager {
} }
} }
// Package access for testing // Locking: stateChangeLock
@GuardedBy("stateChangeLock") protected boolean storeEncryptedDatabaseKey(String hex) {
boolean storeEncryptedDatabaseKey(String hex) {
LOG.info("Storing database key in file"); LOG.info("Storing database key in file");
// Create the directory if necessary // Create the directory if necessary
if (databaseConfig.getDatabaseKeyDirectory().mkdirs()) if (databaseConfig.getDatabaseKeyDirectory().mkdirs())
@@ -147,10 +140,10 @@ class AccountManagerImpl implements AccountManager {
} }
} }
@GuardedBy("stateChangeLock") // Locking: stateChangeLock
private void writeDbKeyToFile(String key, File f) throws IOException { private void writeDbKeyToFile(String key, File f) throws IOException {
FileOutputStream out = new FileOutputStream(f); FileOutputStream out = new FileOutputStream(f);
out.write(key.getBytes(Charset.forName("UTF-8"))); out.write(key.getBytes("UTF-8"));
out.flush(); out.flush();
out.close(); out.close();
} }
@@ -158,7 +151,8 @@ class AccountManagerImpl implements AccountManager {
@Override @Override
public boolean accountExists() { public boolean accountExists() {
synchronized (stateChangeLock) { synchronized (stateChangeLock) {
return loadEncryptedDatabaseKey() != null; return loadEncryptedDatabaseKey() != null
&& databaseConfig.getDatabaseDirectory().isDirectory();
} }
} }
@@ -176,11 +170,10 @@ class AccountManagerImpl implements AccountManager {
} }
} }
@GuardedBy("stateChangeLock") // Locking: stateChangeLock
private boolean encryptAndStoreDatabaseKey(SecretKey key, String password) { private boolean encryptAndStoreDatabaseKey(SecretKey key, String password) {
byte[] plaintext = key.getBytes(); byte[] plaintext = key.getBytes();
byte[] ciphertext = crypto.encryptWithPassword(plaintext, password, byte[] ciphertext = crypto.encryptWithPassword(plaintext, password);
databaseConfig.getKeyStrengthener());
return storeEncryptedDatabaseKey(toHexString(ciphertext)); return storeEncryptedDatabaseKey(toHexString(ciphertext));
} }
@@ -195,41 +188,37 @@ class AccountManagerImpl implements AccountManager {
} }
@Override @Override
public void signIn(String password) throws DecryptionException { public boolean signIn(String password) {
synchronized (stateChangeLock) { synchronized (stateChangeLock) {
databaseKey = loadAndDecryptDatabaseKey(password); SecretKey key = loadAndDecryptDatabaseKey(password);
if (key == null) return false;
databaseKey = key;
return true;
} }
} }
@GuardedBy("stateChangeLock") // Locking: stateChangeLock
private SecretKey loadAndDecryptDatabaseKey(String password) @Nullable
throws DecryptionException { private SecretKey loadAndDecryptDatabaseKey(String password) {
String hex = loadEncryptedDatabaseKey(); String hex = loadEncryptedDatabaseKey();
if (hex == null) { if (hex == null) {
LOG.warning("Failed to load encrypted database key"); LOG.warning("Failed to load encrypted database key");
throw new DecryptionException(INVALID_CIPHERTEXT); return null;
} }
byte[] ciphertext = fromHexString(hex); byte[] ciphertext = fromHexString(hex);
KeyStrengthener keyStrengthener = databaseConfig.getKeyStrengthener(); byte[] plaintext = crypto.decryptWithPassword(ciphertext, password);
byte[] plaintext = crypto.decryptWithPassword(ciphertext, password, if (plaintext == null) {
keyStrengthener); LOG.info("Failed to decrypt database key");
SecretKey key = new SecretKey(plaintext); return null;
// If the DB key was encrypted with a weak key and a key strengthener
// is now available, re-encrypt the DB key with a strengthened key
if (keyStrengthener != null &&
!crypto.isEncryptedWithStrengthenedKey(ciphertext)) {
LOG.info("Re-encrypting database key with strengthened key");
encryptAndStoreDatabaseKey(key, password);
} }
return key; return new SecretKey(plaintext);
} }
@Override @Override
public void changePassword(String oldPassword, String newPassword) public boolean changePassword(String oldPassword, String newPassword) {
throws DecryptionException {
synchronized (stateChangeLock) { synchronized (stateChangeLock) {
SecretKey key = loadAndDecryptDatabaseKey(oldPassword); SecretKey key = loadAndDecryptDatabaseKey(oldPassword);
encryptAndStoreDatabaseKey(key, newPassword); return key != null && encryptAndStoreDatabaseKey(key, newPassword);
} }
} }
} }

View File

@@ -139,8 +139,7 @@ class ContactManagerImpl implements ContactManager, EventListener {
pendingContactFactory.createPendingContact(link, alias); pendingContactFactory.createPendingContact(link, alias);
Transaction txn = db.startTransaction(false); Transaction txn = db.startTransaction(false);
try { try {
AuthorId local = identityManager.getLocalAuthor(txn).getId(); db.addPendingContact(txn, p);
db.addPendingContact(txn, p, local);
KeyPair ourKeyPair = identityManager.getHandshakeKeys(txn); KeyPair ourKeyPair = identityManager.getHandshakeKeys(txn);
keyManager.addPendingContact(txn, p.getId(), p.getPublicKey(), keyManager.addPendingContact(txn, p.getId(), p.getPublicKey(),
ourKeyPair); ourKeyPair);

View File

@@ -7,10 +7,8 @@ import net.i2p.crypto.eddsa.KeyPairGenerator;
import org.briarproject.bramble.api.crypto.AgreementPrivateKey; import org.briarproject.bramble.api.crypto.AgreementPrivateKey;
import org.briarproject.bramble.api.crypto.AgreementPublicKey; import org.briarproject.bramble.api.crypto.AgreementPublicKey;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.DecryptionException;
import org.briarproject.bramble.api.crypto.KeyPair; import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.KeyParser; import org.briarproject.bramble.api.crypto.KeyParser;
import org.briarproject.bramble.api.crypto.KeyStrengthener;
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.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
@@ -40,9 +38,6 @@ import static java.lang.System.arraycopy;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_AGREEMENT; import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_AGREEMENT;
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_SIGNATURE; import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_SIGNATURE;
import static org.briarproject.bramble.api.crypto.DecryptionResult.INVALID_CIPHERTEXT;
import static org.briarproject.bramble.api.crypto.DecryptionResult.INVALID_PASSWORD;
import static org.briarproject.bramble.api.crypto.DecryptionResult.KEY_STRENGTHENER_ERROR;
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES; import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
import static org.briarproject.bramble.util.LogUtils.logDuration; import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.now; import static org.briarproject.bramble.util.LogUtils.now;
@@ -56,8 +51,7 @@ class CryptoComponentImpl implements CryptoComponent {
private static final int SIGNATURE_KEY_PAIR_BITS = 256; private static final int SIGNATURE_KEY_PAIR_BITS = 256;
private static final int STORAGE_IV_BYTES = 24; // 196 bits private static final int STORAGE_IV_BYTES = 24; // 196 bits
private static final int PBKDF_SALT_BYTES = 32; // 256 bits private static final int PBKDF_SALT_BYTES = 32; // 256 bits
private static final byte PBKDF_FORMAT_SCRYPT = 0; private static final int PBKDF_FORMAT_SCRYPT = 0;
private static final byte PBKDF_FORMAT_SCRYPT_STRENGTHENED = 1;
private final SecureRandom secureRandom; private final SecureRandom secureRandom;
private final PasswordBasedKdf passwordBasedKdf; private final PasswordBasedKdf passwordBasedKdf;
@@ -317,8 +311,7 @@ class CryptoComponentImpl implements CryptoComponent {
} }
@Override @Override
public byte[] encryptWithPassword(byte[] input, String password, public byte[] encryptWithPassword(byte[] input, String password) {
@Nullable KeyStrengthener keyStrengthener) {
AuthenticatedCipher cipher = new XSalsa20Poly1305AuthenticatedCipher(); AuthenticatedCipher cipher = new XSalsa20Poly1305AuthenticatedCipher();
int macBytes = cipher.getMacBytes(); int macBytes = cipher.getMacBytes();
// Generate a random salt // Generate a random salt
@@ -326,9 +319,8 @@ class CryptoComponentImpl implements CryptoComponent {
secureRandom.nextBytes(salt); secureRandom.nextBytes(salt);
// Calibrate the KDF // Calibrate the KDF
int cost = passwordBasedKdf.chooseCostParameter(); int cost = passwordBasedKdf.chooseCostParameter();
// Derive the encryption key from the password // Derive the key from the password
SecretKey key = passwordBasedKdf.deriveKey(password, salt, cost); SecretKey key = passwordBasedKdf.deriveKey(password, salt, cost);
if (keyStrengthener != null) key = keyStrengthener.strengthenKey(key);
// Generate a random IV // Generate a random IV
byte[] iv = new byte[STORAGE_IV_BYTES]; byte[] iv = new byte[STORAGE_IV_BYTES];
secureRandom.nextBytes(iv); secureRandom.nextBytes(iv);
@@ -339,9 +331,7 @@ class CryptoComponentImpl implements CryptoComponent {
byte[] output = new byte[outputLen]; byte[] output = new byte[outputLen];
int outputOff = 0; int outputOff = 0;
// Format version // Format version
byte formatVersion = keyStrengthener == null output[outputOff] = PBKDF_FORMAT_SCRYPT;
? PBKDF_FORMAT_SCRYPT : PBKDF_FORMAT_SCRYPT_STRENGTHENED;
output[outputOff] = formatVersion;
outputOff++; outputOff++;
// Salt // Salt
arraycopy(salt, 0, output, outputOff, salt.length); arraycopy(salt, 0, output, outputOff, salt.length);
@@ -363,26 +353,21 @@ class CryptoComponentImpl implements CryptoComponent {
} }
@Override @Override
public byte[] decryptWithPassword(byte[] input, String password, @Nullable
@Nullable KeyStrengthener keyStrengthener) public byte[] decryptWithPassword(byte[] input, String password) {
throws DecryptionException {
AuthenticatedCipher cipher = new XSalsa20Poly1305AuthenticatedCipher(); AuthenticatedCipher cipher = new XSalsa20Poly1305AuthenticatedCipher();
int macBytes = cipher.getMacBytes(); int macBytes = cipher.getMacBytes();
// The input contains the format version, salt, cost parameter, IV, // The input contains the format version, salt, cost parameter, IV,
// ciphertext and MAC // ciphertext and MAC
if (input.length < 1 + PBKDF_SALT_BYTES + INT_32_BYTES if (input.length < 1 + PBKDF_SALT_BYTES + INT_32_BYTES
+ STORAGE_IV_BYTES + macBytes) { + STORAGE_IV_BYTES + macBytes)
throw new DecryptionException(INVALID_CIPHERTEXT); return null; // Invalid input
}
int inputOff = 0; int inputOff = 0;
// Format version // Format version
byte formatVersion = input[inputOff]; byte formatVersion = input[inputOff];
inputOff++; inputOff++;
// Check whether we support this format version if (formatVersion != PBKDF_FORMAT_SCRYPT)
if (formatVersion != PBKDF_FORMAT_SCRYPT && return null; // Unknown format
formatVersion != PBKDF_FORMAT_SCRYPT_STRENGTHENED) {
throw new DecryptionException(INVALID_CIPHERTEXT);
}
// Salt // Salt
byte[] salt = new byte[PBKDF_SALT_BYTES]; byte[] salt = new byte[PBKDF_SALT_BYTES];
arraycopy(input, inputOff, salt, 0, salt.length); arraycopy(input, inputOff, salt, 0, salt.length);
@@ -390,22 +375,14 @@ class CryptoComponentImpl implements CryptoComponent {
// Cost parameter // Cost parameter
long cost = ByteUtils.readUint32(input, inputOff); long cost = ByteUtils.readUint32(input, inputOff);
inputOff += INT_32_BYTES; inputOff += INT_32_BYTES;
if (cost < 2 || cost > Integer.MAX_VALUE) { if (cost < 2 || cost > Integer.MAX_VALUE)
throw new DecryptionException(INVALID_CIPHERTEXT); return null; // Invalid cost parameter
}
// IV // IV
byte[] iv = new byte[STORAGE_IV_BYTES]; byte[] iv = new byte[STORAGE_IV_BYTES];
arraycopy(input, inputOff, iv, 0, iv.length); arraycopy(input, inputOff, iv, 0, iv.length);
inputOff += iv.length; inputOff += iv.length;
// Derive the decryption key from the password // Derive the key from the password
SecretKey key = passwordBasedKdf.deriveKey(password, salt, (int) cost); SecretKey key = passwordBasedKdf.deriveKey(password, salt, (int) cost);
if (formatVersion == PBKDF_FORMAT_SCRYPT_STRENGTHENED) {
if (keyStrengthener == null || !keyStrengthener.isInitialised()) {
// Can't derive the same strengthened key
throw new DecryptionException(KEY_STRENGTHENER_ERROR);
}
key = keyStrengthener.strengthenKey(key);
}
// Initialise the cipher // Initialise the cipher
try { try {
cipher.init(false, key, iv); cipher.init(false, key, iv);
@@ -419,16 +396,10 @@ class CryptoComponentImpl implements CryptoComponent {
cipher.process(input, inputOff, inputLen, output, 0); cipher.process(input, inputOff, inputLen, output, 0);
return output; return output;
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
throw new DecryptionException(INVALID_PASSWORD); return null; // Invalid ciphertext
} }
} }
@Override
public boolean isEncryptedWithStrengthenedKey(byte[] ciphertext) {
return ciphertext.length > 0 &&
ciphertext[0] == PBKDF_FORMAT_SCRYPT_STRENGTHENED;
}
@Override @Override
public byte[] encryptToKey(PublicKey publicKey, byte[] plaintext) { public byte[] encryptToKey(PublicKey publicKey, byte[] plaintext) {
try { try {

View File

@@ -267,16 +267,6 @@ interface Database<T> {
*/ */
Collection<ContactId> getContacts(T txn, AuthorId local) throws DbException; Collection<ContactId> getContacts(T txn, AuthorId local) throws DbException;
/**
* Returns the contact with the given {@code handshakePublicKey}
* for the given local pseudonym or {@code null} if none exists.
* <p/>
* Read-only.
*/
@Nullable
Contact getContact(T txn, PublicKey handshakePublicKey, AuthorId local)
throws DbException;
/** /**
* Returns the group with the given ID. * Returns the group with the given ID.
* <p/> * <p/>

View File

@@ -291,17 +291,12 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
@Override @Override
public void addPendingContact(Transaction transaction, PendingContact p, public void addPendingContact(Transaction transaction, PendingContact p)
AuthorId local) throws DbException { throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
Contact contact = db.getContact(txn, p.getPublicKey(), local); if (db.containsPendingContact(txn, p.getId()))
if (contact != null) throw new PendingContactExistsException();
throw new ContactExistsException(local, contact.getAuthor());
if (db.containsPendingContact(txn, p.getId())) {
PendingContact existing = db.getPendingContact(txn, p.getId());
throw new PendingContactExistsException(existing);
}
db.addPendingContact(txn, p); db.addPendingContact(txn, p);
transaction.attach(new PendingContactAddedEvent(p)); transaction.attach(new PendingContactAddedEvent(p));
} }

View File

@@ -25,7 +25,6 @@ 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.db.JdbcUtils.tryToClose; import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
import static org.briarproject.bramble.util.IoUtils.isNonEmptyDirectory;
import static org.briarproject.bramble.util.LogUtils.logFileOrDir; import static org.briarproject.bramble.util.LogUtils.logFileOrDir;
/** /**
@@ -70,9 +69,8 @@ class H2Database extends JdbcDatabase {
LOG.info("Contents of account directory before opening DB:"); LOG.info("Contents of account directory before opening DB:");
logFileOrDir(LOG, INFO, dir.getParentFile()); logFileOrDir(LOG, INFO, dir.getParentFile());
} }
boolean reopen = isNonEmptyDirectory(dir); boolean reopen = !dir.mkdirs();
if (LOG.isLoggable(INFO)) LOG.info("Reopening DB: " + reopen); if (LOG.isLoggable(INFO)) LOG.info("Reopening DB: " + reopen);
if (!reopen && dir.mkdirs()) LOG.info("Created database directory");
super.open("org.h2.Driver", reopen, key, listener); super.open("org.h2.Driver", reopen, key, listener);
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO)) {
LOG.info("Contents of account directory after opening DB:"); LOG.info("Contents of account directory after opening DB:");

View File

@@ -20,11 +20,9 @@ import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
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.db.JdbcUtils.tryToClose; import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
import static org.briarproject.bramble.util.IoUtils.isNonEmptyDirectory;
/** /**
* Contains all the HSQLDB-specific code for the database. * Contains all the HSQLDB-specific code for the database.
@@ -66,10 +64,7 @@ class HyperSqlDatabase extends JdbcDatabase {
public boolean open(SecretKey key, @Nullable MigrationListener listener) public boolean open(SecretKey key, @Nullable MigrationListener listener)
throws DbException { throws DbException {
this.key = key; this.key = key;
File dir = config.getDatabaseDirectory(); boolean reopen = !config.getDatabaseDirectory().mkdirs();
boolean reopen = isNonEmptyDirectory(dir);
if (LOG.isLoggable(INFO)) LOG.info("Reopening DB: " + reopen);
if (!reopen && dir.mkdirs()) LOG.info("Created database directory");
super.open("org.hsqldb.jdbc.JDBCDriver", reopen, key, listener); super.open("org.hsqldb.jdbc.JDBCDriver", reopen, key, listener);
return reopen; return reopen;
} }

View File

@@ -1465,47 +1465,6 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
@Nullable
@Override
public Contact getContact(Connection txn, PublicKey handshakePublicKey,
AuthorId localAuthorId) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT contactId, authorId, formatVersion, name,"
+ " alias, publicKey, verified"
+ " FROM contacts"
+ " WHERE handshakePublicKey = ? AND localAuthorId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, handshakePublicKey.getEncoded());
ps.setBytes(2, localAuthorId.getBytes());
rs = ps.executeQuery();
if (!rs.next()) {
rs.close();
ps.close();
return null;
}
ContactId contactId = new ContactId(rs.getInt(1));
AuthorId authorId = new AuthorId(rs.getBytes(2));
int formatVersion = rs.getInt(3);
String name = rs.getString(4);
String alias = rs.getString(5);
PublicKey publicKey = new SignaturePublicKey(rs.getBytes(6));
boolean verified = rs.getBoolean(7);
if (rs.next()) throw new DbStateException();
rs.close();
ps.close();
Author author =
new Author(authorId, formatVersion, name, publicKey);
return new Contact(contactId, author, localAuthorId, alias,
handshakePublicKey, verified);
} catch (SQLException e) {
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override @Override
public Group getGroup(Connection txn, GroupId g) throws DbException { public Group getGroup(Connection txn, GroupId g) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;

View File

@@ -1,18 +0,0 @@
package org.briarproject.bramble.io;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
@Module
public class IoModule {
@Provides
@Singleton
TimeoutMonitor provideTimeoutMonitor(TimeoutMonitorImpl timeoutMonitor) {
return timeoutMonitor;
}
}

View File

@@ -1,104 +0,0 @@
package org.briarproject.bramble.io;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.Clock;
import java.io.IOException;
import java.io.InputStream;
import javax.annotation.concurrent.GuardedBy;
@NotNullByDefault
class TimeoutInputStream extends InputStream {
private final Clock clock;
private final InputStream in;
private final long timeoutMs;
private final CloseListener listener;
private final Object lock = new Object();
@GuardedBy("lock")
private long readStartedMs = -1;
TimeoutInputStream(Clock clock, InputStream in, long timeoutMs,
CloseListener listener) {
this.clock = clock;
this.in = in;
this.timeoutMs = timeoutMs;
this.listener = listener;
}
@Override
public int read() throws IOException {
synchronized (lock) {
readStartedMs = clock.currentTimeMillis();
}
int input = in.read();
synchronized (lock) {
readStartedMs = -1;
}
return input;
}
@Override
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
synchronized (lock) {
readStartedMs = clock.currentTimeMillis();
}
int read = in.read(b, off, len);
synchronized (lock) {
readStartedMs = -1;
}
return read;
}
@Override
public void close() throws IOException {
try {
in.close();
} finally {
listener.onClose(this);
}
}
@Override
public int available() throws IOException {
return in.available();
}
@Override
public void mark(int readlimit) {
in.mark(readlimit);
}
@Override
public boolean markSupported() {
return in.markSupported();
}
@Override
public void reset() throws IOException {
in.reset();
}
@Override
public long skip(long n) throws IOException {
return in.skip(n);
}
boolean hasTimedOut() {
synchronized (lock) {
return readStartedMs != -1 &&
clock.currentTimeMillis() - readStartedMs > timeoutMs;
}
}
interface CloseListener {
void onClose(TimeoutInputStream closed);
}
}

View File

@@ -1,96 +0,0 @@
package org.briarproject.bramble.io;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.Scheduler;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.logging.Logger;
import javax.annotation.concurrent.GuardedBy;
import javax.inject.Inject;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
import static java.util.logging.Level.INFO;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logException;
class TimeoutMonitorImpl implements TimeoutMonitor {
private static final Logger LOG =
getLogger(TimeoutMonitorImpl.class.getName());
private static final long CHECK_INTERVAL_MS = SECONDS.toMillis(10);
private final ScheduledExecutorService scheduler;
private final Executor ioExecutor;
private final Clock clock;
private final Object lock = new Object();
@GuardedBy("lock")
private final List<TimeoutInputStream> streams = new ArrayList<>();
@GuardedBy("lock")
private Future<?> task = null;
@Inject
TimeoutMonitorImpl(@Scheduler ScheduledExecutorService scheduler,
@IoExecutor Executor ioExecutor, Clock clock) {
this.scheduler = scheduler;
this.ioExecutor = ioExecutor;
this.clock = clock;
}
@Override
public InputStream createTimeoutInputStream(InputStream in,
long timeoutMs) {
TimeoutInputStream stream = new TimeoutInputStream(clock, in,
timeoutMs, this::removeStream);
synchronized (lock) {
if (streams.isEmpty()) {
task = scheduler.scheduleWithFixedDelay(this::checkTimeouts,
CHECK_INTERVAL_MS, CHECK_INTERVAL_MS, MILLISECONDS);
}
streams.add(stream);
}
return stream;
}
private void removeStream(TimeoutInputStream stream) {
Future<?> toCancel = null;
synchronized (lock) {
if (streams.remove(stream) && streams.isEmpty()) {
toCancel = task;
task = null;
}
}
if (toCancel != null) toCancel.cancel(false);
}
@Scheduler
private void checkTimeouts() {
ioExecutor.execute(() -> {
List<TimeoutInputStream> snapshot;
synchronized (lock) {
snapshot = new ArrayList<>(streams);
}
for (TimeoutInputStream stream : snapshot) {
if (stream.hasTimedOut()) {
LOG.info("Input stream has timed out");
try {
stream.close();
} catch (IOException e) {
logException(LOG, INFO, e);
}
}
}
});
}
}

View File

@@ -15,8 +15,6 @@ import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter; import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
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.properties.TransportPropertyManager;
import org.briarproject.bramble.api.sync.SyncSession; import org.briarproject.bramble.api.sync.SyncSession;
import org.briarproject.bramble.api.sync.SyncSessionFactory; import org.briarproject.bramble.api.sync.SyncSessionFactory;
import org.briarproject.bramble.api.transport.KeyManager; import org.briarproject.bramble.api.transport.KeyManager;
@@ -54,7 +52,6 @@ class ConnectionManagerImpl implements ConnectionManager {
private final HandshakeManager handshakeManager; private final HandshakeManager handshakeManager;
private final ContactExchangeManager contactExchangeManager; private final ContactExchangeManager contactExchangeManager;
private final ConnectionRegistry connectionRegistry; private final ConnectionRegistry connectionRegistry;
private final TransportPropertyManager transportPropertyManager;
@Inject @Inject
ConnectionManagerImpl(@IoExecutor Executor ioExecutor, ConnectionManagerImpl(@IoExecutor Executor ioExecutor,
@@ -63,8 +60,7 @@ class ConnectionManagerImpl implements ConnectionManager {
SyncSessionFactory syncSessionFactory, SyncSessionFactory syncSessionFactory,
HandshakeManager handshakeManager, HandshakeManager handshakeManager,
ContactExchangeManager contactExchangeManager, ContactExchangeManager contactExchangeManager,
ConnectionRegistry connectionRegistry, ConnectionRegistry connectionRegistry) {
TransportPropertyManager transportPropertyManager) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.keyManager = keyManager; this.keyManager = keyManager;
this.streamReaderFactory = streamReaderFactory; this.streamReaderFactory = streamReaderFactory;
@@ -73,7 +69,6 @@ class ConnectionManagerImpl implements ConnectionManager {
this.handshakeManager = handshakeManager; this.handshakeManager = handshakeManager;
this.contactExchangeManager = contactExchangeManager; this.contactExchangeManager = contactExchangeManager;
this.connectionRegistry = connectionRegistry; this.connectionRegistry = connectionRegistry;
this.transportPropertyManager = transportPropertyManager;
} }
@Override @Override
@@ -130,8 +125,8 @@ class ConnectionManagerImpl implements ConnectionManager {
TransportConnectionWriter w) throws IOException { TransportConnectionWriter w) throws IOException {
StreamWriter streamWriter = streamWriterFactory.createStreamWriter( StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
w.getOutputStream(), ctx); w.getOutputStream(), ctx);
return syncSessionFactory.createSimplexOutgoingSession( ContactId c = requireNonNull(ctx.getContactId());
requireNonNull(ctx.getContactId()), ctx.getTransportId(), return syncSessionFactory.createSimplexOutgoingSession(c,
w.getMaxLatency(), streamWriter); w.getMaxLatency(), streamWriter);
} }
@@ -139,8 +134,8 @@ class ConnectionManagerImpl implements ConnectionManager {
TransportConnectionWriter w) throws IOException { TransportConnectionWriter w) throws IOException {
StreamWriter streamWriter = streamWriterFactory.createStreamWriter( StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
w.getOutputStream(), ctx); w.getOutputStream(), ctx);
return syncSessionFactory.createDuplexOutgoingSession( ContactId c = requireNonNull(ctx.getContactId());
requireNonNull(ctx.getContactId()), ctx.getTransportId(), return syncSessionFactory.createDuplexOutgoingSession(c,
w.getMaxLatency(), w.getMaxIdleTime(), streamWriter); w.getMaxLatency(), w.getMaxIdleTime(), streamWriter);
} }
@@ -274,7 +269,6 @@ class ConnectionManagerImpl implements ConnectionManager {
private final TransportId transportId; private final TransportId transportId;
private final TransportConnectionReader reader; private final TransportConnectionReader reader;
private final TransportConnectionWriter writer; private final TransportConnectionWriter writer;
private final TransportProperties remote;
@Nullable @Nullable
private volatile SyncSession outgoingSession = null; private volatile SyncSession outgoingSession = null;
@@ -284,7 +278,6 @@ class ConnectionManagerImpl implements ConnectionManager {
this.transportId = transportId; this.transportId = transportId;
reader = connection.getReader(); reader = connection.getReader();
writer = connection.getWriter(); writer = connection.getWriter();
remote = connection.getRemoteProperties();
} }
@Override @Override
@@ -320,16 +313,13 @@ class ConnectionManagerImpl implements ConnectionManager {
// Start the outgoing session on another thread // Start the outgoing session on another thread
ioExecutor.execute(() -> runOutgoingSession(contactId)); ioExecutor.execute(() -> runOutgoingSession(contactId));
try { try {
// Store any transport properties discovered from the connection
transportPropertyManager.addRemotePropertiesFromConnection(
contactId, transportId, remote);
// Create and run the incoming session // Create and run the incoming session
createIncomingSession(ctx, reader).run(); createIncomingSession(ctx, reader).run();
reader.dispose(false, true); reader.dispose(false, true);
// Interrupt the outgoing session so it finishes cleanly // Interrupt the outgoing session so it finishes cleanly
SyncSession out = outgoingSession; SyncSession out = outgoingSession;
if (out != null) out.interrupt(); if (out != null) out.interrupt();
} catch (DbException | IOException e) { } catch (IOException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
onReadError(true); onReadError(true);
} finally { } finally {
@@ -385,7 +375,6 @@ class ConnectionManagerImpl implements ConnectionManager {
private final TransportId transportId; private final TransportId transportId;
private final TransportConnectionReader reader; private final TransportConnectionReader reader;
private final TransportConnectionWriter writer; private final TransportConnectionWriter writer;
private final TransportProperties remote;
@Nullable @Nullable
private volatile SyncSession outgoingSession = null; private volatile SyncSession outgoingSession = null;
@@ -396,7 +385,6 @@ class ConnectionManagerImpl implements ConnectionManager {
this.transportId = transportId; this.transportId = transportId;
reader = connection.getReader(); reader = connection.getReader();
writer = connection.getWriter(); writer = connection.getWriter();
remote = connection.getRemoteProperties();
} }
@Override @Override
@@ -473,16 +461,13 @@ class ConnectionManagerImpl implements ConnectionManager {
connectionRegistry.registerConnection(contactId, transportId, connectionRegistry.registerConnection(contactId, transportId,
false); false);
try { try {
// Store any transport properties discovered from the connection
transportPropertyManager.addRemotePropertiesFromConnection(
contactId, transportId, remote);
// Create and run the incoming session // Create and run the incoming session
createIncomingSession(ctx, reader).run(); createIncomingSession(ctx, reader).run();
reader.dispose(false, true); reader.dispose(false, true);
// Interrupt the outgoing session so it finishes cleanly // Interrupt the outgoing session so it finishes cleanly
SyncSession out = outgoingSession; SyncSession out = outgoingSession;
if (out != null) out.interrupt(); if (out != null) out.interrupt();
} catch (DbException | IOException e) { } catch (IOException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
onReadError(); onReadError();
} finally { } finally {

View File

@@ -3,30 +3,9 @@ package org.briarproject.bramble.plugin.bluetooth;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import static java.util.concurrent.TimeUnit.DAYS;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
@NotNullByDefault @NotNullByDefault
interface BluetoothConnectionLimiter { interface BluetoothConnectionLimiter {
/**
* How long a connection must remain open before it's considered stable.
*/
long STABILITY_PERIOD_MS = SECONDS.toMillis(90);
/**
* The minimum interval between attempts to raise the connection limit.
* This is longer than {@link #STABILITY_PERIOD_MS} so we don't start
* another attempt before knowing the outcome of the last one.
*/
long MIN_ATTEMPT_INTERVAL_MS = MINUTES.toMillis(2);
/**
* The maximum interval between attempts to raise the connection limit.
*/
long MAX_ATTEMPT_INTERVAL_MS = DAYS.toMillis(2);
/** /**
* Informs the limiter that key agreement has started. * Informs the limiter that key agreement has started.
*/ */
@@ -44,12 +23,12 @@ interface BluetoothConnectionLimiter {
boolean canOpenContactConnection(); boolean canOpenContactConnection();
/** /**
* Informs the limiter that a contact connection has been opened. * Informs the limiter that a contact connection has been opened. The
* limiter may close the new connection if key agreement is in progress.
* <p/> * <p/>
* Returns true if the connection is allowed. * Returns false if the limiter has closed the new connection.
*/ */
boolean contactConnectionOpened(DuplexTransportConnection conn, boolean contactConnectionOpened(DuplexTransportConnection conn);
boolean incoming);
/** /**
* Informs the limiter that a key agreement connection has been opened. * Informs the limiter that a key agreement connection has been opened.
@@ -58,13 +37,11 @@ interface BluetoothConnectionLimiter {
/** /**
* Informs the limiter that the given connection has been closed. * Informs the limiter that the given connection has been closed.
*
* @param exception True if the connection was closed due to an exception.
*/ */
void connectionClosed(DuplexTransportConnection conn, boolean exception); void connectionClosed(DuplexTransportConnection conn);
/** /**
* Informs the limiter that the Bluetooth adapter has been disabled. * Informs the limiter that all connections have been closed.
*/ */
void bluetoothDisabled(); void allConnectionsClosed();
} }

View File

@@ -1,59 +1,46 @@
package org.briarproject.bramble.plugin.bluetooth; package org.briarproject.bramble.plugin.bluetooth;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.sync.event.CloseSyncConnectionsEvent;
import org.briarproject.bramble.api.system.Clock;
import java.util.Iterator; import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject;
import static java.lang.Math.min;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID; import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault @NotNullByDefault
@ThreadSafe @ThreadSafe
class BluetoothConnectionLimiterImpl implements BluetoothConnectionLimiter { class BluetoothConnectionLimiterImpl implements BluetoothConnectionLimiter {
private static final Logger LOG = private static final Logger LOG =
getLogger(BluetoothConnectionLimiterImpl.class.getName()); Logger.getLogger(BluetoothConnectionLimiterImpl.class.getName());
private final EventBus eventBus;
private final Clock clock;
private final Object lock = new Object(); private final Object lock = new Object();
@GuardedBy("lock") // The following are locking: lock
private final List<ConnectionRecord> connections = new LinkedList<>(); private final LinkedList<DuplexTransportConnection> connections =
@GuardedBy("lock") new LinkedList<>();
private boolean keyAgreementInProgress = false; private boolean keyAgreementInProgress = false;
@GuardedBy("lock")
private int connectionLimit = 1;
@GuardedBy("lock")
private long timeOfLastAttempt = 0,
attemptInterval = MIN_ATTEMPT_INTERVAL_MS;
@Inject
BluetoothConnectionLimiterImpl(EventBus eventBus, Clock clock) {
this.eventBus = eventBus;
this.clock = clock;
}
@Override @Override
public void keyAgreementStarted() { public void keyAgreementStarted() {
List<DuplexTransportConnection> close;
synchronized (lock) { synchronized (lock) {
keyAgreementInProgress = true; keyAgreementInProgress = true;
close = new ArrayList<>(connections);
connections.clear();
} }
LOG.info("Key agreement started"); if (LOG.isLoggable(INFO)) {
eventBus.broadcast(new CloseSyncConnectionsEvent(ID)); LOG.info("Key agreement started, closing " + close.size() +
" connections");
}
for (DuplexTransportConnection conn : close) tryToClose(conn);
} }
@Override @Override
@@ -68,128 +55,62 @@ class BluetoothConnectionLimiterImpl implements BluetoothConnectionLimiter {
public boolean canOpenContactConnection() { public boolean canOpenContactConnection() {
synchronized (lock) { synchronized (lock) {
if (keyAgreementInProgress) { if (keyAgreementInProgress) {
LOG.info("Refusing contact connection during key agreement"); LOG.info("Can't open contact connection during key agreement");
return false; return false;
} else { } else {
long now = clock.currentTimeMillis(); LOG.info("Can open contact connection");
return isContactConnectionAllowedByLimit(now); return true;
} }
} }
} }
@Override @Override
public boolean contactConnectionOpened(DuplexTransportConnection conn, public boolean contactConnectionOpened(DuplexTransportConnection conn) {
boolean incoming) { boolean accept = true;
synchronized (lock) { synchronized (lock) {
if (keyAgreementInProgress) { if (keyAgreementInProgress) {
LOG.info("Refusing contact connection during key agreement"); LOG.info("Refusing contact connection during key agreement");
return false; accept = false;
} else { } else {
long now = clock.currentTimeMillis(); LOG.info("Accepting contact connection");
if (incoming || isContactConnectionAllowedByLimit(now)) { connections.add(conn);
connections.add(new ConnectionRecord(conn, now));
if (!incoming && connections.size() > connectionLimit) {
LOG.info("Attempting to raise connection limit");
timeOfLastAttempt = now;
}
return true;
} else {
return false;
}
} }
} }
if (!accept) tryToClose(conn);
return accept;
} }
@Override @Override
public void keyAgreementConnectionOpened(DuplexTransportConnection conn) { public void keyAgreementConnectionOpened(DuplexTransportConnection conn) {
synchronized (lock) { synchronized (lock) {
LOG.info("Accepting key agreement connection"); LOG.info("Accepting key agreement connection");
connections.add( connections.add(conn);
new ConnectionRecord(conn, clock.currentTimeMillis())); }
}
private void tryToClose(DuplexTransportConnection conn) {
try {
conn.getWriter().dispose(false);
conn.getReader().dispose(false, false);
} catch (IOException e) {
logException(LOG, WARNING, e);
} }
} }
@Override @Override
public void connectionClosed(DuplexTransportConnection conn, public void connectionClosed(DuplexTransportConnection conn) {
boolean exception) {
synchronized (lock) { synchronized (lock) {
Iterator<ConnectionRecord> it = connections.iterator(); connections.remove(conn);
while (it.hasNext()) {
if (it.next().connection == conn) {
long now = clock.currentTimeMillis();
if (exception) connectionFailed(now);
else considerRaisingConnectionLimit(now);
it.remove();
break;
}
}
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Connection closed, " + connections.size() + " open"); LOG.info("Connection closed, " + connections.size() + " open");
} }
} }
@Override @Override
public void bluetoothDisabled() { public void allConnectionsClosed() {
synchronized (lock) { synchronized (lock) {
LOG.info("Bluetooth disabled");
considerRaisingConnectionLimit(clock.currentTimeMillis());
connections.clear(); connections.clear();
} LOG.info("All connections closed");
}
@GuardedBy("lock")
private boolean isContactConnectionAllowedByLimit(long now) {
considerRaisingConnectionLimit(now);
if (connections.size() > connectionLimit) {
LOG.info("Refusing contact connection, above limit");
return false;
} else if (connections.size() < connectionLimit) {
LOG.info("Allowing contact connection, below limit");
return true;
} else if (now - timeOfLastAttempt >= attemptInterval) {
LOG.info("Allowing contact connection, at limit");
return true;
} else {
LOG.info("Refusing contact connection, at limit");
return false;
}
}
@GuardedBy("lock")
private void considerRaisingConnectionLimit(long now) {
int stable = 0;
for (ConnectionRecord rec : connections) {
if (now - rec.timeOpened >= STABILITY_PERIOD_MS) stable++;
}
if (stable > connectionLimit) {
LOG.info("Raising connection limit");
connectionLimit = stable;
attemptInterval = MIN_ATTEMPT_INTERVAL_MS;
}
if (LOG.isLoggable(INFO)) {
LOG.info(stable + " connections are stable, limit is "
+ connectionLimit);
}
}
@GuardedBy("lock")
private void connectionFailed(long now) {
if (connections.size() > connectionLimit &&
now - timeOfLastAttempt < STABILITY_PERIOD_MS) {
LOG.info("Connection failed above limit, increasing interval");
attemptInterval = min(attemptInterval * 2, MAX_ATTEMPT_INTERVAL_MS);
}
}
private static final class ConnectionRecord {
private final DuplexTransportConnection connection;
private final long timeOpened;
private ConnectionRecord(DuplexTransportConnection connection,
long timeOpened) {
this.connection = connection;
this.timeOpened = timeOpened;
} }
} }
} }

View File

@@ -5,7 +5,6 @@ import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventListener; import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection; import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener; import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementListeningEvent; import org.briarproject.bramble.api.keyagreement.event.KeyAgreementListeningEvent;
@@ -61,13 +60,12 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
getLogger(BluetoothPlugin.class.getName()); getLogger(BluetoothPlugin.class.getName());
final BluetoothConnectionLimiter connectionLimiter; final BluetoothConnectionLimiter connectionLimiter;
final TimeoutMonitor timeoutMonitor;
private final Executor ioExecutor; private final Executor ioExecutor;
private final SecureRandom secureRandom; private final SecureRandom secureRandom;
private final Backoff backoff; private final Backoff backoff;
private final PluginCallback callback; private final PluginCallback callback;
private final int maxLatency, maxIdleTime; private final int maxLatency;
private final AtomicBoolean used = new AtomicBoolean(false); private final AtomicBoolean used = new AtomicBoolean(false);
private volatile boolean running = false, contactConnections = false; private volatile boolean running = false, contactConnections = false;
@@ -107,17 +105,14 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
abstract DuplexTransportConnection discoverAndConnect(String uuid); abstract DuplexTransportConnection discoverAndConnect(String uuid);
BluetoothPlugin(BluetoothConnectionLimiter connectionLimiter, BluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
TimeoutMonitor timeoutMonitor, Executor ioExecutor, Executor ioExecutor, SecureRandom secureRandom,
SecureRandom secureRandom, Backoff backoff, Backoff backoff, PluginCallback callback, int maxLatency) {
PluginCallback callback, int maxLatency, int maxIdleTime) {
this.connectionLimiter = connectionLimiter; this.connectionLimiter = connectionLimiter;
this.timeoutMonitor = timeoutMonitor;
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.secureRandom = secureRandom; this.secureRandom = secureRandom;
this.backoff = backoff; this.backoff = backoff;
this.callback = callback; this.callback = callback;
this.maxLatency = maxLatency; this.maxLatency = maxLatency;
this.maxIdleTime = maxIdleTime;
} }
void onAdapterEnabled() { void onAdapterEnabled() {
@@ -130,7 +125,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
void onAdapterDisabled() { void onAdapterDisabled() {
LOG.info("Bluetooth disabled"); LOG.info("Bluetooth disabled");
tryToClose(socket); tryToClose(socket);
connectionLimiter.bluetoothDisabled(); connectionLimiter.allConnectionsClosed();
callback.transportDisabled(); callback.transportDisabled();
} }
@@ -146,7 +141,8 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
@Override @Override
public int getMaxIdleTime() { public int getMaxIdleTime() {
return maxIdleTime; // Bluetooth detects dead connections so we don't need keepalives
return Integer.MAX_VALUE;
} }
@Override @Override
@@ -231,26 +227,13 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
if (LOG.isLoggable(INFO)) LOG.info(e.toString()); if (LOG.isLoggable(INFO)) LOG.info(e.toString());
return; return;
} }
LOG.info("Connection received"); backoff.reset();
if (connectionLimiter.contactConnectionOpened(conn, true)) { if (connectionLimiter.contactConnectionOpened(conn))
backoff.reset();
callback.handleConnection(conn); callback.handleConnection(conn);
} else {
tryToClose(conn);
}
if (!running) return; if (!running) return;
} }
} }
private void tryToClose(DuplexTransportConnection conn) {
try {
conn.getWriter().dispose(false);
conn.getReader().dispose(false, false);
} catch (IOException e) {
logException(LOG, WARNING, e);
}
}
@Override @Override
public void stop() { public void stop() {
running = false; running = false;
@@ -290,10 +273,13 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
String uuid = p.get(PROP_UUID); String uuid = p.get(PROP_UUID);
if (isNullOrEmpty(uuid)) return; if (isNullOrEmpty(uuid)) return;
ioExecutor.execute(() -> { ioExecutor.execute(() -> {
if (!isRunning() || !shouldAllowContactConnections()) return;
if (!connectionLimiter.canOpenContactConnection()) return;
DuplexTransportConnection d = createConnection(p); DuplexTransportConnection d = createConnection(p);
if (d != null) { if (d != null) {
backoff.reset(); backoff.reset();
h.handleConnection(d); if (connectionLimiter.contactConnectionOpened(d))
h.handleConnection(d);
} }
}); });
} }
@@ -339,12 +325,8 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
if (isNullOrEmpty(uuid)) return null; if (isNullOrEmpty(uuid)) return null;
DuplexTransportConnection conn = connect(address, uuid); DuplexTransportConnection conn = connect(address, uuid);
if (conn == null) return null; if (conn == null) return null;
if (connectionLimiter.contactConnectionOpened(conn, false)) { // TODO: Why don't we reset the backoff here?
return conn; return connectionLimiter.contactConnectionOpened(conn) ? conn : null;
} else {
tryToClose(conn);
return null;
}
} }
@Override @Override

View File

@@ -17,20 +17,18 @@ import java.io.IOException;
import java.net.Inet4Address; import java.net.Inet4Address;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.InterfaceAddress;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.net.SocketAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Random;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable;
import static java.lang.Integer.parseInt;
import static java.util.Collections.addAll; import static java.util.Collections.addAll;
import static java.util.Collections.emptyList;
import static java.util.Collections.sort; import static java.util.Collections.sort;
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;
@@ -39,7 +37,6 @@ import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TR
import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID; import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID;
import static org.briarproject.bramble.api.plugin.LanTcpConstants.PREF_LAN_IP_PORTS; import static org.briarproject.bramble.api.plugin.LanTcpConstants.PREF_LAN_IP_PORTS;
import static org.briarproject.bramble.api.plugin.LanTcpConstants.PROP_IP_PORTS; import static org.briarproject.bramble.api.plugin.LanTcpConstants.PROP_IP_PORTS;
import static org.briarproject.bramble.api.plugin.LanTcpConstants.PROP_PORT;
import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED; import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED;
import static org.briarproject.bramble.util.PrivacyUtils.scrubSocketAddress; import static org.briarproject.bramble.util.PrivacyUtils.scrubSocketAddress;
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
@@ -50,36 +47,15 @@ class LanTcpPlugin extends TcpPlugin {
private static final Logger LOG = getLogger(LanTcpPlugin.class.getName()); private static final Logger LOG = getLogger(LanTcpPlugin.class.getName());
private static final LanAddressComparator ADDRESS_COMPARATOR =
new LanAddressComparator();
private static final int MAX_ADDRESSES = 4; private static final int MAX_ADDRESSES = 4;
private static final String SEPARATOR = ","; private static final String SEPARATOR = ",";
/**
* The IP address of an Android device providing a wifi access point.
*/
protected static final InetAddress WIFI_AP_ADDRESS;
/**
* The IP address of an Android device providing a wifi direct
* legacy mode access point.
*/
protected static final InetAddress WIFI_DIRECT_AP_ADDRESS;
static {
try {
WIFI_AP_ADDRESS = InetAddress.getByAddress(
new byte[] {(byte) 192, (byte) 168, 43, 1});
WIFI_DIRECT_AP_ADDRESS = InetAddress.getByAddress(
new byte[] {(byte) 192, (byte) 168, 49, 1});
} catch (UnknownHostException e) {
// Should only be thrown if the address has an illegal length
throw new AssertionError(e);
}
}
LanTcpPlugin(Executor ioExecutor, Backoff backoff, PluginCallback callback, LanTcpPlugin(Executor ioExecutor, Backoff backoff, PluginCallback callback,
int maxLatency, int maxIdleTime, int connectionTimeout) { int maxLatency, int maxIdleTime) {
super(ioExecutor, backoff, callback, maxLatency, maxIdleTime, super(ioExecutor, backoff, callback, maxLatency, maxIdleTime);
connectionTimeout);
} }
@Override @Override
@@ -87,82 +63,38 @@ class LanTcpPlugin extends TcpPlugin {
return ID; return ID;
} }
@Override
public void start() {
if (used.getAndSet(true)) throw new IllegalStateException();
initialisePortProperty();
running = true;
bind();
}
protected void initialisePortProperty() {
TransportProperties p = callback.getLocalProperties();
if (isNullOrEmpty(p.get(PROP_PORT))) {
int port = new Random().nextInt(32768) + 32768;
p.put(PROP_PORT, String.valueOf(port));
callback.mergeLocalProperties(p);
}
}
@Override @Override
protected List<InetSocketAddress> getLocalSocketAddresses() { protected List<InetSocketAddress> getLocalSocketAddresses() {
// Use the same address and port as last time if available
TransportProperties p = callback.getLocalProperties(); TransportProperties p = callback.getLocalProperties();
int preferredPort = parsePortProperty(p.get(PROP_PORT));
String oldIpPorts = p.get(PROP_IP_PORTS); String oldIpPorts = p.get(PROP_IP_PORTS);
List<InetSocketAddress> olds = parseSocketAddresses(oldIpPorts); List<InetSocketAddress> olds = parseSocketAddresses(oldIpPorts);
List<InetSocketAddress> locals = new ArrayList<>(); List<InetSocketAddress> locals = new ArrayList<>();
List<InetSocketAddress> fallbacks = new ArrayList<>(); for (InetAddress local : getLocalIpAddresses()) {
for (InetAddress local : getUsableLocalInetAddresses()) { if (isAcceptableAddress(local)) {
// If we've used this address before, try to use the same port // If this is the old address, try to use the same port
int port = preferredPort; for (InetSocketAddress old : olds) {
for (InetSocketAddress old : olds) { if (old.getAddress().equals(local))
if (old.getAddress().equals(local)) { locals.add(new InetSocketAddress(local, old.getPort()));
port = old.getPort();
break;
} }
locals.add(new InetSocketAddress(local, 0));
} }
locals.add(new InetSocketAddress(local, port));
// Fall back to any available port
fallbacks.add(new InetSocketAddress(local, 0));
} }
locals.addAll(fallbacks); sort(locals, ADDRESS_COMPARATOR);
return locals; return locals;
} }
private int parsePortProperty(@Nullable String portProperty) {
if (isNullOrEmpty(portProperty)) return 0;
try {
return parseInt(portProperty);
} catch (NumberFormatException e) {
return 0;
}
}
private List<InetSocketAddress> parseSocketAddresses(String ipPorts) { private List<InetSocketAddress> parseSocketAddresses(String ipPorts) {
if (isNullOrEmpty(ipPorts)) return emptyList();
String[] split = ipPorts.split(SEPARATOR);
List<InetSocketAddress> addresses = new ArrayList<>(); List<InetSocketAddress> addresses = new ArrayList<>();
if (isNullOrEmpty(ipPorts)) return addresses; for (String ipPort : split) {
for (String ipPort : ipPorts.split(SEPARATOR)) {
InetSocketAddress a = parseSocketAddress(ipPort); InetSocketAddress a = parseSocketAddress(ipPort);
if (a != null) addresses.add(a); if (a != null) addresses.add(a);
} }
return addresses; return addresses;
} }
protected List<InetAddress> getUsableLocalInetAddresses() {
List<InterfaceAddress> ifAddrs =
new ArrayList<>(getLocalInterfaceAddresses());
// Prefer longer network prefixes
sort(ifAddrs, (a, b) ->
b.getNetworkPrefixLength() - a.getNetworkPrefixLength());
List<InetAddress> addrs = new ArrayList<>();
for (InterfaceAddress ifAddr : ifAddrs) {
InetAddress addr = ifAddr.getAddress();
if (isAcceptableAddress(addr)) addrs.add(addr);
}
return addrs;
}
@Override @Override
protected void setLocalSocketAddress(InetSocketAddress a) { protected void setLocalSocketAddress(InetSocketAddress a) {
String ipPort = getIpPortString(a); String ipPort = getIpPortString(a);
@@ -200,20 +132,7 @@ class LanTcpPlugin extends TcpPlugin {
@Override @Override
protected List<InetSocketAddress> getRemoteSocketAddresses( protected List<InetSocketAddress> getRemoteSocketAddresses(
TransportProperties p) { TransportProperties p) {
String ipPorts = p.get(PROP_IP_PORTS); return parseSocketAddresses(p.get(PROP_IP_PORTS));
List<InetSocketAddress> remotes = parseSocketAddresses(ipPorts);
int port = parsePortProperty(p.get(PROP_PORT));
// If the contact has a preferred port, we can guess their IP:port when
// they're providing a wifi access point
if (port != 0) {
InetSocketAddress wifiAp =
new InetSocketAddress(WIFI_AP_ADDRESS, port);
if (!remotes.contains(wifiAp)) remotes.add(wifiAp);
InetSocketAddress wifiDirectAp =
new InetSocketAddress(WIFI_DIRECT_AP_ADDRESS, port);
if (!remotes.contains(wifiDirectAp)) remotes.add(wifiDirectAp);
}
return remotes;
} }
private boolean isAcceptableAddress(InetAddress a) { private boolean isAcceptableAddress(InetAddress a) {
@@ -226,33 +145,52 @@ class LanTcpPlugin extends TcpPlugin {
} }
@Override @Override
protected boolean isConnectable(InterfaceAddress local, protected boolean isConnectable(InetSocketAddress remote) {
InetSocketAddress remote) {
if (remote.getPort() == 0) return false; if (remote.getPort() == 0) return false;
if (!isAcceptableAddress(remote.getAddress())) return false; if (!isAcceptableAddress(remote.getAddress())) return false;
// Try to determine whether the address is on the same LAN as us // Try to determine whether the address is on the same LAN as us
byte[] localIp = local.getAddress().getAddress(); if (socket == null) return false;
byte[] localIp = socket.getInetAddress().getAddress();
byte[] remoteIp = remote.getAddress().getAddress(); byte[] remoteIp = remote.getAddress().getAddress();
int prefixLength = local.getNetworkPrefixLength(); return addressesAreOnSameLan(localIp, remoteIp);
return areAddressesInSameNetwork(localIp, remoteIp, prefixLength);
} }
// Package access for testing // Package access for testing
static boolean areAddressesInSameNetwork(byte[] localIp, byte[] remoteIp, boolean addressesAreOnSameLan(byte[] localIp, byte[] remoteIp) {
int prefixLength) { // 10.0.0.0/8
if (localIp.length != remoteIp.length) return false; if (isPrefix10(localIp)) return isPrefix10(remoteIp);
// Compare the first prefixLength bits of the addresses // 172.16.0.0/12
for (int i = 0; i < prefixLength; i++) { if (isPrefix172(localIp)) return isPrefix172(remoteIp);
int byteIndex = i >> 3; // 192.168.0.0/16
int bitIndex = i & 7; // 0 to 7 if (isPrefix192(localIp)) return isPrefix192(remoteIp);
int mask = 128 >> bitIndex; // Select the bit at bitIndex // Unrecognised prefix - may be compatible
if ((localIp[byteIndex] & mask) != (remoteIp[byteIndex] & mask)) {
return false; // Addresses differ at bit i
}
}
return true; return true;
} }
private static boolean isPrefix10(byte[] ipv4) {
return ipv4[0] == 10;
}
private static boolean isPrefix172(byte[] ipv4) {
return ipv4[0] == (byte) 172 && (ipv4[1] & 0xF0) == 16;
}
private static boolean isPrefix192(byte[] ipv4) {
return ipv4[0] == (byte) 192 && ipv4[1] == (byte) 168;
}
// Returns the prefix length for an RFC 1918 address, or 0 for any other
// address
private static int getRfc1918PrefixLength(InetAddress addr) {
if (!(addr instanceof Inet4Address)) return 0;
if (!addr.isSiteLocalAddress()) return 0;
byte[] ipv4 = addr.getAddress();
if (isPrefix10(ipv4)) return 8;
if (isPrefix172(ipv4)) return 12;
if (isPrefix192(ipv4)) return 16;
return 0;
}
@Override @Override
public boolean supportsKeyAgreement() { public boolean supportsKeyAgreement() {
return true; return true;
@@ -291,12 +229,6 @@ class LanTcpPlugin extends TcpPlugin {
public DuplexTransportConnection createKeyAgreementConnection( public DuplexTransportConnection createKeyAgreementConnection(
byte[] commitment, BdfList descriptor) { byte[] commitment, BdfList descriptor) {
if (!isRunning()) return null; if (!isRunning()) return null;
ServerSocket ss = socket;
InterfaceAddress local = getLocalInterfaceAddress(ss.getInetAddress());
if (local == null) {
LOG.warning("No interface for key agreement server socket");
return null;
}
InetSocketAddress remote; InetSocketAddress remote;
try { try {
remote = parseSocketAddress(descriptor); remote = parseSocketAddress(descriptor);
@@ -304,11 +236,12 @@ class LanTcpPlugin extends TcpPlugin {
LOG.info("Invalid IP/port in key agreement descriptor"); LOG.info("Invalid IP/port in key agreement descriptor");
return null; return null;
} }
if (!isConnectable(local, remote)) { if (!isConnectable(remote)) {
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO)) {
SocketAddress local = socket.getLocalSocketAddress();
LOG.info(scrubSocketAddress(remote) + LOG.info(scrubSocketAddress(remote) +
" is not connectable from " + " is not connectable from " +
scrubSocketAddress(ss.getLocalSocketAddress())); scrubSocketAddress(local));
} }
return null; return null;
} }
@@ -316,8 +249,8 @@ class LanTcpPlugin extends TcpPlugin {
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Connecting to " + scrubSocketAddress(remote)); LOG.info("Connecting to " + scrubSocketAddress(remote));
Socket s = createSocket(); Socket s = createSocket();
s.bind(new InetSocketAddress(ss.getInetAddress(), 0)); s.bind(new InetSocketAddress(socket.getInetAddress(), 0));
s.connect(remote, connectionTimeout); s.connect(remote);
s.setSoTimeout(socketTimeout); s.setSoTimeout(socketTimeout);
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Connected to " + scrubSocketAddress(remote)); LOG.info("Connected to " + scrubSocketAddress(remote));
@@ -366,4 +299,19 @@ class LanTcpPlugin extends TcpPlugin {
IoUtils.tryToClose(ss, LOG, WARNING); IoUtils.tryToClose(ss, LOG, WARNING);
} }
} }
static class LanAddressComparator implements Comparator<InetSocketAddress> {
@Override
public int compare(InetSocketAddress a, InetSocketAddress b) {
// Prefer addresses with non-zero ports
int aPort = a.getPort(), bPort = b.getPort();
if (aPort > 0 && bPort == 0) return -1;
if (aPort == 0 && bPort > 0) return 1;
// Prefer addresses with longer RFC 1918 prefixes
int aPrefix = getRfc1918PrefixLength(a.getAddress());
int bPrefix = getRfc1918PrefixLength(b.getAddress());
return bPrefix - aPrefix;
}
}
} }

View File

@@ -18,11 +18,10 @@ import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID;
@NotNullByDefault @NotNullByDefault
public class LanTcpPluginFactory implements DuplexPluginFactory { public class LanTcpPluginFactory implements DuplexPluginFactory {
private static final int MAX_LATENCY = 30_000; // 30 seconds private static final int MAX_LATENCY = 30 * 1000; // 30 seconds
private static final int MAX_IDLE_TIME = 30_000; // 30 seconds private static final int MAX_IDLE_TIME = 30 * 1000; // 30 seconds
private static final int CONNECTION_TIMEOUT = 3_000; // 3 seconds private static final int MIN_POLLING_INTERVAL = 60 * 1000; // 1 minute
private static final int MIN_POLLING_INTERVAL = 60_000; // 1 minute private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 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; private final Executor ioExecutor;
@@ -49,6 +48,6 @@ public class LanTcpPluginFactory 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);
return new LanTcpPlugin(ioExecutor, backoff, callback, MAX_LATENCY, return new LanTcpPlugin(ioExecutor, backoff, callback, MAX_LATENCY,
MAX_IDLE_TIME, CONNECTION_TIMEOUT); MAX_IDLE_TIME);
} }
} }

View File

@@ -19,10 +19,10 @@ import org.briarproject.bramble.util.IoUtils;
import java.io.IOException; import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface; import java.net.NetworkInterface;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException; import java.net.SocketException;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.ArrayList; import java.util.ArrayList;
@@ -36,6 +36,7 @@ import java.util.regex.Pattern;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static java.net.NetworkInterface.getNetworkInterfaces;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static java.util.Collections.list; import static java.util.Collections.list;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
@@ -57,8 +58,7 @@ abstract class TcpPlugin implements DuplexPlugin {
protected final Executor ioExecutor, bindExecutor; protected final Executor ioExecutor, bindExecutor;
protected final Backoff backoff; protected final Backoff backoff;
protected final PluginCallback callback; protected final PluginCallback callback;
protected final int maxLatency, maxIdleTime; protected final int maxLatency, maxIdleTime, socketTimeout;
protected final int connectionTimeout, socketTimeout;
protected final AtomicBoolean used = new AtomicBoolean(false); protected final AtomicBoolean used = new AtomicBoolean(false);
protected volatile boolean running = false; protected volatile boolean running = false;
@@ -86,18 +86,15 @@ abstract class TcpPlugin implements DuplexPlugin {
/** /**
* Returns true if connections to the given address can be attempted. * Returns true if connections to the given address can be attempted.
*/ */
@SuppressWarnings("BooleanMethodIsAlwaysInverted") protected abstract boolean isConnectable(InetSocketAddress remote);
protected abstract boolean isConnectable(InterfaceAddress local,
InetSocketAddress remote);
TcpPlugin(Executor ioExecutor, Backoff backoff, PluginCallback callback, TcpPlugin(Executor ioExecutor, Backoff backoff, PluginCallback callback,
int maxLatency, int maxIdleTime, int connectionTimeout) { int maxLatency, int maxIdleTime) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.backoff = backoff; this.backoff = backoff;
this.callback = callback; this.callback = callback;
this.maxLatency = maxLatency; this.maxLatency = maxLatency;
this.maxIdleTime = maxIdleTime; this.maxIdleTime = maxIdleTime;
this.connectionTimeout = connectionTimeout;
if (maxIdleTime > Integer.MAX_VALUE / 2) if (maxIdleTime > Integer.MAX_VALUE / 2)
socketTimeout = Integer.MAX_VALUE; socketTimeout = Integer.MAX_VALUE;
else socketTimeout = maxIdleTime * 2; else socketTimeout = maxIdleTime * 2;
@@ -233,23 +230,13 @@ abstract class TcpPlugin implements DuplexPlugin {
@Override @Override
public DuplexTransportConnection createConnection(TransportProperties p) { public DuplexTransportConnection createConnection(TransportProperties p) {
if (!isRunning()) return null; if (!isRunning()) return null;
ServerSocket ss = socket;
InterfaceAddress local = getLocalInterfaceAddress(ss.getInetAddress());
if (local == null) {
LOG.warning("No interface for server socket");
return null;
}
for (InetSocketAddress remote : getRemoteSocketAddresses(p)) { for (InetSocketAddress remote : getRemoteSocketAddresses(p)) {
// Don't try to connect to our own address if (!isConnectable(remote)) {
if (!canConnectToOwnAddress() &&
remote.getAddress().equals(ss.getInetAddress())) {
continue;
}
if (!isConnectable(local, remote)) {
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO)) {
SocketAddress local = socket.getLocalSocketAddress();
LOG.info(scrubSocketAddress(remote) + LOG.info(scrubSocketAddress(remote) +
" is not connectable from " + " is not connectable from " +
scrubSocketAddress(ss.getLocalSocketAddress())); scrubSocketAddress(local));
} }
continue; continue;
} }
@@ -257,8 +244,8 @@ abstract class TcpPlugin implements DuplexPlugin {
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Connecting to " + scrubSocketAddress(remote)); LOG.info("Connecting to " + scrubSocketAddress(remote));
Socket s = createSocket(); Socket s = createSocket();
s.bind(new InetSocketAddress(ss.getInetAddress(), 0)); s.bind(new InetSocketAddress(socket.getInetAddress(), 0));
s.connect(remote, connectionTimeout); s.connect(remote);
s.setSoTimeout(socketTimeout); s.setSoTimeout(socketTimeout);
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Connected to " + scrubSocketAddress(remote)); LOG.info("Connected to " + scrubSocketAddress(remote));
@@ -272,19 +259,6 @@ abstract class TcpPlugin implements DuplexPlugin {
return null; return null;
} }
@Nullable
InterfaceAddress getLocalInterfaceAddress(InetAddress a) {
for (InterfaceAddress ifAddr : getLocalInterfaceAddresses()) {
if (ifAddr.getAddress().equals(a)) return ifAddr;
}
return null;
}
// Override for testing
protected boolean canConnectToOwnAddress() {
return false;
}
protected Socket createSocket() throws IOException { protected Socket createSocket() throws IOException {
return new Socket(); return new Socket();
} }
@@ -340,27 +314,14 @@ abstract class TcpPlugin implements DuplexPlugin {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
List<InterfaceAddress> getLocalInterfaceAddresses() { Collection<InetAddress> getLocalIpAddresses() {
List<InterfaceAddress> addrs = new ArrayList<>();
for (NetworkInterface iface : getNetworkInterfaces()) {
addrs.addAll(iface.getInterfaceAddresses());
}
return addrs;
}
List<InetAddress> getLocalInetAddresses() {
List<InetAddress> addrs = new ArrayList<>();
for (NetworkInterface iface : getNetworkInterfaces()) {
addrs.addAll(list(iface.getInetAddresses()));
}
return addrs;
}
private List<NetworkInterface> getNetworkInterfaces() {
try { try {
Enumeration<NetworkInterface> ifaces = Enumeration<NetworkInterface> ifaces = getNetworkInterfaces();
NetworkInterface.getNetworkInterfaces(); if (ifaces == null) return emptyList();
return ifaces == null ? emptyList() : list(ifaces); List<InetAddress> addrs = new ArrayList<>();
for (NetworkInterface iface : list(ifaces))
addrs.addAll(list(iface.getInetAddresses()));
return addrs;
} catch (SocketException e) { } catch (SocketException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
return emptyList(); return emptyList();

View File

@@ -10,7 +10,6 @@ import org.briarproject.bramble.api.properties.TransportProperties;
import java.net.Inet4Address; import java.net.Inet4Address;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.InterfaceAddress;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@@ -30,10 +29,8 @@ class WanTcpPlugin extends TcpPlugin {
private volatile MappingResult mappingResult; private volatile MappingResult mappingResult;
WanTcpPlugin(Executor ioExecutor, Backoff backoff, PortMapper portMapper, WanTcpPlugin(Executor ioExecutor, Backoff backoff, PortMapper portMapper,
PluginCallback callback, int maxLatency, int maxIdleTime, PluginCallback callback, int maxLatency, int maxIdleTime) {
int connectionTimeout) { super(ioExecutor, backoff, callback, maxLatency, maxIdleTime);
super(ioExecutor, backoff, callback, maxLatency, maxIdleTime,
connectionTimeout);
this.portMapper = portMapper; this.portMapper = portMapper;
} }
@@ -48,7 +45,7 @@ class WanTcpPlugin extends TcpPlugin {
TransportProperties p = callback.getLocalProperties(); TransportProperties p = callback.getLocalProperties();
InetSocketAddress old = parseSocketAddress(p.get(PROP_IP_PORT)); InetSocketAddress old = parseSocketAddress(p.get(PROP_IP_PORT));
List<InetSocketAddress> addrs = new LinkedList<>(); List<InetSocketAddress> addrs = new LinkedList<>();
for (InetAddress a : getLocalInetAddresses()) { for (InetAddress a : getLocalIpAddresses()) {
if (isAcceptableAddress(a)) { if (isAcceptableAddress(a)) {
// If this is the old address, try to use the same port // If this is the old address, try to use the same port
if (old != null && old.getAddress().equals(a)) if (old != null && old.getAddress().equals(a))
@@ -89,8 +86,7 @@ class WanTcpPlugin extends TcpPlugin {
} }
@Override @Override
protected boolean isConnectable(InterfaceAddress local, protected boolean isConnectable(InetSocketAddress remote) {
InetSocketAddress remote) {
if (remote.getPort() == 0) return false; if (remote.getPort() == 0) return false;
return isAcceptableAddress(remote.getAddress()); return isAcceptableAddress(remote.getAddress());
} }

View File

@@ -19,11 +19,10 @@ import static org.briarproject.bramble.api.plugin.WanTcpConstants.ID;
@NotNullByDefault @NotNullByDefault
public class WanTcpPluginFactory implements DuplexPluginFactory { public class WanTcpPluginFactory implements DuplexPluginFactory {
private static final int MAX_LATENCY = 30_000; // 30 seconds private static final int MAX_LATENCY = 30 * 1000; // 30 seconds
private static final int MAX_IDLE_TIME = 30_000; // 30 seconds private static final int MAX_IDLE_TIME = 30 * 1000; // 30 seconds
private static final int CONNECTION_TIMEOUT = 30_000; // 30 seconds private static final int MIN_POLLING_INTERVAL = 60 * 1000; // 1 minute
private static final int MIN_POLLING_INTERVAL = 60_000; // 1 minute private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 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; private final Executor ioExecutor;
@@ -53,6 +52,6 @@ public class WanTcpPluginFactory implements DuplexPluginFactory {
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);
return new WanTcpPlugin(ioExecutor, backoff, return new WanTcpPlugin(ioExecutor, backoff,
new PortMapperImpl(shutdownManager), callback, MAX_LATENCY, new PortMapperImpl(shutdownManager), callback, MAX_LATENCY,
MAX_IDLE_TIME, CONNECTION_TIMEOUT); MAX_IDLE_TIME);
} }
} }

View File

@@ -195,6 +195,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
if (!assetsAreUpToDate()) installAssets(); if (!assetsAreUpToDate()) installAssets();
if (cookieFile.exists() && !cookieFile.delete()) if (cookieFile.exists() && !cookieFile.delete())
LOG.warning("Old auth cookie not deleted"); LOG.warning("Old auth cookie not deleted");
// Migrate old settings before having a chance to stop
migrateSettings();
// Start a new Tor process // Start a new Tor process
LOG.info("Starting Tor"); LOG.info("Starting Tor");
String torPath = torFile.getAbsolutePath(); String torPath = torFile.getAbsolutePath();
@@ -814,6 +816,21 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
controlConnection.setConf("ConnectionPadding", enable ? "1" : "0"); controlConnection.setConf("ConnectionPadding", enable ? "1" : "0");
} }
// TODO remove when sufficient time has passed. Added 2018-08-15
private void migrateSettings() {
Settings sOld = callback.getSettings();
int oldNetwork = sOld.getInt("network", -1);
if (oldNetwork == -1) return;
Settings s = new Settings();
if (oldNetwork == 0) {
s.putInt(PREF_TOR_NETWORK, PREF_TOR_NETWORK_NEVER);
} else if (oldNetwork == 1) {
s.putBoolean(PREF_TOR_MOBILE, false);
}
s.putInt("network", -1);
callback.mergeSettings(s);
}
private static class ConnectionStatus { private static class ConnectionStatus {
// All of the following are locking: this // All of the following are locking: this

View File

@@ -37,11 +37,6 @@ import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.GROUP_KEY_DISCOVERED;
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MSG_KEY_LOCAL;
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MSG_KEY_TRANSPORT_ID;
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MSG_KEY_VERSION;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
class TransportPropertyManagerImpl implements TransportPropertyManager, class TransportPropertyManagerImpl implements TransportPropertyManager,
@@ -116,10 +111,10 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
try { try {
// Find the latest update for this transport, if any // Find the latest update for this transport, if any
BdfDictionary d = metadataParser.parse(meta); BdfDictionary d = metadataParser.parse(meta);
TransportId t = new TransportId(d.getString(MSG_KEY_TRANSPORT_ID)); TransportId t = new TransportId(d.getString("transportId"));
LatestUpdate latest = findLatest(txn, m.getGroupId(), t, false); LatestUpdate latest = findLatest(txn, m.getGroupId(), t, false);
if (latest != null) { if (latest != null) {
if (d.getLong(MSG_KEY_VERSION) > latest.version) { if (d.getLong("version") > latest.version) {
// This update is newer - delete the previous update // This update is newer - delete the previous update
db.deleteMessage(txn, latest.messageId); db.deleteMessage(txn, latest.messageId);
db.deleteMessageMetadata(txn, latest.messageId); db.deleteMessageMetadata(txn, latest.messageId);
@@ -145,27 +140,6 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
} }
} }
@Override
public void addRemotePropertiesFromConnection(ContactId c, TransportId t,
TransportProperties props) throws DbException {
if (props.isEmpty()) return;
try {
db.transaction(false, txn -> {
Group g = getContactGroup(db.getContact(txn, c));
BdfDictionary meta = clientHelper.getGroupMetadataAsDictionary(
txn, g.getId());
BdfDictionary discovered =
meta.getOptionalDictionary(GROUP_KEY_DISCOVERED);
if (discovered == null) discovered = new BdfDictionary();
discovered.putAll(props);
meta.put(GROUP_KEY_DISCOVERED, discovered);
clientHelper.mergeGroupMetadata(txn, g.getId(), meta);
});
} catch (FormatException e) {
throw new DbException(e);
}
}
@Override @Override
public Map<TransportId, TransportProperties> getLocalProperties() public Map<TransportId, TransportProperties> getLocalProperties()
throws DbException { throws DbException {
@@ -229,26 +203,12 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
Group g = getContactGroup(c); Group g = getContactGroup(c);
try { try {
// Find the latest remote update // Find the latest remote update
TransportProperties remote;
LatestUpdate latest = findLatest(txn, g.getId(), t, false); LatestUpdate latest = findLatest(txn, g.getId(), t, false);
if (latest == null) { if (latest == null) return new TransportProperties();
remote = new TransportProperties(); // Retrieve and parse the latest remote properties
} else { BdfList message =
// Retrieve and parse the latest remote properties clientHelper.getMessageAsList(txn, latest.messageId);
BdfList message = return parseProperties(message);
clientHelper.getMessageAsList(txn, latest.messageId);
remote = parseProperties(message);
}
// Merge in any discovered properties
BdfDictionary meta =
clientHelper.getGroupMetadataAsDictionary(txn, g.getId());
BdfDictionary d = meta.getOptionalDictionary(GROUP_KEY_DISCOVERED);
if (d == null) return remote;
TransportProperties merged =
clientHelper.parseAndValidateTransportProperties(d);
// Received properties override discovered properties
merged.putAll(remote);
return merged;
} catch (FormatException e) { } catch (FormatException e) {
throw new DbException(e); throw new DbException(e);
} }
@@ -321,9 +281,9 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
long now = clock.currentTimeMillis(); long now = clock.currentTimeMillis();
Message m = clientHelper.createMessage(g, now, body); Message m = clientHelper.createMessage(g, now, body);
BdfDictionary meta = new BdfDictionary(); BdfDictionary meta = new BdfDictionary();
meta.put(MSG_KEY_TRANSPORT_ID, t.getString()); meta.put("transportId", t.getString());
meta.put(MSG_KEY_VERSION, version); meta.put("version", version);
meta.put(MSG_KEY_LOCAL, local); meta.put("local", local);
clientHelper.addLocalMessage(txn, m, meta, shared, false); clientHelper.addLocalMessage(txn, m, meta, shared, false);
} catch (FormatException e) { } catch (FormatException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
@@ -342,9 +302,8 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
.getMessageMetadataAsDictionary(txn, localGroup.getId()); .getMessageMetadataAsDictionary(txn, localGroup.getId());
for (Entry<MessageId, BdfDictionary> e : metadata.entrySet()) { for (Entry<MessageId, BdfDictionary> e : metadata.entrySet()) {
BdfDictionary meta = e.getValue(); BdfDictionary meta = e.getValue();
TransportId t = TransportId t = new TransportId(meta.getString("transportId"));
new TransportId(meta.getString(MSG_KEY_TRANSPORT_ID)); long version = meta.getLong("version");
long version = meta.getLong(MSG_KEY_VERSION);
latestUpdates.put(t, new LatestUpdate(e.getKey(), version)); latestUpdates.put(t, new LatestUpdate(e.getKey(), version));
} }
return latestUpdates; return latestUpdates;
@@ -357,10 +316,9 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
clientHelper.getMessageMetadataAsDictionary(txn, g); clientHelper.getMessageMetadataAsDictionary(txn, g);
for (Entry<MessageId, BdfDictionary> e : metadata.entrySet()) { for (Entry<MessageId, BdfDictionary> e : metadata.entrySet()) {
BdfDictionary meta = e.getValue(); BdfDictionary meta = e.getValue();
if (meta.getString(MSG_KEY_TRANSPORT_ID).equals(t.getString()) if (meta.getString("transportId").equals(t.getString())
&& meta.getBoolean(MSG_KEY_LOCAL) == local) { && meta.getBoolean("local") == local) {
return new LatestUpdate(e.getKey(), return new LatestUpdate(e.getKey(), meta.getLong("version"));
meta.getLong(MSG_KEY_VERSION));
} }
} }
return null; return null;

View File

@@ -11,7 +11,6 @@ import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.lifecycle.event.LifecycleEvent; import org.briarproject.bramble.api.lifecycle.event.LifecycleEvent;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.sync.Ack; import org.briarproject.bramble.api.sync.Ack;
import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.Offer; import org.briarproject.bramble.api.sync.Offer;
@@ -19,7 +18,6 @@ import org.briarproject.bramble.api.sync.Request;
import org.briarproject.bramble.api.sync.SyncRecordWriter; import org.briarproject.bramble.api.sync.SyncRecordWriter;
import org.briarproject.bramble.api.sync.SyncSession; import org.briarproject.bramble.api.sync.SyncSession;
import org.briarproject.bramble.api.sync.Versions; import org.briarproject.bramble.api.sync.Versions;
import org.briarproject.bramble.api.sync.event.CloseSyncConnectionsEvent;
import org.briarproject.bramble.api.sync.event.GroupVisibilityUpdatedEvent; import org.briarproject.bramble.api.sync.event.GroupVisibilityUpdatedEvent;
import org.briarproject.bramble.api.sync.event.MessageRequestedEvent; import org.briarproject.bramble.api.sync.event.MessageRequestedEvent;
import org.briarproject.bramble.api.sync.event.MessageSharedEvent; import org.briarproject.bramble.api.sync.event.MessageSharedEvent;
@@ -73,7 +71,6 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
private final EventBus eventBus; private final EventBus eventBus;
private final Clock clock; private final Clock clock;
private final ContactId contactId; private final ContactId contactId;
private final TransportId transportId;
private final int maxLatency, maxIdleTime; private final int maxLatency, maxIdleTime;
private final StreamWriter streamWriter; private final StreamWriter streamWriter;
private final SyncRecordWriter recordWriter; private final SyncRecordWriter recordWriter;
@@ -89,15 +86,14 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
private volatile boolean interrupted = false; private volatile boolean interrupted = false;
DuplexOutgoingSession(DatabaseComponent db, Executor dbExecutor, DuplexOutgoingSession(DatabaseComponent db, Executor dbExecutor,
EventBus eventBus, Clock clock, ContactId contactId, EventBus eventBus, Clock clock, ContactId contactId, int maxLatency,
TransportId transportId, int maxLatency, int maxIdleTime, int maxIdleTime, StreamWriter streamWriter,
StreamWriter streamWriter, SyncRecordWriter recordWriter) { SyncRecordWriter recordWriter) {
this.db = db; this.db = db;
this.dbExecutor = dbExecutor; this.dbExecutor = dbExecutor;
this.eventBus = eventBus; this.eventBus = eventBus;
this.clock = clock; this.clock = clock;
this.contactId = contactId; this.contactId = contactId;
this.transportId = transportId;
this.maxLatency = maxLatency; this.maxLatency = maxLatency;
this.maxIdleTime = maxIdleTime; this.maxIdleTime = maxIdleTime;
this.streamWriter = streamWriter; this.streamWriter = streamWriter;
@@ -227,9 +223,6 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
} else if (e instanceof LifecycleEvent) { } else if (e instanceof LifecycleEvent) {
LifecycleEvent l = (LifecycleEvent) e; LifecycleEvent l = (LifecycleEvent) e;
if (l.getLifecycleState() == STOPPING) interrupt(); if (l.getLifecycleState() == STOPPING) interrupt();
} else if (e instanceof CloseSyncConnectionsEvent) {
CloseSyncConnectionsEvent c = (CloseSyncConnectionsEvent) e;
if (c.getTransportId().equals(transportId)) interrupt();
} }
} }

View File

@@ -11,13 +11,11 @@ import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.lifecycle.event.LifecycleEvent; import org.briarproject.bramble.api.lifecycle.event.LifecycleEvent;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.sync.Ack; import org.briarproject.bramble.api.sync.Ack;
import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.SyncRecordWriter; import org.briarproject.bramble.api.sync.SyncRecordWriter;
import org.briarproject.bramble.api.sync.SyncSession; import org.briarproject.bramble.api.sync.SyncSession;
import org.briarproject.bramble.api.sync.Versions; import org.briarproject.bramble.api.sync.Versions;
import org.briarproject.bramble.api.sync.event.CloseSyncConnectionsEvent;
import org.briarproject.bramble.api.transport.StreamWriter; import org.briarproject.bramble.api.transport.StreamWriter;
import java.io.IOException; import java.io.IOException;
@@ -58,7 +56,6 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
private final Executor dbExecutor; private final Executor dbExecutor;
private final EventBus eventBus; private final EventBus eventBus;
private final ContactId contactId; private final ContactId contactId;
private final TransportId transportId;
private final int maxLatency; private final int maxLatency;
private final StreamWriter streamWriter; private final StreamWriter streamWriter;
private final SyncRecordWriter recordWriter; private final SyncRecordWriter recordWriter;
@@ -68,14 +65,12 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
private volatile boolean interrupted = false; private volatile boolean interrupted = false;
SimplexOutgoingSession(DatabaseComponent db, Executor dbExecutor, SimplexOutgoingSession(DatabaseComponent db, Executor dbExecutor,
EventBus eventBus, ContactId contactId, TransportId transportId, EventBus eventBus, ContactId contactId, int maxLatency,
int maxLatency, StreamWriter streamWriter, StreamWriter streamWriter, SyncRecordWriter recordWriter) {
SyncRecordWriter recordWriter) {
this.db = db; this.db = db;
this.dbExecutor = dbExecutor; this.dbExecutor = dbExecutor;
this.eventBus = eventBus; this.eventBus = eventBus;
this.contactId = contactId; this.contactId = contactId;
this.transportId = transportId;
this.maxLatency = maxLatency; this.maxLatency = maxLatency;
this.streamWriter = streamWriter; this.streamWriter = streamWriter;
this.recordWriter = recordWriter; this.recordWriter = recordWriter;
@@ -128,9 +123,6 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
} else if (e instanceof LifecycleEvent) { } else if (e instanceof LifecycleEvent) {
LifecycleEvent l = (LifecycleEvent) e; LifecycleEvent l = (LifecycleEvent) e;
if (l.getLifecycleState() == STOPPING) interrupt(); if (l.getLifecycleState() == STOPPING) interrupt();
} else if (e instanceof CloseSyncConnectionsEvent) {
CloseSyncConnectionsEvent c = (CloseSyncConnectionsEvent) e;
if (c.getTransportId().equals(transportId)) interrupt();
} }
} }

View File

@@ -5,7 +5,6 @@ import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.sync.SyncRecordReader; import org.briarproject.bramble.api.sync.SyncRecordReader;
import org.briarproject.bramble.api.sync.SyncRecordReaderFactory; import org.briarproject.bramble.api.sync.SyncRecordReaderFactory;
import org.briarproject.bramble.api.sync.SyncRecordWriter; import org.briarproject.bramble.api.sync.SyncRecordWriter;
@@ -54,23 +53,22 @@ class SyncSessionFactoryImpl implements SyncSessionFactory {
} }
@Override @Override
public SyncSession createSimplexOutgoingSession(ContactId c, TransportId t, public SyncSession createSimplexOutgoingSession(ContactId c,
int maxLatency, StreamWriter streamWriter) { int maxLatency, StreamWriter streamWriter) {
OutputStream out = streamWriter.getOutputStream(); OutputStream out = streamWriter.getOutputStream();
SyncRecordWriter recordWriter = SyncRecordWriter recordWriter =
recordWriterFactory.createRecordWriter(out); recordWriterFactory.createRecordWriter(out);
return new SimplexOutgoingSession(db, dbExecutor, eventBus, c, t, return new SimplexOutgoingSession(db, dbExecutor, eventBus, c,
maxLatency, streamWriter, recordWriter); maxLatency, streamWriter, recordWriter);
} }
@Override @Override
public SyncSession createDuplexOutgoingSession(ContactId c, public SyncSession createDuplexOutgoingSession(ContactId c, int maxLatency,
TransportId t, int maxLatency, int maxIdleTime, int maxIdleTime, StreamWriter streamWriter) {
StreamWriter streamWriter) {
OutputStream out = streamWriter.getOutputStream(); OutputStream out = streamWriter.getOutputStream();
SyncRecordWriter recordWriter = SyncRecordWriter recordWriter =
recordWriterFactory.createRecordWriter(out); recordWriterFactory.createRecordWriter(out);
return new DuplexOutgoingSession(db, dbExecutor, eventBus, clock, c, t, return new DuplexOutgoingSession(db, dbExecutor, eventBus, clock, c,
maxLatency, maxIdleTime, streamWriter, recordWriter); maxLatency, maxIdleTime, streamWriter, recordWriter);
} }
} }

View File

@@ -26,9 +26,7 @@ import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.validation.IncomingMessageHook; import org.briarproject.bramble.api.sync.validation.IncomingMessageHook;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientMajorVersion; import org.briarproject.bramble.api.versioning.ClientMajorVersion;
import org.briarproject.bramble.api.versioning.ClientVersion;
import org.briarproject.bramble.api.versioning.ClientVersioningManager; import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.bramble.api.versioning.event.ClientVersionUpdatedEvent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
@@ -82,9 +80,9 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
@Override @Override
public void registerClient(ClientId clientId, int majorVersion, public void registerClient(ClientId clientId, int majorVersion,
int minorVersion, ClientVersioningHook hook) { int minorVersion, ClientVersioningHook hook) {
ClientMajorVersion cmv = new ClientMajorVersion(clientId, majorVersion); ClientMajorVersion cv = new ClientMajorVersion(clientId, majorVersion);
clients.add(new ClientVersion(cmv, minorVersion)); clients.add(new ClientVersion(cv, minorVersion));
hooks.put(cmv, hook); hooks.put(cv, hook);
} }
@Override @Override
@@ -98,9 +96,9 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
Update remoteUpdate = loadUpdate(txn, latest.remote.messageId); Update remoteUpdate = loadUpdate(txn, latest.remote.messageId);
Map<ClientMajorVersion, Visibility> visibilities = Map<ClientMajorVersion, Visibility> visibilities =
getVisibilities(localUpdate.states, remoteUpdate.states); getVisibilities(localUpdate.states, remoteUpdate.states);
ClientMajorVersion key = ClientMajorVersion cv =
new ClientMajorVersion(clientId, majorVersion); new ClientMajorVersion(clientId, majorVersion);
Visibility v = visibilities.get(key); Visibility v = visibilities.get(cv);
return v == null ? INVISIBLE : v; return v == null ? INVISIBLE : v;
} catch (FormatException e) { } catch (FormatException e) {
throw new DbException(e); throw new DbException(e);
@@ -114,11 +112,10 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
LatestUpdates latest = findLatestUpdates(txn, contactId); LatestUpdates latest = findLatestUpdates(txn, contactId);
if (latest == null || latest.remote == null) return -1; if (latest == null || latest.remote == null) return -1;
Update remoteUpdate = loadUpdate(txn, latest.remote.messageId); Update remoteUpdate = loadUpdate(txn, latest.remote.messageId);
ClientMajorVersion key = ClientMajorVersion cv =
new ClientMajorVersion(clientId, majorVersion); new ClientMajorVersion(clientId, majorVersion);
for (ClientState remote : remoteUpdate.states) { for (ClientState remote : remoteUpdate.states) {
if (remote.clientVersion.getClientMajorVersion().equals(key)) if (remote.majorVersion.equals(cv)) return remote.minorVersion;
return remote.clientVersion.getMinorVersion();
} }
return -1; return -1;
} catch (FormatException e) { } catch (FormatException e) {
@@ -229,21 +226,9 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
Map<ClientMajorVersion, Visibility> after = Map<ClientMajorVersion, Visibility> after =
getVisibilities(newLocalStates, newRemoteStates); getVisibilities(newLocalStates, newRemoteStates);
// Call hooks for any visibilities that have changed // Call hooks for any visibilities that have changed
ContactId c = getContactId(txn, m.getGroupId());
if (!before.equals(after)) { if (!before.equals(after)) {
Contact contact = db.getContact(txn, c); Contact c = getContact(txn, m.getGroupId());
callVisibilityHooks(txn, contact, before, after); callVisibilityHooks(txn, c, before, after);
}
// Broadcast events for any new client versions
Set<ClientVersion> oldRemoteVersions = new HashSet<>();
for (ClientState cs : oldRemoteStates) {
oldRemoteVersions.add(cs.clientVersion);
}
for (ClientState cs : newRemoteStates) {
if (!oldRemoteVersions.contains(cs.clientVersion)) {
txn.attach(new ClientVersionUpdatedEvent(c,
cs.clientVersion));
}
} }
} catch (FormatException e) { } catch (FormatException e) {
throw new InvalidMessageException(e); throw new InvalidMessageException(e);
@@ -271,8 +256,8 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
} }
private BdfList encodeClientVersion(ClientVersion cv) { private BdfList encodeClientVersion(ClientVersion cv) {
return BdfList.of(cv.getClientId().getString(), cv.getMajorVersion(), return BdfList.of(cv.majorVersion.getClientId().getString(),
cv.getMinorVersion()); cv.majorVersion.getMajorVersion(), cv.minorVersion);
} }
/** /**
@@ -313,7 +298,8 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
ClientId clientId = new ClientId(cv.getString(0)); ClientId clientId = new ClientId(cv.getString(0));
int majorVersion = cv.getLong(1).intValue(); int majorVersion = cv.getLong(1).intValue();
int minorVersion = cv.getLong(2).intValue(); int minorVersion = cv.getLong(2).intValue();
parsed.add(new ClientVersion(clientId, majorVersion, minorVersion)); parsed.add(new ClientVersion(clientId, majorVersion,
minorVersion));
} }
return parsed; return parsed;
} }
@@ -432,15 +418,13 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
private List<ClientState> updateStatesFromLocalVersions( private List<ClientState> updateStatesFromLocalVersions(
List<ClientState> oldStates, List<ClientVersion> newVersions) { List<ClientState> oldStates, List<ClientVersion> newVersions) {
Map<ClientMajorVersion, ClientState> oldMap = new HashMap<>(); Map<ClientMajorVersion, ClientState> oldMap = new HashMap<>();
for (ClientState cs : oldStates) { for (ClientState cs : oldStates) oldMap.put(cs.majorVersion, cs);
oldMap.put(cs.clientVersion.getClientMajorVersion(), cs);
}
List<ClientState> newStates = new ArrayList<>(newVersions.size()); List<ClientState> newStates = new ArrayList<>(newVersions.size());
for (ClientVersion newVersion : newVersions) { for (ClientVersion newVersion : newVersions) {
ClientMajorVersion key = newVersion.getClientMajorVersion(); ClientState oldState = oldMap.get(newVersion.majorVersion);
ClientState oldState = oldMap.get(key);
boolean active = oldState != null && oldState.active; boolean active = oldState != null && oldState.active;
newStates.add(new ClientState(newVersion, active)); newStates.add(new ClientState(newVersion.majorVersion,
newVersion.minorVersion, active));
} }
return newStates; return newStates;
} }
@@ -467,25 +451,21 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
} }
private BdfList encodeClientState(ClientState cs) { private BdfList encodeClientState(ClientState cs) {
ClientVersion cv = cs.clientVersion; return BdfList.of(cs.majorVersion.getClientId().getString(),
return BdfList.of(cv.getClientId().getString(), cv.getMajorVersion(), cs.majorVersion.getMajorVersion(), cs.minorVersion, cs.active);
cv.getMinorVersion(), cs.active);
} }
private Map<ClientMajorVersion, Visibility> getVisibilities( private Map<ClientMajorVersion, Visibility> getVisibilities(
List<ClientState> localStates, List<ClientState> remoteStates) { List<ClientState> localStates, List<ClientState> remoteStates) {
Map<ClientMajorVersion, ClientState> remoteMap = new HashMap<>(); Map<ClientMajorVersion, ClientState> remoteMap = new HashMap<>();
for (ClientState cs : remoteStates) { for (ClientState cs : remoteStates) remoteMap.put(cs.majorVersion, cs);
remoteMap.put(cs.clientVersion.getClientMajorVersion(), cs);
}
Map<ClientMajorVersion, Visibility> visibilities = new HashMap<>(); Map<ClientMajorVersion, Visibility> visibilities = new HashMap<>();
for (ClientState local : localStates) { for (ClientState local : localStates) {
ClientMajorVersion key = ClientState remote = remoteMap.get(local.majorVersion);
local.clientVersion.getClientMajorVersion(); if (remote == null) visibilities.put(local.majorVersion, INVISIBLE);
ClientState remote = remoteMap.get(key); else if (remote.active)
if (remote == null) visibilities.put(key, INVISIBLE); visibilities.put(local.majorVersion, SHARED);
else if (remote.active) visibilities.put(key, SHARED); else visibilities.put(local.majorVersion, VISIBLE);
else visibilities.put(key, VISIBLE);
} }
return visibilities; return visibilities;
} }
@@ -506,9 +486,9 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
} }
} }
private void callVisibilityHook(Transaction txn, ClientMajorVersion cmv, private void callVisibilityHook(Transaction txn, ClientMajorVersion cv,
Contact c, Visibility v) throws DbException { Contact c, Visibility v) throws DbException {
ClientVersioningHook hook = hooks.get(cmv); ClientVersioningHook hook = hooks.get(cv);
if (hook != null) hook.onClientVisibilityChanging(txn, c, v); if (hook != null) hook.onClientVisibilityChanging(txn, c, v);
} }
@@ -516,17 +496,18 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
List<ClientVersion> versions) throws DbException { List<ClientVersion> versions) throws DbException {
List<ClientState> states = new ArrayList<>(versions.size()); List<ClientState> states = new ArrayList<>(versions.size());
for (ClientVersion cv : versions) { for (ClientVersion cv : versions) {
states.add(new ClientState(cv, false)); states.add(new ClientState(cv.majorVersion, cv.minorVersion,
false));
} }
storeUpdate(txn, g, states, 1); storeUpdate(txn, g, states, 1);
} }
private ContactId getContactId(Transaction txn, GroupId g) private Contact getContact(Transaction txn, GroupId g) throws DbException {
throws DbException {
try { try {
BdfDictionary meta = BdfDictionary meta =
clientHelper.getGroupMetadataAsDictionary(txn, g); clientHelper.getGroupMetadataAsDictionary(txn, g);
return new ContactId(meta.getLong(GROUP_KEY_CONTACT_ID).intValue()); int id = meta.getLong(GROUP_KEY_CONTACT_ID).intValue();
return db.getContact(txn, new ContactId(id));
} catch (FormatException e) { } catch (FormatException e) {
throw new DbException(e); throw new DbException(e);
} }
@@ -535,16 +516,13 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
private List<ClientState> updateStatesFromRemoteStates( private List<ClientState> updateStatesFromRemoteStates(
List<ClientState> oldLocalStates, List<ClientState> remoteStates) { List<ClientState> oldLocalStates, List<ClientState> remoteStates) {
Set<ClientMajorVersion> remoteSet = new HashSet<>(); Set<ClientMajorVersion> remoteSet = new HashSet<>();
for (ClientState cs : remoteStates) { for (ClientState cs : remoteStates) remoteSet.add(cs.majorVersion);
remoteSet.add(cs.clientVersion.getClientMajorVersion());
}
List<ClientState> newLocalStates = List<ClientState> newLocalStates =
new ArrayList<>(oldLocalStates.size()); new ArrayList<>(oldLocalStates.size());
for (ClientState oldState : oldLocalStates) { for (ClientState oldState : oldLocalStates) {
ClientMajorVersion cmv = boolean active = remoteSet.contains(oldState.majorVersion);
oldState.clientVersion.getClientMajorVersion(); newLocalStates.add(new ClientState(oldState.majorVersion,
boolean active = remoteSet.contains(cmv); oldState.minorVersion, active));
newLocalStates.add(new ClientState(oldState.clientVersion, active));
} }
return newLocalStates; return newLocalStates;
} }
@@ -583,19 +561,61 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
} }
} }
private static class ClientVersion implements Comparable<ClientVersion> {
private final ClientMajorVersion majorVersion;
private final int minorVersion;
private ClientVersion(ClientMajorVersion majorVersion,
int minorVersion) {
this.majorVersion = majorVersion;
this.minorVersion = minorVersion;
}
private ClientVersion(ClientId clientId, int majorVersion,
int minorVersion) {
this(new ClientMajorVersion(clientId, majorVersion), minorVersion);
}
@Override
public boolean equals(Object o) {
if (o instanceof ClientVersion) {
ClientVersion cv = (ClientVersion) o;
return majorVersion.equals(cv.majorVersion)
&& minorVersion == cv.minorVersion;
}
return false;
}
@Override
public int hashCode() {
return majorVersion.hashCode();
}
@Override
public int compareTo(ClientVersion cv) {
int compare = majorVersion.compareTo(cv.majorVersion);
if (compare != 0) return compare;
return minorVersion - cv.minorVersion;
}
}
private static class ClientState { private static class ClientState {
private final ClientVersion clientVersion; private final ClientMajorVersion majorVersion;
private final int minorVersion;
private final boolean active; private final boolean active;
private ClientState(ClientVersion clientVersion, boolean active) { private ClientState(ClientMajorVersion majorVersion, int minorVersion,
this.clientVersion = clientVersion; boolean active) {
this.majorVersion = majorVersion;
this.minorVersion = minorVersion;
this.active = active; this.active = active;
} }
private ClientState(ClientId clientId, int majorVersion, private ClientState(ClientId clientId, int majorVersion,
int minorVersion, boolean active) { int minorVersion, boolean active) {
this(new ClientVersion(clientId, majorVersion, minorVersion), this(new ClientMajorVersion(clientId, majorVersion), minorVersion,
active); active);
} }
@@ -603,7 +623,8 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
public boolean equals(Object o) { public boolean equals(Object o) {
if (o instanceof ClientState) { if (o instanceof ClientState) {
ClientState cs = (ClientState) o; ClientState cs = (ClientState) o;
return clientVersion.equals(cs.clientVersion) return majorVersion.equals(cs.majorVersion)
&& minorVersion == cs.minorVersion
&& active == cs.active; && active == cs.active;
} }
return false; return false;
@@ -611,7 +632,7 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
@Override @Override
public int hashCode() { public int hashCode() {
return clientVersion.hashCode(); return majorVersion.hashCode();
} }
} }
} }

View File

@@ -1,8 +1,6 @@
package org.briarproject.bramble.account; package org.briarproject.bramble.account;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.DecryptionException;
import org.briarproject.bramble.api.crypto.KeyStrengthener;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseConfig; import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.identity.Identity; import org.briarproject.bramble.api.identity.Identity;
@@ -20,15 +18,12 @@ import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.nio.charset.Charset;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.assertTrue;
import static org.briarproject.bramble.api.crypto.DecryptionResult.INVALID_CIPHERTEXT;
import static org.briarproject.bramble.api.crypto.DecryptionResult.INVALID_PASSWORD;
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory; import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
import static org.briarproject.bramble.test.TestUtils.getIdentity; import static org.briarproject.bramble.test.TestUtils.getIdentity;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes; import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
@@ -39,14 +34,11 @@ import static org.briarproject.bramble.util.StringUtils.toHexString;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
public class AccountManagerImplTest extends BrambleMockTestCase { public class AccountManagerImplTest extends BrambleMockTestCase {
private final DatabaseConfig databaseConfig = private final DatabaseConfig databaseConfig =
context.mock(DatabaseConfig.class); context.mock(DatabaseConfig.class);
private final KeyStrengthener keyStrengthener =
context.mock(KeyStrengthener.class);
private final CryptoComponent crypto = context.mock(CryptoComponent.class); private final CryptoComponent crypto = context.mock(CryptoComponent.class);
private final IdentityManager identityManager = private final IdentityManager identityManager =
context.mock(IdentityManager.class); context.mock(IdentityManager.class);
@@ -76,8 +68,6 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
will(returnValue(dbDir)); will(returnValue(dbDir));
allowing(databaseConfig).getDatabaseKeyDirectory(); allowing(databaseConfig).getDatabaseKeyDirectory();
will(returnValue(keyDir)); will(returnValue(keyDir));
allowing(databaseConfig).getKeyStrengthener();
will(returnValue(keyStrengthener));
}}); }});
accountManager = accountManager =
@@ -88,13 +78,8 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
} }
@Test @Test
public void testSignInThrowsExceptionIfDbKeyCannotBeLoaded() { public void testSignInReturnsFalseIfDbKeyCannotBeLoaded() {
try { assertFalse(accountManager.signIn(password));
accountManager.signIn(password);
fail();
} catch (DecryptionException expected) {
assertEquals(INVALID_CIPHERTEXT, expected.getDecryptionResult());
}
assertFalse(accountManager.hasDatabaseKey()); assertFalse(accountManager.hasDatabaseKey());
assertFalse(keyFile.exists()); assertFalse(keyFile.exists());
@@ -102,11 +87,10 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
} }
@Test @Test
public void testSignInThrowsExceptionIfPasswordIsWrong() throws Exception { public void testSignInReturnsFalseIfPasswordIsWrong() throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(crypto).decryptWithPassword(encryptedKey, password, oneOf(crypto).decryptWithPassword(encryptedKey, password);
keyStrengthener); will(returnValue(null));
will(throwException(new DecryptionException(INVALID_PASSWORD)));
}}); }});
storeDatabaseKey(keyFile, encryptedKeyHex); storeDatabaseKey(keyFile, encryptedKeyHex);
@@ -115,12 +99,7 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile)); assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile)); assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
try { assertFalse(accountManager.signIn(password));
accountManager.signIn(password);
fail();
} catch (DecryptionException expected) {
assertEquals(INVALID_PASSWORD, expected.getDecryptionResult());
}
assertFalse(accountManager.hasDatabaseKey()); assertFalse(accountManager.hasDatabaseKey());
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile)); assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
@@ -130,11 +109,8 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
@Test @Test
public void testSignInReturnsTrueIfPasswordIsRight() throws Exception { public void testSignInReturnsTrueIfPasswordIsRight() throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(crypto).decryptWithPassword(encryptedKey, password, oneOf(crypto).decryptWithPassword(encryptedKey, password);
keyStrengthener);
will(returnValue(key.getBytes())); will(returnValue(key.getBytes()));
oneOf(crypto).isEncryptedWithStrengthenedKey(encryptedKey);
will(returnValue(true));
}}); }});
storeDatabaseKey(keyFile, encryptedKeyHex); storeDatabaseKey(keyFile, encryptedKeyHex);
@@ -143,7 +119,7 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile)); assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile)); assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
accountManager.signIn(password); assertTrue(accountManager.signIn(password));
assertTrue(accountManager.hasDatabaseKey()); assertTrue(accountManager.hasDatabaseKey());
SecretKey decrypted = accountManager.getDatabaseKey(); SecretKey decrypted = accountManager.getDatabaseKey();
assertNotNull(decrypted); assertNotNull(decrypted);
@@ -153,35 +129,6 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile)); assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
} }
@Test
public void testSignInReEncryptsKey() throws Exception {
context.checking(new Expectations() {{
oneOf(crypto).decryptWithPassword(encryptedKey, password,
keyStrengthener);
will(returnValue(key.getBytes()));
oneOf(crypto).isEncryptedWithStrengthenedKey(encryptedKey);
will(returnValue(false));
oneOf(crypto).encryptWithPassword(key.getBytes(), password,
keyStrengthener);
will(returnValue(newEncryptedKey));
}});
storeDatabaseKey(keyFile, encryptedKeyHex);
storeDatabaseKey(keyBackupFile, encryptedKeyHex);
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
accountManager.signIn(password);
assertTrue(accountManager.hasDatabaseKey());
SecretKey decrypted = accountManager.getDatabaseKey();
assertNotNull(decrypted);
assertArrayEquals(key.getBytes(), decrypted.getBytes());
assertEquals(newEncryptedKeyHex, loadDatabaseKey(keyFile));
assertEquals(newEncryptedKeyHex, loadDatabaseKey(keyBackupFile));
}
@Test @Test
public void testDbKeyIsLoadedFromPrimaryFile() throws Exception { public void testDbKeyIsLoadedFromPrimaryFile() throws Exception {
storeDatabaseKey(keyFile, encryptedKeyHex); storeDatabaseKey(keyFile, encryptedKeyHex);
@@ -254,6 +201,55 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
assertFalse(keyBackupFile.exists()); assertFalse(keyBackupFile.exists());
} }
@Test
public void testAccountExistsReturnsFalseIfDbDirectoryDoesNotExist()
throws Exception {
storeDatabaseKey(keyFile, encryptedKeyHex);
storeDatabaseKey(keyBackupFile, encryptedKeyHex);
assertFalse(dbDir.exists());
assertFalse(accountManager.accountExists());
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
assertFalse(dbDir.exists());
}
@Test
public void testAccountExistsReturnsFalseIfDbDirectoryIsNotDirectory()
throws Exception {
storeDatabaseKey(keyFile, encryptedKeyHex);
storeDatabaseKey(keyBackupFile, encryptedKeyHex);
assertTrue(dbDir.createNewFile());
assertFalse(dbDir.isDirectory());
assertFalse(accountManager.accountExists());
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
assertTrue(dbDir.exists());
assertFalse(dbDir.isDirectory());
}
@Test
public void testAccountExistsReturnsTrueIfDbDirectoryIsDirectory()
throws Exception {
storeDatabaseKey(keyFile, encryptedKeyHex);
storeDatabaseKey(keyBackupFile, encryptedKeyHex);
assertTrue(dbDir.mkdirs());
assertTrue(dbDir.isDirectory());
assertTrue(accountManager.accountExists());
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
assertTrue(dbDir.exists());
assertTrue(dbDir.isDirectory());
}
@Test @Test
public void testCreateAccountStoresDbKey() throws Exception { public void testCreateAccountStoresDbKey() throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
@@ -262,8 +258,7 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
oneOf(identityManager).registerIdentity(identity); oneOf(identityManager).registerIdentity(identity);
oneOf(crypto).generateSecretKey(); oneOf(crypto).generateSecretKey();
will(returnValue(key)); will(returnValue(key));
oneOf(crypto).encryptWithPassword(key.getBytes(), password, oneOf(crypto).encryptWithPassword(key.getBytes(), password);
keyStrengthener);
will(returnValue(encryptedKey)); will(returnValue(encryptedKey));
}}); }});
@@ -281,36 +276,25 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
} }
@Test @Test
public void testChangePasswordThrowsExceptionIfDbKeyCannotBeLoaded() { public void testChangePasswordReturnsFalseIfDbKeyCannotBeLoaded() {
try { assertFalse(accountManager.changePassword(password, newPassword));
accountManager.changePassword(password, newPassword);
fail();
} catch (DecryptionException expected) {
assertEquals(INVALID_CIPHERTEXT, expected.getDecryptionResult());
}
assertFalse(keyFile.exists()); assertFalse(keyFile.exists());
assertFalse(keyBackupFile.exists()); assertFalse(keyBackupFile.exists());
} }
@Test @Test
public void testChangePasswordThrowsExceptionIfPasswordIsWrong() public void testChangePasswordReturnsFalseIfPasswordIsWrong()
throws Exception { throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(crypto).decryptWithPassword(encryptedKey, password, oneOf(crypto).decryptWithPassword(encryptedKey, password);
keyStrengthener); will(returnValue(null));
will(throwException(new DecryptionException(INVALID_PASSWORD)));
}}); }});
storeDatabaseKey(keyFile, encryptedKeyHex); storeDatabaseKey(keyFile, encryptedKeyHex);
storeDatabaseKey(keyBackupFile, encryptedKeyHex); storeDatabaseKey(keyBackupFile, encryptedKeyHex);
try { assertFalse(accountManager.changePassword(password, newPassword));
accountManager.changePassword(password, newPassword);
fail();
} catch (DecryptionException expected) {
assertEquals(INVALID_PASSWORD, expected.getDecryptionResult());
}
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile)); assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile)); assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
@@ -320,20 +304,16 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
public void testChangePasswordReturnsTrueIfPasswordIsRight() public void testChangePasswordReturnsTrueIfPasswordIsRight()
throws Exception { throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(crypto).decryptWithPassword(encryptedKey, password, oneOf(crypto).decryptWithPassword(encryptedKey, password);
keyStrengthener);
will(returnValue(key.getBytes())); will(returnValue(key.getBytes()));
oneOf(crypto).isEncryptedWithStrengthenedKey(encryptedKey); oneOf(crypto).encryptWithPassword(key.getBytes(), newPassword);
will(returnValue(true));
oneOf(crypto).encryptWithPassword(key.getBytes(), newPassword,
keyStrengthener);
will(returnValue(newEncryptedKey)); will(returnValue(newEncryptedKey));
}}); }});
storeDatabaseKey(keyFile, encryptedKeyHex); storeDatabaseKey(keyFile, encryptedKeyHex);
storeDatabaseKey(keyBackupFile, encryptedKeyHex); storeDatabaseKey(keyBackupFile, encryptedKeyHex);
accountManager.changePassword(password, newPassword); assertTrue(accountManager.changePassword(password, newPassword));
assertEquals(newEncryptedKeyHex, loadDatabaseKey(keyFile)); assertEquals(newEncryptedKeyHex, loadDatabaseKey(keyFile));
assertEquals(newEncryptedKeyHex, loadDatabaseKey(keyBackupFile)); assertEquals(newEncryptedKeyHex, loadDatabaseKey(keyBackupFile));
@@ -342,7 +322,7 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
private void storeDatabaseKey(File f, String hex) throws IOException { private void storeDatabaseKey(File f, String hex) throws IOException {
f.getParentFile().mkdirs(); f.getParentFile().mkdirs();
FileOutputStream out = new FileOutputStream(f); FileOutputStream out = new FileOutputStream(f);
out.write(hex.getBytes(Charset.forName("UTF-8"))); out.write(hex.getBytes("UTF-8"));
out.flush(); out.flush();
out.close(); out.close();
} }
@@ -350,7 +330,7 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
@Nullable @Nullable
private String loadDatabaseKey(File f) throws IOException { private String loadDatabaseKey(File f) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader( BufferedReader reader = new BufferedReader(new InputStreamReader(
new FileInputStream(f), Charset.forName("UTF-8"))); new FileInputStream(f), "UTF-8"));
String hex = reader.readLine(); String hex = reader.readLine();
reader.close(); reader.close();
return hex; return hex;

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.contact; package org.briarproject.bramble.contact;
import org.briarproject.bramble.BrambleCoreEagerSingletons;
import org.briarproject.bramble.api.Pair; import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactManager; import org.briarproject.bramble.api.contact.ContactManager;
@@ -62,11 +61,11 @@ public class ContactExchangeIntegrationTest extends BrambleTestCase {
alice = DaggerContactExchangeIntegrationTestComponent.builder() alice = DaggerContactExchangeIntegrationTestComponent.builder()
.testDatabaseConfigModule( .testDatabaseConfigModule(
new TestDatabaseConfigModule(aliceDir)).build(); new TestDatabaseConfigModule(aliceDir)).build();
BrambleCoreEagerSingletons.Helper.injectEagerSingletons(alice); alice.injectBrambleCoreEagerSingletons();
bob = DaggerContactExchangeIntegrationTestComponent.builder() bob = DaggerContactExchangeIntegrationTestComponent.builder()
.testDatabaseConfigModule(new TestDatabaseConfigModule(bobDir)) .testDatabaseConfigModule(new TestDatabaseConfigModule(bobDir))
.build(); .build();
BrambleCoreEagerSingletons.Helper.injectEagerSingletons(bob); bob.injectBrambleCoreEagerSingletons();
// Set up the devices and get the identities // Set up the devices and get the identities
aliceIdentity = setUp(alice, "Alice"); aliceIdentity = setUp(alice, "Alice");
bobIdentity = setUp(bob, "Bob"); bobIdentity = setUp(bob, "Bob");

View File

@@ -1,116 +1,40 @@
package org.briarproject.bramble.crypto; package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.DecryptionException;
import org.briarproject.bramble.api.crypto.KeyStrengthener;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.system.SystemClock; import org.briarproject.bramble.system.SystemClock;
import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestSecureRandomProvider; import org.briarproject.bramble.test.TestSecureRandomProvider;
import org.jmock.Expectations; import org.briarproject.bramble.test.TestUtils;
import org.junit.Test; import org.junit.Test;
import static org.briarproject.bramble.api.crypto.DecryptionResult.INVALID_CIPHERTEXT; import java.util.Random;
import static org.briarproject.bramble.api.crypto.DecryptionResult.INVALID_PASSWORD;
import static org.briarproject.bramble.api.crypto.DecryptionResult.KEY_STRENGTHENER_ERROR;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
public class PasswordBasedEncryptionTest extends BrambleMockTestCase { public class PasswordBasedEncryptionTest extends BrambleTestCase {
private final KeyStrengthener keyStrengthener =
context.mock(KeyStrengthener.class);
private final CryptoComponentImpl crypto = private final CryptoComponentImpl crypto =
new CryptoComponentImpl(new TestSecureRandomProvider(), new CryptoComponentImpl(new TestSecureRandomProvider(),
new ScryptKdf(new SystemClock())); new ScryptKdf(new SystemClock()));
@Test @Test
public void testEncryptionAndDecryption() throws Exception { public void testEncryptionAndDecryption() {
byte[] input = getRandomBytes(1234); byte[] input = TestUtils.getRandomBytes(1234);
String password = "password"; String password = "password";
byte[] ciphertext = crypto.encryptWithPassword(input, password, null); byte[] ciphertext = crypto.encryptWithPassword(input, password);
byte[] output = crypto.decryptWithPassword(ciphertext, password, null); byte[] output = crypto.decryptWithPassword(ciphertext, password);
assertArrayEquals(input, output); assertArrayEquals(input, output);
} }
@Test @Test
public void testInvalidFormatVersionThrowsException() { public void testInvalidCiphertextReturnsNull() {
byte[] input = getRandomBytes(1234); byte[] input = TestUtils.getRandomBytes(1234);
String password = "password"; String password = "password";
byte[] ciphertext = crypto.encryptWithPassword(input, password, null); byte[] ciphertext = crypto.encryptWithPassword(input, password);
// Modify the ciphertext
// Modify the format version int position = new Random().nextInt(ciphertext.length);
ciphertext[0] ^= (byte) 0xFF; ciphertext[position] = (byte) (ciphertext[position] ^ 0xFF);
try { byte[] output = crypto.decryptWithPassword(ciphertext, password);
crypto.decryptWithPassword(ciphertext, password, null); assertNull(output);
fail();
} catch (DecryptionException expected) {
assertEquals(INVALID_CIPHERTEXT, expected.getDecryptionResult());
}
}
@Test
public void testInvalidPasswordThrowsException() {
byte[] input = getRandomBytes(1234);
byte[] ciphertext = crypto.encryptWithPassword(input, "password", null);
// Try to decrypt with the wrong password
try {
crypto.decryptWithPassword(ciphertext, "wrong", null);
fail();
} catch (DecryptionException expected) {
assertEquals(INVALID_PASSWORD, expected.getDecryptionResult());
}
}
@Test
public void testMissingKeyStrengthenerThrowsException() {
SecretKey strengthened = getSecretKey();
context.checking(new Expectations() {{
oneOf(keyStrengthener).strengthenKey(with(any(SecretKey.class)));
will(returnValue(strengthened));
}});
// Use the key strengthener during encryption
byte[] input = getRandomBytes(1234);
String password = "password";
byte[] ciphertext =
crypto.encryptWithPassword(input, password, keyStrengthener);
// The key strengthener is missing during decryption
try {
crypto.decryptWithPassword(ciphertext, password, null);
fail();
} catch (DecryptionException expected) {
assertEquals(KEY_STRENGTHENER_ERROR, expected.getDecryptionResult());
}
}
@Test
public void testKeyStrengthenerFailureThrowsException() {
SecretKey strengthened = getSecretKey();
context.checking(new Expectations() {{
oneOf(keyStrengthener).strengthenKey(with(any(SecretKey.class)));
will(returnValue(strengthened));
oneOf(keyStrengthener).isInitialised();
will(returnValue(false));
}});
// Use the key strengthener during encryption
byte[] input = getRandomBytes(1234);
String password = "password";
byte[] ciphertext =
crypto.encryptWithPassword(input, password, keyStrengthener);
// The key strengthener fails during decryption
try {
crypto.decryptWithPassword(ciphertext, password, keyStrengthener);
fail();
} catch (DecryptionException expected) {
assertEquals(KEY_STRENGTHENER_ERROR, expected.getDecryptionResult());
}
} }
} }

View File

@@ -4,7 +4,6 @@ import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.system.SystemClock; import org.briarproject.bramble.system.SystemClock;
import org.briarproject.bramble.test.ArrayClock;
import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.BrambleTestCase;
import org.junit.Test; import org.junit.Test;
@@ -12,6 +11,8 @@ import java.util.HashSet;
import java.util.Set; import java.util.Set;
import static junit.framework.TestCase.assertTrue; import static junit.framework.TestCase.assertTrue;
import org.briarproject.bramble.test.ArrayClock;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes; import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.util.StringUtils.getRandomString; import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;

View File

@@ -34,7 +34,6 @@ import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.SettableClock; import org.briarproject.bramble.test.SettableClock;
import org.briarproject.bramble.test.TestDatabaseConfig; import org.briarproject.bramble.test.TestDatabaseConfig;
import org.briarproject.bramble.test.TestMessageFactory; import org.briarproject.bramble.test.TestMessageFactory;
import org.briarproject.bramble.test.TestUtils;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -1150,43 +1149,6 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.close(); db.close();
} }
@Test
public void testGetContactsByHandshakePublicKey() throws Exception {
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add an identity for a local author - no contacts should be
// associated
db.addIdentity(txn, identity);
PublicKey handshakePublicKey = TestUtils.getSignaturePublicKey();
Contact contact =
db.getContact(txn, handshakePublicKey, localAuthor.getId());
assertNull(contact);
// Add a contact associated with the local author
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
handshakePublicKey, true));
contact = db.getContact(txn, handshakePublicKey, localAuthor.getId());
assertNotNull(contact);
assertEquals(contactId, contact.getId());
assertEquals(author, contact.getAuthor());
assertNull(contact.getAlias());
assertEquals(handshakePublicKey, contact.getHandshakePublicKey());
assertTrue(contact.isVerified());
assertEquals(author.getName(), contact.getAuthor().getName());
assertEquals(author.getPublicKey(), contact.getAuthor().getPublicKey());
assertEquals(author.getFormatVersion(),
contact.getAuthor().getFormatVersion());
// Ensure no contacts are returned after contact was deleted
db.removeContact(txn, contactId);
contact = db.getContact(txn, handshakePublicKey, localAuthor.getId());
assertNull(contact);
db.commitTransaction(txn);
db.close();
}
@Test @Test
public void testOfferedMessages() throws Exception { public void testOfferedMessages() throws Exception {
Database<Connection> db = open(false); Database<Connection> db = open(false);

View File

@@ -1,143 +0,0 @@
package org.briarproject.bramble.io;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.SettableClock;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class TimeoutInputStreamTest extends BrambleTestCase {
private static final long TIMEOUT_MS = MINUTES.toMillis(1);
private final long now = System.currentTimeMillis();
private AtomicLong time;
private UnresponsiveInputStream in;
private AtomicBoolean listenerCalled;
private TimeoutInputStream stream;
private CountDownLatch readReturned;
@Before
public void setUp() {
time = new AtomicLong(now);
in = new UnresponsiveInputStream();
listenerCalled = new AtomicBoolean(false);
stream = new TimeoutInputStream(new SettableClock(time), in,
TIMEOUT_MS, stream -> listenerCalled.set(true));
readReturned = new CountDownLatch(1);
}
@Test
public void testTimeoutIsReportedIfReadDoesNotReturn() throws Exception {
startReading();
try {
// The stream should not report a timeout
assertFalse(stream.hasTimedOut());
// Time passes
time.set(now + TIMEOUT_MS);
// The stream still shouldn't report a timeout
assertFalse(stream.hasTimedOut());
// Time passes
time.set(now + TIMEOUT_MS + 1);
// The stream should report a timeout
assertTrue(stream.hasTimedOut());
// The listener should not have been called yet
assertFalse(listenerCalled.get());
// Close the stream
stream.close();
// The listener should have been called
assertTrue(listenerCalled.get());
} finally {
// Allow the read to return
in.readFinished.countDown();
}
}
@Test
public void testTimeoutIsNotReportedIfReadReturns() throws Exception {
startReading();
try {
// The stream should not report a timeout
assertFalse(stream.hasTimedOut());
// Time passes
time.set(now + TIMEOUT_MS);
// The stream still shouldn't report a timeout
assertFalse(stream.hasTimedOut());
// Allow the read to finish and wait for it to return
in.readFinished.countDown();
readReturned.await(10, SECONDS);
// Time passes
time.set(now + TIMEOUT_MS + 1);
// The stream should not report a timeout as the read has returned
assertFalse(stream.hasTimedOut());
// The listener should not have been called yet
assertFalse(listenerCalled.get());
// Close the stream
stream.close();
// The listener should have been called
assertTrue(listenerCalled.get());
} finally {
// Allow the read to return in case an assertion was thrown
in.readFinished.countDown();
}
}
private void startReading() throws Exception {
// Start a background thread to read from the unresponsive stream
new Thread(() -> {
try {
assertEquals(123, stream.read());
readReturned.countDown();
} catch (IOException e) {
fail();
}
}).start();
// Wait for the background thread to start reading
assertTrue(in.readStarted.await(10, SECONDS));
}
private class UnresponsiveInputStream extends InputStream {
private final CountDownLatch readStarted = new CountDownLatch(1);
private final CountDownLatch readFinished = new CountDownLatch(1);
@Override
public int read() throws IOException {
readStarted.countDown();
try {
readFinished.await();
return 123;
} catch (InterruptedException e) {
throw new IOException(e);
}
}
}
}

View File

@@ -1,182 +0,0 @@
package org.briarproject.bramble.plugin.bluetooth;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.sync.event.CloseSyncConnectionsEvent;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.SettableClock;
import org.jmock.Expectations;
import org.junit.Before;
import org.junit.Test;
import java.util.concurrent.atomic.AtomicLong;
import static org.briarproject.bramble.plugin.bluetooth.BluetoothConnectionLimiter.MIN_ATTEMPT_INTERVAL_MS;
import static org.briarproject.bramble.plugin.bluetooth.BluetoothConnectionLimiter.STABILITY_PERIOD_MS;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class BluetoothConnectionLimiterImplTest extends BrambleMockTestCase {
private final EventBus eventBus = context.mock(EventBus.class);
private final DuplexTransportConnection conn1 =
context.mock(DuplexTransportConnection.class, "conn1");
private final DuplexTransportConnection conn2 =
context.mock(DuplexTransportConnection.class, "conn2");
private final DuplexTransportConnection conn3 =
context.mock(DuplexTransportConnection.class, "conn3");
private final long now = System.currentTimeMillis();
private AtomicLong time;
private BluetoothConnectionLimiter limiter;
@Before
public void setUp() {
time = new AtomicLong(now);
Clock clock = new SettableClock(time);
limiter = new BluetoothConnectionLimiterImpl(eventBus, clock);
}
@Test
public void testLimiterDoesNotAllowContactConnectionsDuringKeyAgreement() {
assertTrue(limiter.canOpenContactConnection());
expectCloseSyncConnectionsEvent();
limiter.keyAgreementStarted();
assertFalse(limiter.canOpenContactConnection());
limiter.keyAgreementEnded();
assertTrue(limiter.canOpenContactConnection());
}
@Test
public void testLimiterAllowsAttemptToRaiseLimitAtStartup() {
// First outgoing connection is allowed - we're below the limit of 1
assertTrue(limiter.canOpenContactConnection());
assertTrue(limiter.contactConnectionOpened(conn1, false));
// Second outgoing connection is allowed - it's time to try raising
// the limit to 2
assertTrue(limiter.canOpenContactConnection());
assertTrue(limiter.contactConnectionOpened(conn2, false));
// Third outgoing connection is not allowed - we're above the limit of 1
assertFalse(limiter.canOpenContactConnection());
}
@Test
public void testLimiterAllowsThirdConnectionAfterFirstTwoAreClosed() {
// First outgoing connection is allowed - we're below the limit of 1
assertTrue(limiter.canOpenContactConnection());
assertTrue(limiter.contactConnectionOpened(conn1, false));
// Second outgoing connection is allowed - it's time to try raising
// the limit to 2
assertTrue(limiter.canOpenContactConnection());
assertTrue(limiter.contactConnectionOpened(conn2, false));
// Third outgoing connection is not allowed - we're above the limit of 1
assertFalse(limiter.canOpenContactConnection());
// Close the first connection
limiter.connectionClosed(conn1, false);
// Third outgoing connection is not allowed - we're at the limit of 1
assertFalse(limiter.canOpenContactConnection());
// Close the second connection
limiter.connectionClosed(conn2, false);
// Third outgoing connection is allowed - we're below the limit of 1
assertTrue(limiter.canOpenContactConnection());
assertTrue(limiter.contactConnectionOpened(conn3, false));
}
@Test
public void testLimiterRaisesLimitWhenConnectionsAreStable() {
// First outgoing connection is allowed - we're below the limit of 1
assertTrue(limiter.canOpenContactConnection());
assertTrue(limiter.contactConnectionOpened(conn1, false));
// Second outgoing connection is allowed - it's time to try raising
// the limit to 2
assertTrue(limiter.canOpenContactConnection());
assertTrue(limiter.contactConnectionOpened(conn2, false));
// Third outgoing connection is not allowed - we're above the limit of 1
assertFalse(limiter.canOpenContactConnection());
// Time passes
time.set(now + STABILITY_PERIOD_MS);
// Third outgoing connection is still not allowed - first two are now
// stable so limit is raised to 2, but we're already at the new limit
assertFalse(limiter.canOpenContactConnection());
// Time passes
time.set(now + MIN_ATTEMPT_INTERVAL_MS);
// Third outgoing connection is allowed - it's time to try raising
// the limit to 3
assertTrue(limiter.canOpenContactConnection());
assertTrue(limiter.contactConnectionOpened(conn3, false));
// Fourth outgoing connection is not allowed - we're above the limit
// of 2
assertFalse(limiter.canOpenContactConnection());
}
@Test
public void testLimiterIncreasesIntervalWhenConnectionFailsAboveLimit() {
// First outgoing connection is allowed - we're below the limit of 1
assertTrue(limiter.canOpenContactConnection());
assertTrue(limiter.contactConnectionOpened(conn1, false));
// Time passes
time.set(now + 1);
// Second outgoing connection is allowed - it's time to try raising
// the limit to 2
assertTrue(limiter.canOpenContactConnection());
assertTrue(limiter.contactConnectionOpened(conn2, false));
// Time passes - the first connection is stable, the second isn't
time.set(now + STABILITY_PERIOD_MS);
// First connection fails. The second connection isn't stable yet, so
// the limiter considers this a failed attempt and doubles the interval
// between attempts
limiter.connectionClosed(conn1, true);
// Third outgoing connection is not allowed - we're still at the limit
// of 1
assertFalse(limiter.canOpenContactConnection());
// Time passes - nearly time for the second attempt
time.set(now + MIN_ATTEMPT_INTERVAL_MS * 2);
// Third outgoing connection is not allowed - we're still at the limit
// of 1
assertFalse(limiter.canOpenContactConnection());
// Time passes - now it's time for the second attempt
time.set(now + 1 + MIN_ATTEMPT_INTERVAL_MS * 2);
// Third outgoing connection is allowed - it's time to try raising the
// limit to 2 again
assertTrue(limiter.canOpenContactConnection());
assertTrue(limiter.contactConnectionOpened(conn3, false));
}
private void expectCloseSyncConnectionsEvent() {
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(
CloseSyncConnectionsEvent.class)));
}});
}
}

View File

@@ -7,11 +7,12 @@ 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.plugin.TransportConnectionReader; import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter; import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
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.properties.TransportProperties;
import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.api.settings.Settings;
import org.briarproject.bramble.plugin.tcp.LanTcpPlugin.LanAddressComparator;
import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.BrambleTestCase;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import java.io.IOException; import java.io.IOException;
@@ -21,6 +22,7 @@ import java.net.InetSocketAddress;
import java.net.NetworkInterface; import java.net.NetworkInterface;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.util.Comparator;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
@@ -31,89 +33,56 @@ import static java.util.concurrent.Executors.newCachedThreadPool;
import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.concurrent.TimeUnit.SECONDS;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_LAN; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_LAN;
import static org.briarproject.bramble.plugin.tcp.LanTcpPlugin.areAddressesInSameNetwork;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
public class LanTcpPluginTest extends BrambleTestCase { public class LanTcpPluginTest extends BrambleTestCase {
private final Backoff backoff = new TestBackoff(); private final Backoff backoff = new TestBackoff();
private final ExecutorService ioExecutor = newCachedThreadPool(); private final ExecutorService ioExecutor = newCachedThreadPool();
private Callback callback = null;
private LanTcpPlugin plugin = null;
@Before
public void setUp() {
callback = new Callback();
plugin = new LanTcpPlugin(ioExecutor, backoff, callback, 0, 0, 1000) {
@Override
protected boolean canConnectToOwnAddress() {
return true;
}
};
}
@Test @Test
public void testAreAddressesInSameNetwork() { public void testAddressesAreOnSameLan() {
// Local and remote in 10.0.0.0/8 Callback callback = new Callback();
assertTrue(areAddressesInSameNetwork(makeAddress(10, 0, 0, 0), LanTcpPlugin plugin = new LanTcpPlugin(ioExecutor, backoff, callback,
makeAddress(10, 255, 255, 255), 8)); 0, 0);
assertFalse(areAddressesInSameNetwork(makeAddress(10, 0, 0, 0), // Local and remote in 10.0.0.0/8 should return true
makeAddress(10, 255, 255, 255), 9)); assertTrue(plugin.addressesAreOnSameLan(makeAddress(10, 0, 0, 0),
makeAddress(10, 255, 255, 255)));
// Local and remote in 172.16.0.0/12 // Local and remote in 172.16.0.0/12 should return true
assertTrue(areAddressesInSameNetwork(makeAddress(172, 16, 0, 0), assertTrue(plugin.addressesAreOnSameLan(makeAddress(172, 16, 0, 0),
makeAddress(172, 31, 255, 255), 12)); makeAddress(172, 31, 255, 255)));
assertFalse(areAddressesInSameNetwork(makeAddress(172, 16, 0, 0), // Local and remote in 192.168.0.0/16 should return true
makeAddress(172, 31, 255, 255), 13)); assertTrue(plugin.addressesAreOnSameLan(makeAddress(192, 168, 0, 0),
makeAddress(192, 168, 255, 255)));
// Local and remote in 192.168.0.0/16 // Local and remote in 169.254.0.0/16 (link-local) should return true
assertTrue(areAddressesInSameNetwork(makeAddress(192, 168, 0, 0), assertTrue(plugin.addressesAreOnSameLan(makeAddress(169, 254, 0, 0),
makeAddress(192, 168, 255, 255), 16)); makeAddress(169, 254, 255, 255)));
assertFalse(areAddressesInSameNetwork(makeAddress(192, 168, 0, 0), // Local and remote in different recognised prefixes should return false
makeAddress(192, 168, 255, 255), 17)); assertFalse(plugin.addressesAreOnSameLan(makeAddress(10, 0, 0, 0),
makeAddress(172, 31, 255, 255)));
// Local and remote in 169.254.0.0/16 assertFalse(plugin.addressesAreOnSameLan(makeAddress(10, 0, 0, 0),
assertTrue(areAddressesInSameNetwork(makeAddress(169, 254, 0, 0), makeAddress(192, 168, 255, 255)));
makeAddress(169, 254, 255, 255), 16)); assertFalse(plugin.addressesAreOnSameLan(makeAddress(172, 16, 0, 0),
assertFalse(areAddressesInSameNetwork(makeAddress(169, 254, 0, 0), makeAddress(10, 255, 255, 255)));
makeAddress(169, 254, 255, 255), 17)); assertFalse(plugin.addressesAreOnSameLan(makeAddress(172, 16, 0, 0),
makeAddress(192, 168, 255, 255)));
// Local in 10.0.0.0/8, remote in a different network assertFalse(plugin.addressesAreOnSameLan(makeAddress(192, 168, 0, 0),
assertFalse(areAddressesInSameNetwork(makeAddress(10, 0, 0, 0), makeAddress(10, 255, 255, 255)));
makeAddress(172, 31, 255, 255), 8)); assertFalse(plugin.addressesAreOnSameLan(makeAddress(192, 168, 0, 0),
assertFalse(areAddressesInSameNetwork(makeAddress(10, 0, 0, 0), makeAddress(172, 31, 255, 255)));
makeAddress(192, 168, 255, 255), 8)); // Remote prefix unrecognised should return false
assertFalse(areAddressesInSameNetwork(makeAddress(10, 0, 0, 0), assertFalse(plugin.addressesAreOnSameLan(makeAddress(10, 0, 0, 0),
makeAddress(169, 254, 255, 255), 8)); makeAddress(1, 2, 3, 4)));
assertFalse(plugin.addressesAreOnSameLan(makeAddress(172, 16, 0, 0),
// Local in 172.16.0.0/12, remote in a different network makeAddress(1, 2, 3, 4)));
assertFalse(areAddressesInSameNetwork(makeAddress(172, 16, 0, 0), assertFalse(plugin.addressesAreOnSameLan(makeAddress(192, 168, 0, 0),
makeAddress(10, 255, 255, 255), 12)); makeAddress(1, 2, 3, 4)));
assertFalse(areAddressesInSameNetwork(makeAddress(172, 16, 0, 0), // Both prefixes unrecognised should return true (could be link-local)
makeAddress(192, 168, 255, 255), 12)); assertTrue(plugin.addressesAreOnSameLan(makeAddress(1, 2, 3, 4),
assertFalse(areAddressesInSameNetwork(makeAddress(172, 16, 0, 0), makeAddress(5, 6, 7, 8)));
makeAddress(169, 254, 255, 255), 12));
// Local in 192.168.0.0/16, remote in a different network
assertFalse(areAddressesInSameNetwork(makeAddress(192, 168, 0, 0),
makeAddress(10, 255, 255, 255), 16));
assertFalse(areAddressesInSameNetwork(makeAddress(192, 168, 0, 0),
makeAddress(172, 31, 255, 255), 16));
assertFalse(areAddressesInSameNetwork(makeAddress(192, 168, 0, 0),
makeAddress(169, 254, 255, 255), 16));
// Local in 169.254.0.0/16, remote in a different network
assertFalse(areAddressesInSameNetwork(makeAddress(169, 254, 0, 0),
makeAddress(10, 255, 255, 255), 16));
assertFalse(areAddressesInSameNetwork(makeAddress(169, 254, 0, 0),
makeAddress(172, 31, 255, 255), 16));
assertFalse(areAddressesInSameNetwork(makeAddress(169, 254, 0, 0),
makeAddress(192, 168, 255, 255), 16));
} }
private byte[] makeAddress(int... parts) { private byte[] makeAddress(int... parts) {
@@ -124,7 +93,13 @@ public class LanTcpPluginTest extends BrambleTestCase {
@Test @Test
public void testIncomingConnection() throws Exception { public void testIncomingConnection() throws Exception {
assumeTrue(systemHasLocalIpv4Address()); if (!systemHasLocalIpv4Address()) {
System.err.println("WARNING: Skipping test, no local IPv4 address");
return;
}
Callback callback = new Callback();
DuplexPlugin plugin = new LanTcpPlugin(ioExecutor, backoff, callback,
0, 0);
plugin.start(); plugin.start();
// The plugin should have bound a socket and stored the port number // The plugin should have bound a socket and stored the port number
assertTrue(callback.propertiesLatch.await(5, SECONDS)); assertTrue(callback.propertiesLatch.await(5, SECONDS));
@@ -153,7 +128,13 @@ public class LanTcpPluginTest extends BrambleTestCase {
@Test @Test
public void testOutgoingConnection() throws Exception { public void testOutgoingConnection() throws Exception {
assumeTrue(systemHasLocalIpv4Address()); if (!systemHasLocalIpv4Address()) {
System.err.println("WARNING: Skipping test, no local IPv4 address");
return;
}
Callback callback = new Callback();
DuplexPlugin plugin = new LanTcpPlugin(ioExecutor, backoff, callback,
0, 0);
plugin.start(); plugin.start();
// The plugin should have bound a socket and stored the port number // The plugin should have bound a socket and stored the port number
assertTrue(callback.propertiesLatch.await(5, SECONDS)); assertTrue(callback.propertiesLatch.await(5, SECONDS));
@@ -196,7 +177,13 @@ public class LanTcpPluginTest extends BrambleTestCase {
@Test @Test
public void testIncomingKeyAgreementConnection() throws Exception { public void testIncomingKeyAgreementConnection() throws Exception {
assumeTrue(systemHasLocalIpv4Address()); if (!systemHasLocalIpv4Address()) {
System.err.println("WARNING: Skipping test, no local IPv4 address");
return;
}
Callback callback = new Callback();
DuplexPlugin plugin = new LanTcpPlugin(ioExecutor, backoff, callback,
0, 0);
plugin.start(); plugin.start();
assertTrue(callback.propertiesLatch.await(5, SECONDS)); assertTrue(callback.propertiesLatch.await(5, SECONDS));
KeyAgreementListener kal = KeyAgreementListener kal =
@@ -238,7 +225,13 @@ public class LanTcpPluginTest extends BrambleTestCase {
@Test @Test
public void testOutgoingKeyAgreementConnection() throws Exception { public void testOutgoingKeyAgreementConnection() throws Exception {
assumeTrue(systemHasLocalIpv4Address()); if (!systemHasLocalIpv4Address()) {
System.err.println("WARNING: Skipping test, no local IPv4 address");
return;
}
Callback callback = new Callback();
DuplexPlugin plugin = new LanTcpPlugin(ioExecutor, backoff, callback,
0, 0);
plugin.start(); plugin.start();
// The plugin should have bound a socket and stored the port number // The plugin should have bound a socket and stored the port number
assertTrue(callback.propertiesLatch.await(5, SECONDS)); assertTrue(callback.propertiesLatch.await(5, SECONDS));
@@ -283,12 +276,62 @@ public class LanTcpPluginTest extends BrambleTestCase {
plugin.stop(); plugin.stop();
} }
@Test
public void testComparatorPrefersNonZeroPorts() {
Comparator<InetSocketAddress> comparator = new LanAddressComparator();
InetSocketAddress nonZero = new InetSocketAddress("1.2.3.4", 1234);
InetSocketAddress zero = new InetSocketAddress("1.2.3.4", 0);
assertEquals(0, comparator.compare(nonZero, nonZero));
assertTrue(comparator.compare(nonZero, zero) < 0);
assertTrue(comparator.compare(zero, nonZero) > 0);
assertEquals(0, comparator.compare(zero, zero));
}
@Test
public void testComparatorPrefersLongerPrefixes() {
Comparator<InetSocketAddress> comparator = new LanAddressComparator();
InetSocketAddress prefix192 = new InetSocketAddress("192.168.0.1", 0);
InetSocketAddress prefix172 = new InetSocketAddress("172.16.0.1", 0);
InetSocketAddress prefix10 = new InetSocketAddress("10.0.0.1", 0);
assertEquals(0, comparator.compare(prefix192, prefix192));
assertTrue(comparator.compare(prefix192, prefix172) < 0);
assertTrue(comparator.compare(prefix192, prefix10) < 0);
assertTrue(comparator.compare(prefix172, prefix192) > 0);
assertEquals(0, comparator.compare(prefix172, prefix172));
assertTrue(comparator.compare(prefix172, prefix10) < 0);
assertTrue(comparator.compare(prefix10, prefix192) > 0);
assertTrue(comparator.compare(prefix10, prefix172) > 0);
assertEquals(0, comparator.compare(prefix10, prefix10));
}
@Test
public void testComparatorPrefersSiteLocalToLinkLocal() {
Comparator<InetSocketAddress> comparator = new LanAddressComparator();
InetSocketAddress prefix192 = new InetSocketAddress("192.168.0.1", 0);
InetSocketAddress prefix172 = new InetSocketAddress("172.16.0.1", 0);
InetSocketAddress prefix10 = new InetSocketAddress("10.0.0.1", 0);
InetSocketAddress linkLocal = new InetSocketAddress("169.254.0.1", 0);
assertTrue(comparator.compare(prefix192, linkLocal) < 0);
assertTrue(comparator.compare(prefix172, linkLocal) < 0);
assertTrue(comparator.compare(prefix10, linkLocal) < 0);
assertTrue(comparator.compare(linkLocal, prefix192) > 0);
assertTrue(comparator.compare(linkLocal, prefix172) > 0);
assertTrue(comparator.compare(linkLocal, prefix10) > 0);
assertEquals(0, comparator.compare(linkLocal, linkLocal));
}
private boolean systemHasLocalIpv4Address() throws Exception { private boolean systemHasLocalIpv4Address() throws Exception {
for (NetworkInterface i : list(getNetworkInterfaces())) { for (NetworkInterface i : list(getNetworkInterfaces())) {
for (InetAddress a : list(i.getInetAddresses())) { for (InetAddress a : list(i.getInetAddresses())) {
if (a instanceof Inet4Address) { if (a instanceof Inet4Address)
return a.isLinkLocalAddress() || a.isSiteLocalAddress(); return a.isLinkLocalAddress() || a.isSiteLocalAddress();
}
} }
} }
return false; return false;
@@ -297,9 +340,7 @@ public class LanTcpPluginTest extends BrambleTestCase {
@NotNullByDefault @NotNullByDefault
private static class Callback implements PluginCallback { private static class Callback implements PluginCallback {
// Properties will be stored twice: the preferred port at startup, private final CountDownLatch propertiesLatch = new CountDownLatch(1);
// and the IP:port when the server socket is bound
private final CountDownLatch propertiesLatch = new CountDownLatch(2);
private final CountDownLatch connectionsLatch = new CountDownLatch(1); private final CountDownLatch connectionsLatch = new CountDownLatch(1);
private final TransportProperties local = new TransportProperties(); private final TransportProperties local = new TransportProperties();

View File

@@ -24,18 +24,14 @@ import org.briarproject.bramble.test.DbExpectations;
import org.jmock.Expectations; import org.jmock.Expectations;
import org.junit.Test; import org.junit.Test;
import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.GROUP_KEY_DISCOVERED;
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MSG_KEY_LOCAL;
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MSG_KEY_TRANSPORT_ID;
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MSG_KEY_VERSION;
import static org.briarproject.bramble.api.properties.TransportPropertyManager.CLIENT_ID; import static org.briarproject.bramble.api.properties.TransportPropertyManager.CLIENT_ID;
import static org.briarproject.bramble.api.properties.TransportPropertyManager.MAJOR_VERSION; import static org.briarproject.bramble.api.properties.TransportPropertyManager.MAJOR_VERSION;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED; import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
@@ -190,25 +186,25 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
Message message = getMessage(contactGroupId); Message message = getMessage(contactGroupId);
Metadata meta = new Metadata(); Metadata meta = new Metadata();
BdfDictionary metaDictionary = BdfDictionary.of( BdfDictionary metaDictionary = BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "foo"), new BdfEntry("transportId", "foo"),
new BdfEntry(MSG_KEY_VERSION, 2), new BdfEntry("version", 2),
new BdfEntry(MSG_KEY_LOCAL, false) new BdfEntry("local", false)
); );
Map<MessageId, BdfDictionary> messageMetadata = Map<MessageId, BdfDictionary> messageMetadata =
new LinkedHashMap<>(); new LinkedHashMap<>();
// A remote update for another transport should be ignored // A remote update for another transport should be ignored
MessageId barUpdateId = new MessageId(getRandomId()); MessageId barUpdateId = new MessageId(getRandomId());
messageMetadata.put(barUpdateId, BdfDictionary.of( messageMetadata.put(barUpdateId, BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "bar"), new BdfEntry("transportId", "bar"),
new BdfEntry(MSG_KEY_VERSION, 1), new BdfEntry("version", 1),
new BdfEntry(MSG_KEY_LOCAL, false) new BdfEntry("local", false)
)); ));
// A local update for the same transport should be ignored // A local update for the same transport should be ignored
MessageId localUpdateId = new MessageId(getRandomId()); MessageId localUpdateId = new MessageId(getRandomId());
messageMetadata.put(localUpdateId, BdfDictionary.of( messageMetadata.put(localUpdateId, BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "foo"), new BdfEntry("transportId", "foo"),
new BdfEntry(MSG_KEY_VERSION, 1), new BdfEntry("version", 1),
new BdfEntry(MSG_KEY_LOCAL, true) new BdfEntry("local", true)
)); ));
context.checking(new Expectations() {{ context.checking(new Expectations() {{
@@ -232,18 +228,18 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
Metadata meta = new Metadata(); Metadata meta = new Metadata();
// Version 4 is being delivered // Version 4 is being delivered
BdfDictionary metaDictionary = BdfDictionary.of( BdfDictionary metaDictionary = BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "foo"), new BdfEntry("transportId", "foo"),
new BdfEntry(MSG_KEY_VERSION, 4), new BdfEntry("version", 4),
new BdfEntry(MSG_KEY_LOCAL, false) new BdfEntry("local", false)
); );
Map<MessageId, BdfDictionary> messageMetadata = Map<MessageId, BdfDictionary> messageMetadata =
new LinkedHashMap<>(); new LinkedHashMap<>();
// An older remote update for the same transport should be deleted // An older remote update for the same transport should be deleted
MessageId fooVersion3 = new MessageId(getRandomId()); MessageId fooVersion3 = new MessageId(getRandomId());
messageMetadata.put(fooVersion3, BdfDictionary.of( messageMetadata.put(fooVersion3, BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "foo"), new BdfEntry("transportId", "foo"),
new BdfEntry(MSG_KEY_VERSION, 3), new BdfEntry("version", 3),
new BdfEntry(MSG_KEY_LOCAL, false) new BdfEntry("local", false)
)); ));
context.checking(new Expectations() {{ context.checking(new Expectations() {{
@@ -269,18 +265,18 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
Metadata meta = new Metadata(); Metadata meta = new Metadata();
// Version 3 is being delivered // Version 3 is being delivered
BdfDictionary metaDictionary = BdfDictionary.of( BdfDictionary metaDictionary = BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "foo"), new BdfEntry("transportId", "foo"),
new BdfEntry(MSG_KEY_VERSION, 3), new BdfEntry("version", 3),
new BdfEntry(MSG_KEY_LOCAL, false) new BdfEntry("local", false)
); );
Map<MessageId, BdfDictionary> messageMetadata = Map<MessageId, BdfDictionary> messageMetadata =
new LinkedHashMap<>(); new LinkedHashMap<>();
// A newer remote update for the same transport should not be deleted // A newer remote update for the same transport should not be deleted
MessageId fooVersion4 = new MessageId(getRandomId()); MessageId fooVersion4 = new MessageId(getRandomId());
messageMetadata.put(fooVersion4, BdfDictionary.of( messageMetadata.put(fooVersion4, BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "foo"), new BdfEntry("transportId", "foo"),
new BdfEntry(MSG_KEY_VERSION, 4), new BdfEntry("version", 4),
new BdfEntry(MSG_KEY_LOCAL, false) new BdfEntry("local", false)
)); ));
context.checking(new Expectations() {{ context.checking(new Expectations() {{
@@ -346,9 +342,9 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
// A local update for another transport should be ignored // A local update for another transport should be ignored
MessageId barUpdateId = new MessageId(getRandomId()); MessageId barUpdateId = new MessageId(getRandomId());
messageMetadata.put(barUpdateId, BdfDictionary.of( messageMetadata.put(barUpdateId, BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "bar"), new BdfEntry("transportId", "bar"),
new BdfEntry(MSG_KEY_VERSION, 1), new BdfEntry("version", 1),
new BdfEntry(MSG_KEY_LOCAL, true) new BdfEntry("local", true)
)); ));
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
@@ -370,16 +366,16 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
// A local update for another transport should be ignored // A local update for another transport should be ignored
MessageId barUpdateId = new MessageId(getRandomId()); MessageId barUpdateId = new MessageId(getRandomId());
messageMetadata.put(barUpdateId, BdfDictionary.of( messageMetadata.put(barUpdateId, BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "bar"), new BdfEntry("transportId", "bar"),
new BdfEntry(MSG_KEY_VERSION, 1), new BdfEntry("version", 1),
new BdfEntry(MSG_KEY_LOCAL, true) new BdfEntry("local", true)
)); ));
// A local update for the right transport should be returned // A local update for the right transport should be returned
MessageId fooUpdateId = new MessageId(getRandomId()); MessageId fooUpdateId = new MessageId(getRandomId());
messageMetadata.put(fooUpdateId, BdfDictionary.of( messageMetadata.put(fooUpdateId, BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "foo"), new BdfEntry("transportId", "foo"),
new BdfEntry(MSG_KEY_VERSION, 1), new BdfEntry("version", 1),
new BdfEntry(MSG_KEY_LOCAL, true) new BdfEntry("local", true)
)); ));
BdfList fooUpdate = BdfList.of("foo", 1, fooPropertiesDict); BdfList fooUpdate = BdfList.of("foo", 1, fooPropertiesDict);
@@ -409,28 +405,28 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
List<Contact> contacts = asList(contact1, contact2); List<Contact> contacts = asList(contact1, contact2);
Group contactGroup1 = getGroup(CLIENT_ID, MAJOR_VERSION); Group contactGroup1 = getGroup(CLIENT_ID, MAJOR_VERSION);
Group contactGroup2 = getGroup(CLIENT_ID, MAJOR_VERSION); Group contactGroup2 = getGroup(CLIENT_ID, MAJOR_VERSION);
Map<MessageId, BdfDictionary> messageMetadata = Map<MessageId, BdfDictionary> messageMetadata2 =
new LinkedHashMap<>(); new LinkedHashMap<>();
// A remote update for another transport should be ignored // A remote update for another transport should be ignored
MessageId barUpdateId = new MessageId(getRandomId()); MessageId barUpdateId = new MessageId(getRandomId());
messageMetadata.put(barUpdateId, BdfDictionary.of( messageMetadata2.put(barUpdateId, BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "bar"), new BdfEntry("transportId", "bar"),
new BdfEntry(MSG_KEY_VERSION, 1), new BdfEntry("version", 1),
new BdfEntry(MSG_KEY_LOCAL, false) new BdfEntry("local", false)
)); ));
// A local update for the right transport should be ignored // A local update for the right transport should be ignored
MessageId localUpdateId = new MessageId(getRandomId()); MessageId localUpdateId = new MessageId(getRandomId());
messageMetadata.put(localUpdateId, BdfDictionary.of( messageMetadata2.put(localUpdateId, BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "foo"), new BdfEntry("transportId", "foo"),
new BdfEntry(MSG_KEY_VERSION, 1), new BdfEntry("version", 1),
new BdfEntry(MSG_KEY_LOCAL, true) new BdfEntry("local", true)
)); ));
// A remote update for the right transport should be returned // A remote update for the right transport should be returned
MessageId fooUpdateId = new MessageId(getRandomId()); MessageId fooUpdateId = new MessageId(getRandomId());
messageMetadata.put(fooUpdateId, BdfDictionary.of( messageMetadata2.put(fooUpdateId, BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "foo"), new BdfEntry("transportId", "foo"),
new BdfEntry(MSG_KEY_VERSION, 1), new BdfEntry("version", 1),
new BdfEntry(MSG_KEY_LOCAL, false) new BdfEntry("local", false)
)); ));
BdfList fooUpdate = BdfList.of("foo", 1, fooPropertiesDict); BdfList fooUpdate = BdfList.of("foo", 1, fooPropertiesDict);
@@ -444,25 +440,19 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
will(returnValue(contactGroup1)); will(returnValue(contactGroup1));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup1.getId()); contactGroup1.getId());
will(returnValue(emptyMap())); will(returnValue(Collections.emptyMap()));
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
contactGroup1.getId());
will(returnValue(new BdfDictionary()));
// Second contact: returns an update // Second contact: returns an update
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact2); MAJOR_VERSION, contact2);
will(returnValue(contactGroup2)); will(returnValue(contactGroup2));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup2.getId()); contactGroup2.getId());
will(returnValue(messageMetadata)); will(returnValue(messageMetadata2));
oneOf(clientHelper).getMessageAsList(txn, fooUpdateId); oneOf(clientHelper).getMessageAsList(txn, fooUpdateId);
will(returnValue(fooUpdate)); will(returnValue(fooUpdate));
oneOf(clientHelper).parseAndValidateTransportProperties( oneOf(clientHelper).parseAndValidateTransportProperties(
fooPropertiesDict); fooPropertiesDict);
will(returnValue(fooProperties)); will(returnValue(fooProperties));
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
contactGroup2.getId());
will(returnValue(new BdfDictionary()));
}}); }});
TransportPropertyManagerImpl t = createInstance(); TransportPropertyManagerImpl t = createInstance();
@@ -473,62 +463,6 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
assertEquals(fooProperties, properties.get(contact2.getId())); assertEquals(fooProperties, properties.get(contact2.getId()));
} }
@Test
public void testReceivePropertiesOverrideDiscoveredProperties()
throws Exception {
Transaction txn = new Transaction(null, true);
Contact contact = getContact();
List<Contact> contacts = singletonList(contact);
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
MessageId updateId = new MessageId(getRandomId());
Map<MessageId, BdfDictionary> messageMetadata = singletonMap(updateId,
BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "foo"),
new BdfEntry(MSG_KEY_VERSION, 1),
new BdfEntry(MSG_KEY_LOCAL, false)
));
BdfList update = BdfList.of("foo", 1, fooPropertiesDict);
TransportProperties discovered = new TransportProperties();
discovered.put("fooKey1", "overridden");
discovered.put("fooKey3", "fooValue3");
BdfDictionary discoveredDict = new BdfDictionary(discovered);
BdfDictionary groupMeta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_DISCOVERED, discoveredDict)
);
TransportProperties merged = new TransportProperties();
merged.putAll(fooProperties);
merged.put("fooKey3", "fooValue3");
context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
oneOf(db).getContacts(txn);
will(returnValue(contacts));
// One update
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact);
will(returnValue(contactGroup));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(messageMetadata));
oneOf(clientHelper).getMessageAsList(txn, updateId);
will(returnValue(update));
oneOf(clientHelper).parseAndValidateTransportProperties(
fooPropertiesDict);
will(returnValue(fooProperties));
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(groupMeta));
oneOf(clientHelper).parseAndValidateTransportProperties(
discoveredDict);
will(returnValue(discovered));
}});
TransportPropertyManagerImpl t = createInstance();
Map<ContactId, TransportProperties> properties =
t.getRemoteProperties(new TransportId("foo"));
assertEquals(merged, properties.get(contact.getId()));
}
@Test @Test
public void testMergingUnchangedPropertiesDoesNotCreateUpdate() public void testMergingUnchangedPropertiesDoesNotCreateUpdate()
throws Exception { throws Exception {
@@ -536,9 +470,9 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
MessageId updateId = new MessageId(getRandomId()); MessageId updateId = new MessageId(getRandomId());
Map<MessageId, BdfDictionary> messageMetadata = singletonMap(updateId, Map<MessageId, BdfDictionary> messageMetadata = singletonMap(updateId,
BdfDictionary.of( BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "foo"), new BdfEntry("transportId", "foo"),
new BdfEntry(MSG_KEY_VERSION, 1), new BdfEntry("version", 1),
new BdfEntry(MSG_KEY_LOCAL, true) new BdfEntry("local", true)
)); ));
BdfList update = BdfList.of("foo", 1, fooPropertiesDict); BdfList update = BdfList.of("foo", 1, fooPropertiesDict);
@@ -571,7 +505,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
// There are no existing properties to merge with // There are no existing properties to merge with
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
localGroup.getId()); localGroup.getId());
will(returnValue(emptyMap())); will(returnValue(Collections.emptyMap()));
// Store the new properties in the local group, version 1 // Store the new properties in the local group, version 1
expectStoreMessage(txn, localGroup.getId(), "foo", expectStoreMessage(txn, localGroup.getId(), "foo",
fooPropertiesDict, 1, true, false); fooPropertiesDict, 1, true, false);
@@ -583,7 +517,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
will(returnValue(contactGroup)); will(returnValue(contactGroup));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup.getId()); contactGroup.getId());
will(returnValue(emptyMap())); will(returnValue(Collections.emptyMap()));
expectStoreMessage(txn, contactGroup.getId(), "foo", expectStoreMessage(txn, contactGroup.getId(), "foo",
fooPropertiesDict, 1, true, true); fooPropertiesDict, 1, true, true);
}}); }});
@@ -598,9 +532,9 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
Contact contact = getContact(); Contact contact = getContact();
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION); Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
BdfDictionary oldMetadata = BdfDictionary.of( BdfDictionary oldMetadata = BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "foo"), new BdfEntry("transportId", "foo"),
new BdfEntry(MSG_KEY_VERSION, 1), new BdfEntry("version", 1),
new BdfEntry(MSG_KEY_LOCAL, true) new BdfEntry("local", true)
); );
MessageId localGroupUpdateId = new MessageId(getRandomId()); MessageId localGroupUpdateId = new MessageId(getRandomId());
Map<MessageId, BdfDictionary> localGroupMessageMetadata = Map<MessageId, BdfDictionary> localGroupMessageMetadata =
@@ -655,14 +589,14 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
// The latest update for transport "foo" should be returned // The latest update for transport "foo" should be returned
MessageId fooVersion999 = new MessageId(getRandomId()); MessageId fooVersion999 = new MessageId(getRandomId());
messageMetadata.put(fooVersion999, BdfDictionary.of( messageMetadata.put(fooVersion999, BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "foo"), new BdfEntry("transportId", "foo"),
new BdfEntry(MSG_KEY_VERSION, 999) new BdfEntry("version", 999)
)); ));
// The latest update for transport "bar" should be returned // The latest update for transport "bar" should be returned
MessageId barVersion3 = new MessageId(getRandomId()); MessageId barVersion3 = new MessageId(getRandomId());
messageMetadata.put(barVersion3, BdfDictionary.of( messageMetadata.put(barVersion3, BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, "bar"), new BdfEntry("transportId", "bar"),
new BdfEntry(MSG_KEY_VERSION, 3) new BdfEntry("version", 3)
)); ));
BdfList fooUpdate = BdfList.of("foo", 999, fooPropertiesDict); BdfList fooUpdate = BdfList.of("foo", 999, fooPropertiesDict);
BdfList barUpdate = BdfList.of("bar", 3, barPropertiesDict); BdfList barUpdate = BdfList.of("bar", 3, barPropertiesDict);
@@ -693,9 +627,9 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
Message message = getMessage(g); Message message = getMessage(g);
long timestamp = message.getTimestamp(); long timestamp = message.getTimestamp();
BdfDictionary meta = BdfDictionary.of( BdfDictionary meta = BdfDictionary.of(
new BdfEntry(MSG_KEY_TRANSPORT_ID, transportId), new BdfEntry("transportId", transportId),
new BdfEntry(MSG_KEY_VERSION, version), new BdfEntry("version", version),
new BdfEntry(MSG_KEY_LOCAL, local) new BdfEntry("local", local)
); );
context.checking(new Expectations() {{ context.checking(new Expectations() {{

View File

@@ -4,7 +4,6 @@ import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.sync.Ack; import org.briarproject.bramble.api.sync.Ack;
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;
@@ -24,7 +23,6 @@ import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_IDS;
import static org.briarproject.bramble.test.TestUtils.getContactId; import static org.briarproject.bramble.test.TestUtils.getContactId;
import static org.briarproject.bramble.test.TestUtils.getMessage; import static org.briarproject.bramble.test.TestUtils.getMessage;
import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getTransportId;
public class SimplexOutgoingSessionTest extends BrambleMockTestCase { public class SimplexOutgoingSessionTest extends BrambleMockTestCase {
@@ -38,15 +36,14 @@ public class SimplexOutgoingSessionTest extends BrambleMockTestCase {
private final Executor dbExecutor = new ImmediateExecutor(); private final Executor dbExecutor = new ImmediateExecutor();
private final ContactId contactId = getContactId(); private final ContactId contactId = getContactId();
private final TransportId transportId = getTransportId();
private final Message message = getMessage(new GroupId(getRandomId())); private final Message message = getMessage(new GroupId(getRandomId()));
private final MessageId messageId = message.getId(); private final MessageId messageId = message.getId();
@Test @Test
public void testNothingToSend() throws Exception { public void testNothingToSend() throws Exception {
SimplexOutgoingSession session = new SimplexOutgoingSession(db, SimplexOutgoingSession session = new SimplexOutgoingSession(db,
dbExecutor, eventBus, contactId, transportId, MAX_LATENCY, dbExecutor, eventBus, contactId, MAX_LATENCY, streamWriter,
streamWriter, recordWriter); recordWriter);
Transaction noAckTxn = new Transaction(null, false); Transaction noAckTxn = new Transaction(null, false);
Transaction noMsgTxn = new Transaction(null, false); Transaction noMsgTxn = new Transaction(null, false);
@@ -79,8 +76,8 @@ public class SimplexOutgoingSessionTest extends BrambleMockTestCase {
public void testSomethingToSend() throws Exception { public void testSomethingToSend() throws Exception {
Ack ack = new Ack(singletonList(messageId)); Ack ack = new Ack(singletonList(messageId));
SimplexOutgoingSession session = new SimplexOutgoingSession(db, SimplexOutgoingSession session = new SimplexOutgoingSession(db,
dbExecutor, eventBus, contactId, transportId, MAX_LATENCY, dbExecutor, eventBus, contactId, MAX_LATENCY, streamWriter,
streamWriter, recordWriter); recordWriter);
Transaction ackTxn = new Transaction(null, false); Transaction ackTxn = new Transaction(null, false);
Transaction noAckTxn = new Transaction(null, false); Transaction noAckTxn = new Transaction(null, false);
Transaction msgTxn = new Transaction(null, false); Transaction msgTxn = new Transaction(null, false);

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.sync; package org.briarproject.bramble.sync;
import org.briarproject.bramble.BrambleCoreEagerSingletons;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.TransportCrypto; import org.briarproject.bramble.api.crypto.TransportCrypto;
@@ -73,7 +72,7 @@ public class SyncIntegrationTest extends BrambleTestCase {
SyncIntegrationTestComponent component = SyncIntegrationTestComponent component =
DaggerSyncIntegrationTestComponent.builder().build(); DaggerSyncIntegrationTestComponent.builder().build();
BrambleCoreEagerSingletons.Helper.injectEagerSingletons(component); component.injectBrambleCoreEagerSingletons();
component.inject(this); component.inject(this);
contactId = getContactId(); contactId = getContactId();

View File

@@ -18,6 +18,17 @@ public class BrambleCoreIntegrationTestModule {
@Provides @Provides
FeatureFlags provideFeatureFlags() { FeatureFlags provideFeatureFlags() {
return () -> true; return new FeatureFlags() {
@Override
public boolean shouldEnableImageAttachments() {
return true;
}
@Override
public boolean shouldEnableRemoteContacts() {
return true;
}
};
} }
} }

View File

@@ -1,13 +1,10 @@
package org.briarproject.bramble.test; package org.briarproject.bramble.test;
import org.briarproject.bramble.api.crypto.KeyStrengthener;
import org.briarproject.bramble.api.db.DatabaseConfig; import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.File; import java.io.File;
import javax.annotation.Nullable;
@NotNullByDefault @NotNullByDefault
public class TestDatabaseConfig implements DatabaseConfig { public class TestDatabaseConfig implements DatabaseConfig {
@@ -27,10 +24,4 @@ public class TestDatabaseConfig implements DatabaseConfig {
public File getDatabaseKeyDirectory() { public File getDatabaseKeyDirectory() {
return keyDir; return keyDir;
} }
@Nullable
@Override
public KeyStrengthener getKeyStrengthener() {
return null;
}
} }

View File

@@ -4,7 +4,6 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportConnectionReader; import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter; import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.properties.TransportProperties;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@@ -38,11 +37,6 @@ public class TestDuplexTransportConnection
return writer; return writer;
} }
@Override
public TransportProperties getRemoteProperties() {
return new TransportProperties();
}
/** /**
* Creates and returns a pair of TestDuplexTransportConnections that are * Creates and returns a pair of TestDuplexTransportConnections that are
* connected to each other. * connected to each other.

View File

@@ -60,8 +60,6 @@ public class ClientVersioningManagerImplTest extends BrambleMockTestCase {
private final ClientId clientId = getClientId(); private final ClientId clientId = getClientId();
private final long now = System.currentTimeMillis(); private final long now = System.currentTimeMillis();
private final Transaction txn = new Transaction(null, false); private final Transaction txn = new Transaction(null, false);
private final BdfDictionary groupMeta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_CONTACT_ID, contact.getId().getInt()));
private ClientVersioningManagerImpl createInstance() { private ClientVersioningManagerImpl createInstance() {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
@@ -109,6 +107,8 @@ public class ClientVersioningManagerImplTest extends BrambleMockTestCase {
} }
private void expectAddingContact() throws Exception { private void expectAddingContact() throws Exception {
BdfDictionary groupMeta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_CONTACT_ID, contact.getId().getInt()));
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
BdfList localUpdateBody = BdfList.of(new BdfList(), 1L); BdfList localUpdateBody = BdfList.of(new BdfList(), 1L);
Message localUpdate = getMessage(contactGroup.getId()); Message localUpdate = getMessage(contactGroup.getId());
@@ -459,10 +459,6 @@ public class ClientVersioningManagerImplTest extends BrambleMockTestCase {
// Delete the old remote update // Delete the old remote update
oneOf(db).deleteMessage(txn, oldRemoteUpdateId); oneOf(db).deleteMessage(txn, oldRemoteUpdateId);
oneOf(db).deleteMessageMetadata(txn, oldRemoteUpdateId); oneOf(db).deleteMessageMetadata(txn, oldRemoteUpdateId);
// Get contact ID
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(groupMeta));
// No states or visibilities have changed // No states or visibilities have changed
}}); }});
@@ -492,10 +488,6 @@ public class ClientVersioningManagerImplTest extends BrambleMockTestCase {
// Load the latest local update // Load the latest local update
oneOf(clientHelper).getMessageAsList(txn, oldLocalUpdateId); oneOf(clientHelper).getMessageAsList(txn, oldLocalUpdateId);
will(returnValue(oldLocalUpdateBody)); will(returnValue(oldLocalUpdateBody));
// Get client ID
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(groupMeta));
// No states or visibilities have changed // No states or visibilities have changed
}}); }});

View File

@@ -1,17 +1,15 @@
dependencyVerification { dependencyVerification {
verify = [ verify = [
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861', 'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7', 'com.google.code.findbugs:jsr305:1.3.9:jsr305-1.3.9.jar:905721a0eea90a81534abb7ee6ef4ea2e5e645fa1def0a5cd88402df1b46c9ed',
'com.google.dagger:dagger-compiler:2.24:dagger-compiler-2.24.jar:3c5afb955fb188da485cb2c048eff37dce0e1530b9780a0f2f7187d16d1ccc1f', 'com.google.dagger:dagger-compiler:2.22.1:dagger-compiler-2.22.1.jar:e5f28302cbe70a79d3620cddebfb8ec0736814f3980ffe1e673bfe3342f507d3',
'com.google.dagger:dagger-producers:2.24:dagger-producers-2.24.jar:f10f45b95191954d5d6b043fca9e62fb621d21bf70634b8f8476c7988b504c3a', 'com.google.dagger:dagger-producers:2.22.1:dagger-producers-2.22.1.jar:f834a0082014213a68ff06a0f048d750178d02196c58b0b15beb367d32b97e35',
'com.google.dagger:dagger-spi:2.24:dagger-spi-2.24.jar:c038445d14dbcb4054e61bf49e05009edf26fce4fdc7ec1a9db544784f68e718', 'com.google.dagger:dagger-spi:2.22.1:dagger-spi-2.22.1.jar:4b0b922793b3bcb91b99fabb75dba77c68afd7ae4c5f0c4fd6ba681f0a291c7d',
'com.google.dagger:dagger:2.24:dagger-2.24.jar:550a6e46a6dfcdf1d764887b6090cea94f783327e50e5c73754f18facfc70b64', 'com.google.dagger:dagger:2.22.1:dagger-2.22.1.jar:329d4340f24c4f5717af016c097e90668bfea2a5376e6aa9964b01cef3fd241a',
'com.google.errorprone:error_prone_annotations:2.2.0:error_prone_annotations-2.2.0.jar:6ebd22ca1b9d8ec06d41de8d64e0596981d9607b42035f9ed374f9de271a481a', 'com.google.errorprone:error_prone_annotations:2.1.3:error_prone_annotations-2.1.3.jar:03d0329547c13da9e17c634d1049ea2ead093925e290567e1a364fd6b1fc7ff8',
'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30', 'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30',
'com.google.googlejavaformat:google-java-format:1.5:google-java-format-1.5.jar:aa19ad7850fb85178aa22f2fddb163b84d6ce4d0035872f30d4408195ca1144e', 'com.google.googlejavaformat:google-java-format:1.5:google-java-format-1.5.jar:aa19ad7850fb85178aa22f2fddb163b84d6ce4d0035872f30d4408195ca1144e',
'com.google.guava:failureaccess:1.0.1:failureaccess-1.0.1.jar:a171ee4c734dd2da837e4b16be9df4661afab72a41adaf31eb84dfdaf936ca26', 'com.google.guava:guava:25.0-jre:guava-25.0-jre.jar:3fd4341776428c7e0e5c18a7c10de129475b69ab9d30aeafbb5c277bb6074fa9',
'com.google.guava:guava:27.1-jre:guava-27.1-jre.jar:4a5aa70cc968a4d137e599ad37553e5cfeed2265e8c193476d7119036c536fe7',
'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava:listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar:b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99',
'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:2994a7eb78f2710bd3d3bfb639b2c94e219cedac0d4d084d516e78c16dddecf6', 'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:2994a7eb78f2710bd3d3bfb639b2c94e219cedac0d4d084d516e78c16dddecf6',
'com.h2database:h2:1.4.192:h2-1.4.192.jar:225b22e9857235c46c93861410b60b8c81c10dc8985f4faf188985ba5445126c', 'com.h2database:h2:1.4.192:h2-1.4.192.jar:225b22e9857235c46c93861410b60b8c81c10dc8985f4faf188985ba5445126c',
'com.madgag.spongycastle:core:1.58.0.0:core-1.58.0.0.jar:199617dd5698c5a9312b898c0a4cec7ce9dd8649d07f65d91629f58229d72728', 'com.madgag.spongycastle:core:1.58.0.0:core-1.58.0.0.jar:199617dd5698c5a9312b898c0a4cec7ce9dd8649d07f65d91629f58229d72728',
@@ -20,16 +18,14 @@ dependencyVerification {
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff', 'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a', 'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
'net.i2p.crypto:eddsa:0.2.0:eddsa-0.2.0.jar:a7cb1b85c16e2f0730b9204106929a1d9aaae1df728adc7041a8b8b605692140', 'net.i2p.crypto:eddsa:0.2.0:eddsa-0.2.0.jar:a7cb1b85c16e2f0730b9204106929a1d9aaae1df728adc7041a8b8b605692140',
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d', 'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8', 'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8', 'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
'org.bitlet:weupnp:0.1.4:weupnp-0.1.4.jar:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb', 'org.bitlet:weupnp:0.1.4:weupnp-0.1.4.jar:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
'org.briarproject:jtorctl:0.3:jtorctl-0.3.jar:f2939238a097898998432effe93b0334d97a787972ab3a91a8973a1d309fc864', 'org.briarproject:jtorctl:0.3:jtorctl-0.3.jar:f2939238a097898998432effe93b0334d97a787972ab3a91a8973a1d309fc864',
'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d', 'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d',
'org.checkerframework:checker-qual:2.5.2:checker-qual-2.5.2.jar:64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a',
'org.codehaus.mojo.signature:java16:1.1:java16-1.1.signature:53799223a2c98dba2d0add810bed76315460df285c69e4f397ae6098f87dd619', 'org.codehaus.mojo.signature:java16:1.1:java16-1.1.signature:53799223a2c98dba2d0add810bed76315460df285c69e4f397ae6098f87dd619',
'org.codehaus.mojo:animal-sniffer-annotations:1.17:animal-sniffer-annotations-1.17.jar:92654f493ecfec52082e76354f0ebf87648dc3d5cec2e3c3cdb947c016747a53', 'org.codehaus.mojo:animal-sniffer-annotations:1.14:animal-sniffer-annotations-1.14.jar:2068320bd6bad744c3673ab048f67e30bef8f518996fa380033556600669905d',
'org.codehaus.mojo:animal-sniffer-ant-tasks:1.16:animal-sniffer-ant-tasks-1.16.jar:890040976fbe2d584619a6a61b1fd2e925b3b5eb342a85eb2762c467c0d64e90', 'org.codehaus.mojo:animal-sniffer-ant-tasks:1.16:animal-sniffer-ant-tasks-1.16.jar:890040976fbe2d584619a6a61b1fd2e925b3b5eb342a85eb2762c467c0d64e90',
'org.codehaus.mojo:animal-sniffer:1.16:animal-sniffer-1.16.jar:72be8bcc226ba43b937c722a08a07852bfa1b11400089265d5df0ee7b38b1d52', 'org.codehaus.mojo:animal-sniffer:1.16:animal-sniffer-1.16.jar:72be8bcc226ba43b937c722a08a07852bfa1b11400089265d5df0ee7b38b1d52',
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9', 'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',

View File

@@ -16,10 +16,10 @@ dependencies {
implementation fileTree(dir: 'libs', include: '*.jar') implementation fileTree(dir: 'libs', include: '*.jar')
implementation 'net.java.dev.jna:jna:4.5.2' implementation 'net.java.dev.jna:jna:4.5.2'
implementation 'net.java.dev.jna:jna-platform:4.5.2' implementation 'net.java.dev.jna:jna-platform:4.5.2'
tor 'org.briarproject:tor:0.3.5.10@zip' tor 'org.briarproject:tor:0.3.5.8@zip'
tor 'org.briarproject:obfs4proxy:0.0.7@zip' tor 'org.briarproject:obfs4proxy:0.0.7@zip'
annotationProcessor 'com.google.dagger:dagger-compiler:2.24' annotationProcessor 'com.google.dagger:dagger-compiler:2.22.1'
testImplementation project(path: ':bramble-api', configuration: 'testOutput') testImplementation project(path: ':bramble-api', configuration: 'testOutput')
testImplementation project(path: ':bramble-core', configuration: 'testOutput') testImplementation project(path: ':bramble-core', configuration: 'testOutput')
@@ -28,7 +28,7 @@ dependencies {
testImplementation "org.jmock:jmock-junit4:2.8.2" testImplementation "org.jmock:jmock-junit4:2.8.2"
testImplementation "org.jmock:jmock-legacy:2.8.2" testImplementation "org.jmock:jmock-legacy:2.8.2"
testAnnotationProcessor 'com.google.dagger:dagger-compiler:2.24' testAnnotationProcessor 'com.google.dagger:dagger-compiler:2.22.1'
} }
def torBinariesDir = 'src/main/resources' def torBinariesDir = 'src/main/resources'

View File

@@ -1,7 +1,6 @@
package org.briarproject.bramble.plugin; package org.briarproject.bramble.plugin;
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.lifecycle.IoExecutor; import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.lifecycle.ShutdownManager; import org.briarproject.bramble.api.lifecycle.ShutdownManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -10,7 +9,6 @@ import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory; import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import org.briarproject.bramble.api.reliability.ReliabilityLayerFactory; import org.briarproject.bramble.api.reliability.ReliabilityLayerFactory;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.plugin.bluetooth.JavaBluetoothPluginFactory; import org.briarproject.bramble.plugin.bluetooth.JavaBluetoothPluginFactory;
import org.briarproject.bramble.plugin.modem.ModemPluginFactory; import org.briarproject.bramble.plugin.modem.ModemPluginFactory;
import org.briarproject.bramble.plugin.tcp.LanTcpPluginFactory; import org.briarproject.bramble.plugin.tcp.LanTcpPluginFactory;
@@ -33,11 +31,10 @@ public class DesktopPluginModule extends PluginModule {
PluginConfig getPluginConfig(@IoExecutor Executor ioExecutor, PluginConfig getPluginConfig(@IoExecutor Executor ioExecutor,
SecureRandom random, BackoffFactory backoffFactory, SecureRandom random, BackoffFactory backoffFactory,
ReliabilityLayerFactory reliabilityFactory, ReliabilityLayerFactory reliabilityFactory,
ShutdownManager shutdownManager, EventBus eventBus, Clock clock, ShutdownManager shutdownManager, EventBus eventBus) {
TimeoutMonitor timeoutMonitor) { DuplexPluginFactory bluetooth =
DuplexPluginFactory bluetooth = new JavaBluetoothPluginFactory( new JavaBluetoothPluginFactory(ioExecutor, random, eventBus,
ioExecutor, random, eventBus, clock, timeoutMonitor, backoffFactory);
backoffFactory);
DuplexPluginFactory modem = new ModemPluginFactory(ioExecutor, DuplexPluginFactory modem = new ModemPluginFactory(ioExecutor,
reliabilityFactory); reliabilityFactory);
DuplexPluginFactory lan = new LanTcpPluginFactory(ioExecutor, DuplexPluginFactory lan = new LanTcpPluginFactory(ioExecutor,

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.plugin.bluetooth; package org.briarproject.bramble.plugin.bluetooth;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.Backoff;
@@ -35,11 +34,10 @@ class JavaBluetoothPlugin extends BluetoothPlugin<StreamConnectionNotifier> {
private volatile LocalDevice localDevice = null; private volatile LocalDevice localDevice = null;
JavaBluetoothPlugin(BluetoothConnectionLimiter connectionManager, JavaBluetoothPlugin(BluetoothConnectionLimiter connectionManager,
TimeoutMonitor timeoutMonitor, Executor ioExecutor, Executor ioExecutor, SecureRandom secureRandom,
SecureRandom secureRandom, Backoff backoff, Backoff backoff, PluginCallback callback, int maxLatency) {
PluginCallback callback, int maxLatency, int maxIdleTime) { super(connectionManager, ioExecutor, secureRandom, backoff, callback,
super(connectionManager, timeoutMonitor, ioExecutor, secureRandom, maxLatency);
backoff, callback, maxLatency, maxIdleTime);
} }
@Override @Override
@@ -121,9 +119,7 @@ class JavaBluetoothPlugin extends BluetoothPlugin<StreamConnectionNotifier> {
return "btspp://" + address + ":" + uuid + ";name=RFCOMM"; return "btspp://" + address + ":" + uuid + ";name=RFCOMM";
} }
private DuplexTransportConnection wrapSocket(StreamConnection s) private DuplexTransportConnection wrapSocket(StreamConnection s) {
throws IOException { return new JavaBluetoothTransportConnection(this, connectionLimiter, s);
return new JavaBluetoothTransportConnection(this, connectionLimiter,
timeoutMonitor, s);
} }
} }

View File

@@ -1,7 +1,6 @@
package org.briarproject.bramble.plugin.bluetooth; package org.briarproject.bramble.plugin.bluetooth;
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.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;
@@ -9,7 +8,6 @@ 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.Clock;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@@ -23,27 +21,22 @@ import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
public class JavaBluetoothPluginFactory implements DuplexPluginFactory { public class JavaBluetoothPluginFactory 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; private final Executor ioExecutor;
private final SecureRandom secureRandom; private final SecureRandom secureRandom;
private final EventBus eventBus;
private final Clock clock;
private final TimeoutMonitor timeoutMonitor;
private final BackoffFactory backoffFactory; private final BackoffFactory backoffFactory;
private final EventBus eventBus;
public JavaBluetoothPluginFactory(Executor ioExecutor, public JavaBluetoothPluginFactory(Executor ioExecutor,
SecureRandom secureRandom, EventBus eventBus, Clock clock, SecureRandom secureRandom, EventBus eventBus,
TimeoutMonitor timeoutMonitor, BackoffFactory backoffFactory) { BackoffFactory backoffFactory) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.secureRandom = secureRandom; this.secureRandom = secureRandom;
this.eventBus = eventBus;
this.clock = clock;
this.timeoutMonitor = timeoutMonitor;
this.backoffFactory = backoffFactory; this.backoffFactory = backoffFactory;
this.eventBus = eventBus;
} }
@Override @Override
@@ -59,12 +52,11 @@ public class JavaBluetoothPluginFactory implements DuplexPluginFactory {
@Override @Override
public DuplexPlugin createPlugin(PluginCallback callback) { public DuplexPlugin createPlugin(PluginCallback callback) {
BluetoothConnectionLimiter connectionLimiter = BluetoothConnectionLimiter connectionLimiter =
new BluetoothConnectionLimiterImpl(eventBus, clock); new BluetoothConnectionLimiterImpl();
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);
JavaBluetoothPlugin plugin = new JavaBluetoothPlugin(connectionLimiter, JavaBluetoothPlugin plugin = new JavaBluetoothPlugin(connectionLimiter,
timeoutMonitor, ioExecutor, secureRandom, backoff, callback, ioExecutor, secureRandom, backoff, callback, MAX_LATENCY);
MAX_LATENCY, MAX_IDLE_TIME);
eventBus.addListener(plugin); eventBus.addListener(plugin);
return plugin; return plugin;
} }

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.plugin.bluetooth; package org.briarproject.bramble.plugin.bluetooth;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Plugin; import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.duplex.AbstractDuplexTransportConnection; import org.briarproject.bramble.api.plugin.duplex.AbstractDuplexTransportConnection;
@@ -15,24 +14,20 @@ import javax.microedition.io.StreamConnection;
class JavaBluetoothTransportConnection class JavaBluetoothTransportConnection
extends AbstractDuplexTransportConnection { extends AbstractDuplexTransportConnection {
private final BluetoothConnectionLimiter connectionLimiter; private final BluetoothConnectionLimiter connectionManager;
private final StreamConnection stream; private final StreamConnection stream;
private final InputStream in;
JavaBluetoothTransportConnection(Plugin plugin, JavaBluetoothTransportConnection(Plugin plugin,
BluetoothConnectionLimiter connectionLimiter, BluetoothConnectionLimiter connectionManager,
TimeoutMonitor timeoutMonitor, StreamConnection stream) {
StreamConnection stream) throws IOException {
super(plugin); super(plugin);
this.connectionLimiter = connectionLimiter;
this.stream = stream; this.stream = stream;
in = timeoutMonitor.createTimeoutInputStream( this.connectionManager = connectionManager;
stream.openInputStream(), plugin.getMaxIdleTime() * 2);
} }
@Override @Override
protected InputStream getInputStream() { protected InputStream getInputStream() throws IOException {
return in; return stream.openInputStream();
} }
@Override @Override
@@ -45,7 +40,7 @@ class JavaBluetoothTransportConnection
try { try {
stream.close(); stream.close();
} finally { } finally {
connectionLimiter.connectionClosed(this, exception); connectionManager.connectionClosed(this);
} }
} }
} }

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.plugin.tor; package org.briarproject.bramble.plugin.tor;
import org.briarproject.bramble.BrambleCoreEagerSingletons;
import org.briarproject.bramble.api.battery.BatteryManager; import org.briarproject.bramble.api.battery.BatteryManager;
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.lifecycle.IoExecutor;
@@ -46,7 +45,7 @@ public class BridgeTest extends BrambleTestCase {
public static Iterable<String> data() { public static Iterable<String> data() {
BrambleJavaIntegrationTestComponent component = BrambleJavaIntegrationTestComponent component =
DaggerBrambleJavaIntegrationTestComponent.builder().build(); DaggerBrambleJavaIntegrationTestComponent.builder().build();
BrambleCoreEagerSingletons.Helper.injectEagerSingletons(component); component.injectBrambleCoreEagerSingletons();
return component.getCircumventionProvider().getBridges(false); return component.getCircumventionProvider().getBridges(false);
} }
@@ -91,7 +90,7 @@ public class BridgeTest extends BrambleTestCase {
BrambleJavaIntegrationTestComponent component = BrambleJavaIntegrationTestComponent component =
DaggerBrambleJavaIntegrationTestComponent.builder().build(); DaggerBrambleJavaIntegrationTestComponent.builder().build();
BrambleCoreEagerSingletons.Helper.injectEagerSingletons(component); component.injectBrambleCoreEagerSingletons();
component.inject(this); component.inject(this);
LocationUtils locationUtils = () -> "US"; LocationUtils locationUtils = () -> "US";

View File

@@ -1,17 +1,15 @@
dependencyVerification { dependencyVerification {
verify = [ verify = [
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861', 'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7', 'com.google.code.findbugs:jsr305:1.3.9:jsr305-1.3.9.jar:905721a0eea90a81534abb7ee6ef4ea2e5e645fa1def0a5cd88402df1b46c9ed',
'com.google.dagger:dagger-compiler:2.24:dagger-compiler-2.24.jar:3c5afb955fb188da485cb2c048eff37dce0e1530b9780a0f2f7187d16d1ccc1f', 'com.google.dagger:dagger-compiler:2.22.1:dagger-compiler-2.22.1.jar:e5f28302cbe70a79d3620cddebfb8ec0736814f3980ffe1e673bfe3342f507d3',
'com.google.dagger:dagger-producers:2.24:dagger-producers-2.24.jar:f10f45b95191954d5d6b043fca9e62fb621d21bf70634b8f8476c7988b504c3a', 'com.google.dagger:dagger-producers:2.22.1:dagger-producers-2.22.1.jar:f834a0082014213a68ff06a0f048d750178d02196c58b0b15beb367d32b97e35',
'com.google.dagger:dagger-spi:2.24:dagger-spi-2.24.jar:c038445d14dbcb4054e61bf49e05009edf26fce4fdc7ec1a9db544784f68e718', 'com.google.dagger:dagger-spi:2.22.1:dagger-spi-2.22.1.jar:4b0b922793b3bcb91b99fabb75dba77c68afd7ae4c5f0c4fd6ba681f0a291c7d',
'com.google.dagger:dagger:2.24:dagger-2.24.jar:550a6e46a6dfcdf1d764887b6090cea94f783327e50e5c73754f18facfc70b64', 'com.google.dagger:dagger:2.22.1:dagger-2.22.1.jar:329d4340f24c4f5717af016c097e90668bfea2a5376e6aa9964b01cef3fd241a',
'com.google.errorprone:error_prone_annotations:2.2.0:error_prone_annotations-2.2.0.jar:6ebd22ca1b9d8ec06d41de8d64e0596981d9607b42035f9ed374f9de271a481a', 'com.google.errorprone:error_prone_annotations:2.1.3:error_prone_annotations-2.1.3.jar:03d0329547c13da9e17c634d1049ea2ead093925e290567e1a364fd6b1fc7ff8',
'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30', 'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30',
'com.google.googlejavaformat:google-java-format:1.5:google-java-format-1.5.jar:aa19ad7850fb85178aa22f2fddb163b84d6ce4d0035872f30d4408195ca1144e', 'com.google.googlejavaformat:google-java-format:1.5:google-java-format-1.5.jar:aa19ad7850fb85178aa22f2fddb163b84d6ce4d0035872f30d4408195ca1144e',
'com.google.guava:failureaccess:1.0.1:failureaccess-1.0.1.jar:a171ee4c734dd2da837e4b16be9df4661afab72a41adaf31eb84dfdaf936ca26', 'com.google.guava:guava:25.0-jre:guava-25.0-jre.jar:3fd4341776428c7e0e5c18a7c10de129475b69ab9d30aeafbb5c277bb6074fa9',
'com.google.guava:guava:27.1-jre:guava-27.1-jre.jar:4a5aa70cc968a4d137e599ad37553e5cfeed2265e8c193476d7119036c536fe7',
'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava:listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar:b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99',
'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:2994a7eb78f2710bd3d3bfb639b2c94e219cedac0d4d084d516e78c16dddecf6', 'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:2994a7eb78f2710bd3d3bfb639b2c94e219cedac0d4d084d516e78c16dddecf6',
'com.squareup:javapoet:1.11.1:javapoet-1.11.1.jar:9cbf2107be499ec6e95afd36b58e3ca122a24166cdd375732e51267d64058e90', 'com.squareup:javapoet:1.11.1:javapoet-1.11.1.jar:9cbf2107be499ec6e95afd36b58e3ca122a24166cdd375732e51267d64058e90',
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f', 'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
@@ -19,15 +17,13 @@ dependencyVerification {
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a', 'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
'net.java.dev.jna:jna-platform:4.5.2:jna-platform-4.5.2.jar:f1d00c167d8921c6e23c626ef9f1c3ae0be473c95c68ffa012bc7ae55a87e2d6', 'net.java.dev.jna:jna-platform:4.5.2:jna-platform-4.5.2.jar:f1d00c167d8921c6e23c626ef9f1c3ae0be473c95c68ffa012bc7ae55a87e2d6',
'net.java.dev.jna:jna:4.5.2:jna-4.5.2.jar:0c8eb7acf67261656d79005191debaba3b6bf5dd60a43735a245429381dbecff', 'net.java.dev.jna:jna:4.5.2:jna-4.5.2.jar:0c8eb7acf67261656d79005191debaba3b6bf5dd60a43735a245429381dbecff',
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d', 'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8', 'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8', 'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
'org.briarproject:obfs4proxy:0.0.7:obfs4proxy-0.0.7.zip:5b2f693262ce43a7e130f7cc7d5d1617925330640a2eb6d71085e95df8ee0642', 'org.briarproject:obfs4proxy:0.0.7:obfs4proxy-0.0.7.zip:5b2f693262ce43a7e130f7cc7d5d1617925330640a2eb6d71085e95df8ee0642',
'org.briarproject:tor:0.3.5.10:tor-0.3.5.10.zip:7b387d3523ae8af289c23be59dc4c64ec5d3721385d7825a09705095e3318d5c', 'org.briarproject:tor:0.3.5.8:tor-0.3.5.8.zip:96e83391f01984f28669235fc02fbb0243140a2b3b2c73aeffd0042c8d3ced18',
'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d', 'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d',
'org.checkerframework:checker-qual:2.5.2:checker-qual-2.5.2.jar:64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a', 'org.codehaus.mojo:animal-sniffer-annotations:1.14:animal-sniffer-annotations-1.14.jar:2068320bd6bad744c3673ab048f67e30bef8f518996fa380033556600669905d',
'org.codehaus.mojo:animal-sniffer-annotations:1.17:animal-sniffer-annotations-1.17.jar:92654f493ecfec52082e76354f0ebf87648dc3d5cec2e3c3cdb947c016747a53',
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9', 'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c', 'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c', 'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',

View File

@@ -1,6 +1,6 @@
[main] [main]
host = https://www.transifex.com host = https://www.transifex.com
lang_map = pt_BR: pt-rBR, nb_NO: nb, zh-Hans: zh-rCN, zh-Hant: zh-rTW lang_map = pt_BR: pt-rBR, nb_NO: nb, zh-Hans: zh-rCN
[briar.stringsxml-5] [briar.stringsxml-5]
file_filter = src/main/res/values-<lang>/strings.xml file_filter = src/main/res/values-<lang>/strings.xml

View File

@@ -1,123 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Ebene_1"
x="0px"
y="0px"
viewBox="0 0 320 179.99999"
xml:space="preserve"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="briar-android_tv_artwork_logo_horizontal_black.svg"
width="320"
height="180"
inkscape:export-filename="C:\Users\hughi\Downloads\briar-android_tv_artwork_logo_horizontal_black.png"
inkscape:export-xdpi="95.967941"
inkscape:export-ydpi="95.967941"><metadata
id="metadata71"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs69" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1366"
inkscape:window-height="705"
id="namedview67"
showgrid="false"
inkscape:zoom="2"
inkscape:cx="215.47343"
inkscape:cy="62.929329"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="Ebene_1" /><style
type="text/css"
id="style3">
.st0{display:none;fill:#87C214;}
.st1{fill:#87C214;}
.st2{display:none;fill:#FFFFFF;}
.st3{fill:#95D220;}
.st4{display:none;fill:#95D220;}
</style><rect
style="opacity:1;fill:#ffffff;fill-opacity:0.98492461;stroke:none;stroke-width:0;stroke-miterlimit:1.41420996;stroke-dasharray:none;stroke-opacity:1"
id="rect3747"
width="320"
height="180"
x="0"
y="0" /><rect
style="display:none;fill:#87c214"
id="rect11"
height="43.700001"
width="43.700001"
class="st0"
y="-82.800049"
x="47.200001" /><path
class="st2"
d="m 73.2,-130 c 9.7,0 17.7,8 17.7,17.7 V 87.4 c 0,9.7 -8,17.7 -17.7,17.7 h -8.3 c -9.7,0 -17.7,-8 -17.7,-17.7 v -199.7 c 0,-9.7 7.9,-17.7 17.6,-17.7 h 8.4 m 0,-7 h -8.3 c -13.7,0 -24.7,11.1 -24.7,24.7 V 87.4 c 0,13.6 11.1,24.7 24.7,24.7 h 8.3 c 13.6,0 24.7,-11.1 24.7,-24.7 V -112.3 C 97.8,-125.9 86.8,-137 73.2,-137 Z"
id="path17"
inkscape:connector-curvature="0"
style="display:none;fill:#ffffff" /><rect
style="display:none;fill:#87c214"
id="rect25"
height="43.700001"
width="43.700001"
class="st0"
y="14.199951"
x="144.2" /><path
class="st2"
d="m 170.2,-130 c 9.7,0 17.7,8 17.7,17.7 V 87.4 c 0,9.7 -7.9,17.7 -17.7,17.7 h -8.3 c -9.7,0 -17.7,-8 -17.7,-17.7 v -199.7 c 0,-9.7 8,-17.7 17.7,-17.7 h 8.3 m 0,-7 h -8.3 c -13.6,0 -24.7,11.1 -24.7,24.7 V 87.4 c 0,13.6 11.1,24.7 24.7,24.7 h 8.3 c 13.6,0 24.7,-11.1 24.7,-24.7 v -199.7 c -0.1,-13.6 -11.1,-24.7 -24.7,-24.7 z"
id="path29"
inkscape:connector-curvature="0"
style="display:none;fill:#ffffff" /><g
id="g3745"
transform="matrix(0.65979376,0,0,0.65979376,0,-1020.103)"><path
inkscape:connector-curvature="0"
id="path13"
d="m 64.900391,1565 c -9.7,0 -17.701172,7.9992 -17.701172,17.6992 v 22.5 h 43.601562 v -22.5 c 0,-9.7 -7.901562,-17.6992 -17.601562,-17.6992 z m 96.999999,0 c -9.7,0 -17.70117,7.9992 -17.70117,17.6992 v 119.5 h 43.60156 v -119.5 c 0,-9.7 -7.90156,-17.6992 -17.60156,-17.6992 z m -114.701171,97.8008 v 119.5 c 0,9.7 7.901172,17.6992 17.701172,17.6992 h 8.298828 c 9.7,0 17.701172,-7.9992 17.701172,-17.6992 v -119.5 z m 97.000001,97 v 22.5 c 0,9.7 8.00117,17.6992 17.70117,17.6992 h 8.29883 c 9.7,0 17.70117,-7.9992 17.70117,-17.6992 v -22.5 z"
style="fill:#87c214" /><path
inkscape:connector-curvature="0"
id="path35"
d="M 17.699219,1612.1992 C 7.9992186,1612.1992 0,1620.1004 0,1629.9004 v 8.2988 c 0,9.7 7.8992186,17.7012 17.699219,17.7012 H 137.19922 v -43.7012 z m 177.101561,0 v 43.7012 h 22.5 c 9.7,0 17.69922,-7.9012 17.69922,-17.7012 v -8.2988 c 0,-9.8 -7.99922,-17.7012 -17.69922,-17.7012 z m -177.101561,97 C 7.9992186,1709.1992 0,1717.1004 0,1726.9004 v 8.2988 c 0,9.7 7.8992186,17.7012 17.699219,17.7012 h 22.5 v -43.7012 z m 80.101562,0 v 43.7012 H 217.30078 c 9.7,0 17.69922,-8.0012 17.69922,-17.7012 v -8.2988 c 0,-9.8 -7.99922,-17.7012 -17.69922,-17.7012 z"
style="fill:#95d220" /></g><rect
style="display:none;fill:#95d220"
id="rect37"
height="43.700001"
width="43.700001"
class="st4"
y="14.199951"
x="47.200001" /><path
class="st2"
d="m 217.3,14.2 c 9.7,0 17.7,7.9 17.7,17.7 v 8.3 c 0,9.7 -8,17.7 -17.7,17.7 H 17.7 C 8,57.9 0,49.9 0,40.2 V 31.9 C 0,22.2 7.9,14.2 17.7,14.2 h 199.6 m 0,-7 H 17.7 C 4.1,7.2 -7,18.3 -7,31.9 v 8.3 c 0,13.6 11.1,24.7 24.7,24.7 h 199.7 c 13.6,0 24.7,-11.1 24.7,-24.7 V 31.9 C 242,18.2 230.9,7.2 217.3,7.2 Z"
id="path41"
inkscape:connector-curvature="0"
style="display:none;fill:#ffffff" /><rect
style="display:none;fill:#95d220"
id="rect47"
height="43.700001"
width="43.700001"
class="st4"
y="-82.800049"
x="144.2" /><path
class="st2"
d="m 217.3,-82.8 c 9.7,0 17.7,7.9 17.7,17.7 v 8.3 c 0,9.7 -8,17.7 -17.7,17.7 H 17.7 C 8,-39.1 0,-47 0,-56.8 v -8.3 c 0,-9.7 7.9,-17.7 17.7,-17.7 h 199.6 m 0,-7 H 17.7 c -13.6,0 -24.7,11 -24.7,24.6 v 8.3 c 0,13.6 11.1,24.7 24.7,24.7 h 199.7 c 13.6,0 24.7,-11.1 24.7,-24.7 v -8.3 C 242,-78.8 230.9,-89.8 217.3,-89.8 Z"
id="path53"
inkscape:connector-curvature="0"
style="display:none;fill:#ffffff" /><path
inkscape:connector-curvature="0"
d="m 164.94845,130.5118 v 37.0142 h 17.3299 c 8.17094,0 12.45497,-4.0911 12.45497,-10.4911 0,-4.156 -1.91138,-7.2578 -5.73332,-8.9728 v -0.083 c 2.89934,-1.7808 4.15207,-4.0233 4.15207,-7.521 0,-5.2791 -3.62408,-9.9627 -11.26806,-9.9627 z m 35.97934,0 v 37.0142 h 4.34796 v -14.8454 l -0.45977,-0.4615 h 8.76264 c 4.87623,0 7.64484,1.7133 9.424,5.5433 l 4.61191,9.7636 h 4.87655 l -5.46909,-11.5461 c -1.18614,-2.5738 -3.0974,-4.2227 -4.81061,-4.9483 v -0.083 c 4.0196,-1.0565 7.38013,-4.6856 7.38013,-9.5024 0,-7.3236 -5.66649,-10.9515 -12.45366,-10.9515 z m 36.10959,0 v 37.0142 h 4.34922 v -37.0142 z m 25.56832,0 -16.27714,37.0142 h 4.74504 l 3.95341,-8.9069 -0.19703,-0.4619 h 20.2293 l -0.19713,0.4619 3.9535,8.9069 h 4.74491 l -16.34284,-37.0142 z m 25.82985,0 v 37.0142 h 4.34912 v -14.8454 l -0.4612,-0.4615 h 8.76406 c 4.87643,0 7.64485,1.7133 9.42429,5.5433 l 4.61186,9.7636 H 320 l -5.46903,-11.5461 c -1.18594,-2.5738 -3.09737,-4.2227 -4.81072,-4.9483 v -0.083 c 4.01975,-1.0565 7.38026,-4.6856 7.38026,-9.5024 0,-7.3236 -5.6664,-10.9515 -12.45361,-10.9515 z m -119.59919,4.0908 h 12.58636 c 4.7444,0 7.24733,1.9136 7.24733,5.8723 0,3.2982 -1.97576,5.9394 -7.24733,5.9394 h -12.58636 l 0.46124,-0.4633 v -10.8868 z m 35.97962,0 h 12.32187 c 4.48085,0 7.907,1.8468 7.97296,6.7961 0,3.9585 -3.09676,6.7292 -8.43423,6.7292 h -11.8606 l 0.45977,-0.4614 v -12.6023 z m 87.44177,0 h 12.32223 c 4.54673,0 7.97414,1.8468 7.97414,6.7961 0,3.9585 -3.09683,6.7292 -8.43417,6.7292 h -11.8622 l 0.46141,-0.4614 v -12.6023 z m -27.28106,0.4616 h 0.0674 l 1.11984,3.6954 6.52328,14.8464 0.46124,0.46 h -16.27578 l 0.46131,-0.46 6.52318,-14.8464 z m -96.14033,15.4387 h 13.44199 c 5.46934,0 7.97421,2.5073 7.97421,6.532 0,4.0908 -2.30722,6.401 -7.97421,6.401 h -13.44199 l 0.46124,-0.463 V 150.966 Z"
id="path57"
style="stroke-width:0.65979397" /></svg>

Before

Width:  |  Height:  |  Size: 7.8 KiB

View File

@@ -16,14 +16,14 @@ def getStdout = { command, defaultValue ->
} }
android { android {
compileSdkVersion 29 compileSdkVersion 28
buildToolsVersion '29.0.2' buildToolsVersion '28.0.3'
defaultConfig { defaultConfig {
minSdkVersion 16 minSdkVersion 15
targetSdkVersion 28 targetSdkVersion 26
versionCode 10207 versionCode 10107
versionName "1.2.7" versionName "1.1.7"
applicationId "org.briarproject.briar.android" applicationId "org.briarproject.briar.android"
buildConfigField "String", "GitHash", buildConfigField "String", "GitHash",
"\"${getStdout(['git', 'rev-parse', '--short=7', 'HEAD'], 'No commit hash')}\"" "\"${getStdout(['git', 'rev-parse', '--short=7', 'HEAD'], 'No commit hash')}\""
@@ -31,7 +31,6 @@ android {
buildConfigField "Long", "BuildTimestamp", buildConfigField "Long", "BuildTimestamp",
"${getStdout(['git', 'log', '-n', '1', '--format=%ct'], now)}000L" "${getStdout(['git', 'log', '-n', '1', '--format=%ct'], now)}000L"
testInstrumentationRunner 'org.briarproject.briar.android.BriarTestRunner' testInstrumentationRunner 'org.briarproject.briar.android.BriarTestRunner'
testInstrumentationRunnerArguments disableAnalytics: 'true'
} }
buildTypes { buildTypes {
@@ -83,8 +82,6 @@ android {
warning 'MissingTranslation' warning 'MissingTranslation'
warning 'ImpliedQuantity' warning 'ImpliedQuantity'
warning 'ExtraTranslation' warning 'ExtraTranslation'
// FIXME
warning 'InvalidPackage'
} }
} }
@@ -93,61 +90,66 @@ dependencies {
implementation project(path: ':bramble-core', configuration: 'default') implementation project(path: ':bramble-core', configuration: 'default')
implementation project(':bramble-android') implementation project(':bramble-android')
implementation 'androidx.preference:preference:1.1.0' def supportVersion = '28.0.0'
implementation 'androidx.exifinterface:exifinterface:1.0.0' implementation "com.android.support:support-v4:$supportVersion"
implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0' implementation("com.android.support:appcompat-v7:$supportVersion") {
implementation 'androidx.constraintlayout:constraintlayout:1.1.3' exclude module: 'support-v4'
implementation 'com.google.android.material:material:1.1.0-beta01' }
implementation 'androidx.recyclerview:recyclerview-selection:1.1.0-rc01' implementation("com.android.support:preference-v14:$supportVersion") {
exclude module: 'support-v4'
}
implementation("com.android.support:design:$supportVersion") {
exclude module: 'support-v4'
exclude module: 'recyclerview-v7'
}
implementation "com.android.support:cardview-v7:$supportVersion"
implementation "com.android.support:support-annotations:$supportVersion"
implementation "com.android.support:exifinterface:$supportVersion"
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation "android.arch.lifecycle:extensions:1.1.1"
implementation 'ch.acra:acra:4.11' implementation('ch.acra:acra:4.11') {
implementation 'info.guardianproject.panic:panic:1.0' exclude module: 'support-v4'
exclude module: 'support-annotations'
}
implementation 'info.guardianproject.panic:panic:0.5'
implementation 'info.guardianproject.trustedintents:trustedintents:0.2' implementation 'info.guardianproject.trustedintents:trustedintents:0.2'
implementation 'de.hdodenhof:circleimageview:3.0.1' implementation 'de.hdodenhof:circleimageview:2.2.0'
implementation 'com.google.zxing:core:3.3.3' // newer version need minSdk 24 implementation 'com.google.zxing:core:3.3.3'
implementation 'uk.co.samuelwall:material-tap-target-prompt:3.0.0' implementation 'uk.co.samuelwall:material-tap-target-prompt:2.14.0'
implementation 'com.vanniktech:emoji-google:0.6.0' implementation 'com.vanniktech:emoji-google:0.5.1'
implementation 'com.github.kobakei:MaterialFabSpeedDial:1.2.1' implementation 'com.github.kobakei:MaterialFabSpeedDial:1.2.1' // later versions already use androidx
implementation 'com.github.chrisbanes:PhotoView:2.3.0' def glideVersion = '4.9.0'
def glideVersion = '4.10.0'
implementation("com.github.bumptech.glide:glide:$glideVersion") { implementation("com.github.bumptech.glide:glide:$glideVersion") {
exclude group: 'com.android.support' exclude group: 'com.android.support'
exclude module: 'disklrucache' // when there's no disk cache, we can't accidentally use it exclude module: 'disklrucache' // when there's no disk cache, we can't accidentally use it
} }
implementation 'com.github.chrisbanes:PhotoView:2.1.4' // later versions already use androidx
annotationProcessor 'com.google.dagger:dagger-compiler:2.24' annotationProcessor 'com.google.dagger:dagger-compiler:2.22.1'
annotationProcessor "com.github.bumptech.glide:compiler:$glideVersion" annotationProcessor "com.github.bumptech.glide:compiler:$glideVersion"
compileOnly 'javax.annotation:jsr250-api:1.0' compileOnly 'javax.annotation:jsr250-api:1.0'
def espressoVersion = '3.2.0'
def jmockVersion = '2.8.2'
testImplementation project(path: ':bramble-api', configuration: 'testOutput') testImplementation project(path: ':bramble-api', configuration: 'testOutput')
testImplementation project(path: ':bramble-core', configuration: 'testOutput') testImplementation project(path: ':bramble-core', configuration: 'testOutput')
testImplementation 'androidx.test:runner:1.2.0' testImplementation 'org.robolectric:robolectric:4.0.1'
testImplementation 'androidx.test.ext:junit:1.1.1' testImplementation 'org.robolectric:shadows-support-v4:3.3.2'
testImplementation 'androidx.fragment:fragment-testing:1.1.0' testImplementation 'org.mockito:mockito-core:2.25.0'
testImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
testImplementation 'org.robolectric:robolectric:4.3.1'
testImplementation 'org.mockito:mockito-core:3.1.0'
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'
testImplementation "org.jmock:jmock:$jmockVersion" testImplementation "org.jmock:jmock:2.8.2"
testImplementation "org.jmock:jmock-junit4:$jmockVersion" testImplementation "org.jmock:jmock-junit4:2.8.2"
testImplementation "org.jmock:jmock-legacy:$jmockVersion" testImplementation "org.jmock:jmock-legacy:2.8.2"
androidTestImplementation project(path: ':bramble-api', configuration: 'testOutput') def espressoVersion = '3.0.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation "com.android.support.test.espresso:espresso-core:$espressoVersion"
androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion" androidTestImplementation "com.android.support.test.espresso:espresso-contrib:$espressoVersion"
androidTestImplementation "androidx.test.espresso:espresso-contrib:$espressoVersion" androidTestImplementation "com.android.support.test.espresso:espresso-intents:$espressoVersion"
androidTestImplementation "androidx.test.espresso:espresso-intents:$espressoVersion" androidTestAnnotationProcessor "com.google.dagger:dagger-compiler:2.22.1"
androidTestAnnotationProcessor "com.google.dagger:dagger-compiler:2.24"
androidTestCompileOnly 'javax.annotation:jsr250-api:1.0' androidTestCompileOnly 'javax.annotation:jsr250-api:1.0'
androidTestImplementation 'junit:junit:4.12' androidTestImplementation 'junit:junit:4.12'
androidTestScreenshotImplementation('tools.fastlane:screengrab:1.2.0') { androidTestScreenshotImplementation "tools.fastlane:screengrab:1.2.0"
// workaround for jetifier issue https://issuetracker.google.com/issues/123060356 androidTestScreenshotImplementation "com.android.support.test.uiautomator:uiautomator-v18:2.1.3"
exclude group: 'com.android.support.test.uiautomator'
}
androidTestScreenshotImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'
} }
task verifyTranslations { task verifyTranslations {

View File

@@ -5,10 +5,6 @@
# QR codes # QR codes
-keep class com.google.zxing.Result -keep class com.google.zxing.Result
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
# RSS libraries # RSS libraries
-keep,includedescriptorclasses class com.rometools.rome.feed.synd.impl.** { *; } -keep,includedescriptorclasses class com.rometools.rome.feed.synd.impl.** { *; }

View File

@@ -1,8 +1,8 @@
package org.briarproject.briar.android; package org.briarproject.briar.android;
import org.briarproject.bramble.BrambleAndroidEagerSingletons; import org.briarproject.bramble.BrambleAndroidModule;
import org.briarproject.bramble.BrambleCoreEagerSingletons; import org.briarproject.bramble.BrambleCoreModule;
import org.briarproject.briar.BriarCoreEagerSingletons; import org.briarproject.briar.BriarCoreModule;
public class BriarTestComponentApplication extends BriarApplicationImpl { public class BriarTestComponentApplication extends BriarApplicationImpl {
@@ -12,10 +12,10 @@ public class BriarTestComponentApplication extends BriarApplicationImpl {
.appModule(new AppModule(this)).build(); .appModule(new AppModule(this)).build();
// We need to load the eager singletons directly after making the // We need to load the eager singletons directly after making the
// dependency graphs // dependency graphs
BrambleCoreEagerSingletons.Helper.injectEagerSingletons(component); BrambleCoreModule.initEagerSingletons(component);
BrambleAndroidEagerSingletons.Helper.injectEagerSingletons(component); BrambleAndroidModule.initEagerSingletons(component);
BriarCoreEagerSingletons.Helper.injectEagerSingletons(component); BriarCoreModule.initEagerSingletons(component);
AndroidEagerSingletons.Helper.injectEagerSingletons(component); AndroidEagerSingletons.initEagerSingletons(component);
return component; return component;
} }

View File

@@ -2,8 +2,7 @@ package org.briarproject.briar.android;
import android.app.Application; import android.app.Application;
import android.content.Context; import android.content.Context;
import android.support.test.runner.AndroidJUnitRunner;
import androidx.test.runner.AndroidJUnitRunner;
public class BriarTestRunner extends AndroidJUnitRunner { public class BriarTestRunner extends AndroidJUnitRunner {

View File

@@ -2,6 +2,7 @@ package org.briarproject.briar.android;
import android.app.Activity; import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.support.test.espresso.intent.rule.IntentsTestRule;
import org.briarproject.bramble.api.account.AccountManager; import org.briarproject.bramble.api.account.AccountManager;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
@@ -11,16 +12,13 @@ import org.briarproject.briar.R;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import androidx.test.espresso.intent.rule.IntentsTestRule; import static android.support.test.InstrumentationRegistry.getTargetContext;
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
public abstract class UiTest { public abstract class UiTest {
protected final String USERNAME = protected final String USERNAME =
getApplicationContext().getString(R.string.screenshot_alice); getTargetContext().getString(R.string.screenshot_alice);
protected static final String PASSWORD = "123456"; protected static final String PASSWORD = "123456";
@Inject @Inject
@@ -29,7 +27,9 @@ public abstract class UiTest {
protected LifecycleManager lifecycleManager; protected LifecycleManager lifecycleManager;
public UiTest() { public UiTest() {
BriarTestComponentApplication app = getApplicationContext(); BriarTestComponentApplication app =
(BriarTestComponentApplication) getTargetContext()
.getApplicationContext();
inject((BriarUiTestComponent) app.getApplicationComponent()); inject((BriarUiTestComponent) app.getApplicationComponent());
} }
@@ -64,8 +64,8 @@ public abstract class UiTest {
accountManager.createAccount(USERNAME, PASSWORD); accountManager.createAccount(USERNAME, PASSWORD);
if (runnable != null) { if (runnable != null) {
Intent serviceIntent = Intent serviceIntent =
new Intent(getApplicationContext(), BriarService.class); new Intent(getTargetContext(), BriarService.class);
getApplicationContext().startService(serviceIntent); getTargetContext().startService(serviceIntent);
try { try {
lifecycleManager.waitForStartup(); lifecycleManager.waitForStartup();
} catch (InterruptedException e) { } catch (InterruptedException e) {

View File

@@ -1,22 +1,21 @@
package org.briarproject.briar.android; package org.briarproject.briar.android;
import android.app.Activity; import android.app.Activity;
import android.support.test.espresso.PerformException;
import android.support.test.espresso.UiController;
import android.support.test.espresso.ViewAction;
import android.support.test.runner.lifecycle.ActivityLifecycleMonitor;
import android.support.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
import android.support.test.runner.lifecycle.Stage;
import android.view.View; import android.view.View;
import org.hamcrest.Matcher; import org.hamcrest.Matcher;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import androidx.test.espresso.PerformException; import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import androidx.test.espresso.UiController; import static android.support.test.espresso.util.HumanReadables.describe;
import androidx.test.espresso.ViewAction; import static android.support.test.espresso.util.TreeIterables.breadthFirstViewTraversal;
import androidx.test.runner.lifecycle.ActivityLifecycleMonitor;
import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
import androidx.test.runner.lifecycle.Stage;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.util.HumanReadables.describe;
import static androidx.test.espresso.util.TreeIterables.breadthFirstViewTraversal;
import static java.lang.System.currentTimeMillis; import static java.lang.System.currentTimeMillis;
import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.concurrent.TimeUnit.SECONDS;

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