Compare commits

..

1 Commits

Author SHA1 Message Date
akwizgran
e1502e6fab Disable image attachments for remote contacts alpha. 2019-06-28 14:22:35 +01:00
907 changed files with 8931 additions and 24289 deletions

View File

@@ -17,7 +17,7 @@ test:
script:
- ./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:
# these file change every time but should not be cached

View File

@@ -1,7 +1,16 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<option name="RIGHT_MARGIN" value="100" />
<AndroidXmlCodeStyleSettings>
<option name="USE_CUSTOM_SETTINGS" value="true" />
</AndroidXmlCodeStyleSettings>
<JavaCodeStyleSettings>
<option name="ANNOTATION_PARAMETER_WRAP" value="1" />
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
<option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
<value />
</option>
<option name="IMPORT_LAYOUT_TABLE">
<value>
<package name="android" withSubpackages="true" static="false" />
@@ -28,20 +37,6 @@
<option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false" />
</JavaCodeStyleSettings>
<JetCodeStyleSettings>
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
<value />
</option>
<option name="PACKAGES_IMPORT_LAYOUT">
<value>
<package name="" alias="false" withSubpackages="true" />
<package name="java" alias="false" withSubpackages="true" />
<package name="javax" alias="false" withSubpackages="true" />
<package name="kotlin" alias="false" withSubpackages="true" />
<package name="" alias="true" withSubpackages="true" />
</value>
</option>
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<XML>
@@ -82,6 +77,7 @@
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="USE_TAB_CHARACTER" value="true" />
@@ -94,8 +90,7 @@
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
<XML_NAMESPACE>Namespace:</XML_NAMESPACE>
</AND>
</match>
</rule>
@@ -105,8 +100,7 @@
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
<XML_NAMESPACE>Namespace:</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
@@ -117,7 +111,6 @@
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
@@ -128,7 +121,6 @@
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
@@ -139,7 +131,6 @@
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
@@ -150,7 +141,6 @@
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
@@ -161,7 +151,6 @@
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
@@ -172,12 +161,64 @@
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<NAME>.*:layout_width</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</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>
</section>
<section>
@@ -185,7 +226,6 @@
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>

View File

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

View File

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

View File

@@ -5,31 +5,23 @@ apply plugin: 'witness'
apply from: 'witness.gradle'
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
compileSdkVersion 28
buildToolsVersion '28.0.3'
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode rootProject.ext.versionCode
versionName rootProject.ext.versionName
minSdkVersion 14
targetSdkVersion 26
versionCode 10107
versionName "1.1.7"
consumerProguardFiles 'proguard-rules.txt'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
lintOptions {
// FIXME
warning "LintError"
warning "InvalidPackage"
warning "MissingPermission"
warning "InlinedApi", "ObsoleteSdkInt", "Override", "NewApi", "UnusedAttribute"
}
}
configurations {
@@ -38,10 +30,10 @@ configurations {
dependencies {
implementation project(path: ':bramble-core', configuration: 'default')
tor 'org.briarproject:tor-android:0.3.5.10@zip'
tor 'org.briarproject:obfs4proxy-android:0.0.11-2@zip'
tor 'org.briarproject:tor-android:0.3.5.8@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'
@@ -53,12 +45,10 @@ dependencies {
}
def torBinariesDir = 'src/main/res/raw'
def torLibsDir = 'src/main/jniLibs'
task cleanTorBinaries {
doLast {
delete fileTree(torBinariesDir) { include '*.zip' }
delete fileTree(torLibsDir) { include '**/*.so' }
}
}
@@ -69,36 +59,6 @@ task unpackTorBinaries {
copy {
from configurations.tor.collect { zipTree(it) }
into torBinariesDir
include 'geoip.zip'
}
configurations.tor.each { outer ->
zipTree(outer).each { inner ->
if (inner.name.endsWith('_arm_pie.zip')) {
copy {
from zipTree(inner)
into torLibsDir
rename '(.*)', 'armeabi-v7a/lib$1.so'
}
} else if (inner.name.endsWith('_arm64_pie.zip')) {
copy {
from zipTree(inner)
into torLibsDir
rename '(.*)', 'arm64-v8a/lib$1.so'
}
} else if (inner.name.endsWith('_x86_pie.zip')) {
copy {
from zipTree(inner)
into torLibsDir
rename '(.*)', 'x86/lib$1.so'
}
} else if (inner.name.endsWith('_x86_64_pie.zip')) {
copy {
from zipTree(inner)
into torLibsDir
rename '(.*)', 'x86_64/lib$1.so'
}
}
}
}
}
dependsOn cleanTorBinaries
@@ -106,6 +66,5 @@ task unpackTorBinaries {
tasks.withType(MergeResources) {
inputs.dir torBinariesDir
inputs.dir torLibsDir
dependsOn unpackTorBinaries
}

View File

@@ -16,8 +16,6 @@
android:label="@string/app_name"
android:supportsRtl="true">
<receiver android:name=".system.AlarmReceiver" />
</application>
</manifest>

View File

@@ -11,14 +11,4 @@ public interface BrambleAndroidEagerSingletons {
void inject(AndroidNetworkModule.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

@@ -6,8 +6,6 @@ import org.briarproject.bramble.plugin.tor.CircumventionModule;
import org.briarproject.bramble.reporting.ReportingModule;
import org.briarproject.bramble.socks.SocksModule;
import org.briarproject.bramble.system.AndroidSystemModule;
import org.briarproject.bramble.system.AndroidTaskSchedulerModule;
import org.briarproject.bramble.system.AndroidWakefulIoExecutorModule;
import dagger.Module;
@@ -15,11 +13,15 @@ import dagger.Module;
AndroidBatteryModule.class,
AndroidNetworkModule.class,
AndroidSystemModule.class,
AndroidTaskSchedulerModule.class,
AndroidWakefulIoExecutorModule.class,
CircumventionModule.class,
ReportingModule.class,
SocksModule.class
})
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

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

View File

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

View File

@@ -12,16 +12,13 @@ import org.briarproject.bramble.api.identity.IdentityManager;
import java.io.File;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import javax.inject.Inject;
import static android.os.Build.VERSION.SDK_INT;
import static java.util.Arrays.asList;
import static java.util.logging.Level.INFO;
import static org.briarproject.bramble.util.IoUtils.deleteFileOrDir;
import static org.briarproject.bramble.util.LogUtils.logFileOrDir;
@@ -32,11 +29,7 @@ class AndroidAccountManager extends AccountManagerImpl
private static final Logger LOG =
Logger.getLogger(AndroidAccountManager.class.getName());
/**
* 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");
private static final String PREF_DB_KEY = "key";
protected final Context appContext;
private final SharedPreferences prefs;
@@ -60,6 +53,36 @@ class AndroidAccountManager extends AccountManagerImpl
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
public void deleteAccount() {
synchronized (stateChangeLock) {
@@ -82,14 +105,14 @@ class AndroidAccountManager extends AccountManagerImpl
return PreferenceManager.getDefaultSharedPreferences(appContext);
}
@GuardedBy("stateChangeLock")
// Locking: stateChangeLock
private void deleteAppData(SharedPreferences... clear) {
// Clear and commit shared preferences
for (SharedPreferences prefs : clear) {
if (!prefs.edit().clear().commit())
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<>();
File dataDir = getDataDir();
@Nullable
@@ -98,12 +121,14 @@ class AndroidAccountManager extends AccountManagerImpl
LOG.warning("Could not list files in app data dir");
} else {
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(appContext.getFilesDir());
files.add(appContext.getCacheDir());
addIfNotNull(files, appContext.getExternalCacheDir());
if (SDK_INT >= 19) {
for (File file : appContext.getExternalCacheDirs()) {
@@ -115,16 +140,12 @@ class AndroidAccountManager extends AccountManagerImpl
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) {
if (LOG.isLoggable(INFO)) {
LOG.info("Deleting " + file.getAbsolutePath());
}
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() {

View File

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

View File

@@ -1,19 +0,0 @@
package org.briarproject.bramble.api.system;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
public interface AndroidWakeLock {
/**
* Acquires the wake lock. This has no effect if the wake lock has already
* been acquired.
*/
void acquire();
/**
* Releases the wake lock. This has no effect if the wake lock has already
* been released.
*/
void release();
}

View File

@@ -1,38 +0,0 @@
package org.briarproject.bramble.api.system;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.concurrent.Executor;
@NotNullByDefault
public interface AndroidWakeLockManager {
/**
* Creates a wake lock with the given tag. The tag is only used for
* logging; the underlying OS wake lock will use its own tag.
*/
AndroidWakeLock createWakeLock(String tag);
/**
* Runs the given task while holding a wake lock.
*/
void runWakefully(Runnable r, String tag);
/**
* Submits the given task to the given executor while holding a wake lock.
* The lock is released when the task completes, or if an exception is
* thrown while submitting or running the task.
*/
void executeWakefully(Runnable r, Executor executor, String tag);
/**
* Starts a dedicated thread to run the given task asynchronously. A wake
* lock is acquired before starting the thread and released when the task
* completes, or if an exception is thrown while starting the thread or
* running the task.
* <p>
* This method should only be used for lifecycle management tasks that
* can't be run on an executor.
*/
void executeWakefully(Runnable r, String tag);
}

View File

@@ -9,17 +9,16 @@ import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventExecutor;
import org.briarproject.bramble.api.lifecycle.Service;
import org.briarproject.bramble.api.network.NetworkManager;
import org.briarproject.bramble.api.network.NetworkStatus;
import org.briarproject.bramble.api.network.event.NetworkStatusEvent;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.system.TaskScheduler;
import org.briarproject.bramble.api.system.TaskScheduler.Cancellable;
import org.briarproject.bramble.api.system.Scheduler;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
@@ -33,7 +32,6 @@ import static android.content.Intent.ACTION_SCREEN_OFF;
import static android.content.Intent.ACTION_SCREEN_ON;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.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.PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED;
import static java.util.concurrent.TimeUnit.MINUTES;
@@ -51,22 +49,20 @@ class AndroidNetworkManager implements NetworkManager, Service {
private static final String WIFI_AP_STATE_CHANGED_ACTION =
"android.net.wifi.WIFI_AP_STATE_CHANGED";
private final TaskScheduler scheduler;
private final ScheduledExecutorService scheduler;
private final EventBus eventBus;
private final Executor eventExecutor;
private final Context appContext;
private final AtomicReference<Cancellable> connectivityCheck =
private final AtomicReference<Future<?>> connectivityCheck =
new AtomicReference<>();
private final AtomicBoolean used = new AtomicBoolean(false);
private volatile BroadcastReceiver networkStateReceiver = null;
@Inject
AndroidNetworkManager(TaskScheduler scheduler, EventBus eventBus,
@EventExecutor Executor eventExecutor, Application app) {
AndroidNetworkManager(@Scheduler ScheduledExecutorService scheduler,
EventBus eventBus, Application app) {
this.scheduler = scheduler;
this.eventBus = eventBus;
this.eventExecutor = eventExecutor;
this.appContext = app.getApplicationContext();
}
@@ -80,9 +76,9 @@ class AndroidNetworkManager implements NetworkManager, Service {
filter.addAction(ACTION_SCREEN_ON);
filter.addAction(ACTION_SCREEN_OFF);
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);
appContext.registerReceiver(networkStateReceiver, filter);
}
@Override
@@ -107,12 +103,11 @@ class AndroidNetworkManager implements NetworkManager, Service {
}
private void scheduleConnectionStatusUpdate(int delay, TimeUnit unit) {
Cancellable newConnectivityCheck =
scheduler.schedule(this::updateConnectionStatus, eventExecutor,
delay, unit);
Cancellable oldConnectivityCheck =
Future<?> newConnectivityCheck =
scheduler.schedule(this::updateConnectionStatus, delay, unit);
Future<?> oldConnectivityCheck =
connectivityCheck.getAndSet(newConnectivityCheck);
if (oldConnectivityCheck != null) oldConnectivityCheck.cancel();
if (oldConnectivityCheck != null) oldConnectivityCheck.cancel(false);
}
private class NetworkStateReceiver extends BroadcastReceiver {
@@ -141,8 +136,7 @@ class AndroidNetworkManager implements NetworkManager, Service {
}
private boolean isApEvent(@Nullable String action) {
return WIFI_AP_STATE_CHANGED_ACTION.equals(action) ||
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action);
return WIFI_AP_STATE_CHANGED_ACTION.equals(action);
}
}
}

View File

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

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.plugin.bluetooth;
import android.app.Application;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
@@ -25,6 +24,7 @@ import java.io.IOException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
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_ON;
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.os.Build.VERSION.SDK_INT;
import static java.util.Collections.shuffle;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
@@ -59,8 +56,7 @@ import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
class AndroidBluetoothPlugin
extends BluetoothPlugin<BluetoothSocket, BluetoothServerSocket> {
class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
private static final Logger LOG =
getLogger(AndroidBluetoothPlugin.class.getName());
@@ -68,31 +64,23 @@ class AndroidBluetoothPlugin
private static final int MAX_DISCOVERY_MS = 10_000;
private final AndroidExecutor androidExecutor;
private final Application app;
private final Context appContext;
private final Clock clock;
private volatile boolean wasEnabledByUs = false;
private volatile BluetoothStateReceiver receiver = null;
// Non-null if the plugin started successfully
private volatile BluetoothAdapter adapter = null;
AndroidBluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
BluetoothConnectionFactory<BluetoothSocket> connectionFactory,
Executor ioExecutor,
Executor wakefulIoExecutor,
SecureRandom secureRandom,
AndroidExecutor androidExecutor,
Application app,
Clock clock,
Backoff backoff,
PluginCallback callback,
int maxLatency,
int maxIdleTime) {
super(connectionLimiter, connectionFactory, ioExecutor,
wakefulIoExecutor, secureRandom, backoff, callback,
maxLatency, maxIdleTime);
Executor ioExecutor, AndroidExecutor androidExecutor,
Context appContext, SecureRandom secureRandom, Clock clock,
Backoff backoff, PluginCallback callback, int maxLatency) {
super(connectionLimiter, ioExecutor, secureRandom, backoff, callback,
maxLatency);
this.androidExecutor = androidExecutor;
this.app = app;
this.appContext = appContext;
this.clock = clock;
}
@@ -104,13 +92,13 @@ class AndroidBluetoothPlugin
filter.addAction(ACTION_STATE_CHANGED);
filter.addAction(ACTION_SCAN_MODE_CHANGED);
receiver = new BluetoothStateReceiver();
app.registerReceiver(receiver, filter);
appContext.registerReceiver(receiver, filter);
}
@Override
public void stop() {
super.stop();
if (receiver != null) app.unregisterReceiver(receiver);
if (receiver != null) appContext.unregisterReceiver(receiver);
}
@Override
@@ -132,11 +120,36 @@ class AndroidBluetoothPlugin
return adapter != null && adapter.isEnabled();
}
@Override
void enableAdapter() {
if (adapter != null && !adapter.isEnabled()) {
if (adapter.enable()) {
LOG.info("Enabling Bluetooth");
wasEnabledByUs = true;
} else {
LOG.info("Could not enable Bluetooth");
}
}
}
@Override
void disableAdapterIfEnabledByUs() {
if (isAdapterEnabled() && wasEnabledByUs) {
if (adapter.disable()) LOG.info("Disabling Bluetooth");
else LOG.info("Could not disable Bluetooth");
wasEnabledByUs = false;
}
}
@Override
void setEnabledByUs() {
wasEnabledByUs = true;
}
@Override
@Nullable
String getBluetoothAddress() {
if (adapter == null) return null;
String address = AndroidUtils.getBluetoothAddress(app, adapter);
String address = AndroidUtils.getBluetoothAddress(appContext, adapter);
return address.isEmpty() ? null : address;
}
@@ -154,7 +167,12 @@ class AndroidBluetoothPlugin
@Override
DuplexTransportConnection acceptConnection(BluetoothServerSocket ss)
throws IOException {
return connectionFactory.wrapSocket(this, ss.accept());
return wrapSocket(ss.accept());
}
private DuplexTransportConnection wrapSocket(BluetoothSocket s) {
return new AndroidBluetoothTransportConnection(this,
connectionLimiter, s);
}
@Override
@@ -171,7 +189,7 @@ class AndroidBluetoothPlugin
try {
s = d.createInsecureRfcommSocketToServiceRecord(u);
s.connect();
return connectionFactory.wrapSocket(this, s);
return wrapSocket(s);
} catch (IOException e) {
IoUtils.tryToClose(s, LOG, WARNING);
throw e;
@@ -206,7 +224,7 @@ class AndroidBluetoothPlugin
filter.addAction(ACTION_DISCOVERY_STARTED);
filter.addAction(ACTION_DISCOVERY_FINISHED);
filter.addAction(ACTION_FOUND);
app.registerReceiver(receiver, filter);
appContext.registerReceiver(receiver, filter);
try {
if (adapter.startDiscovery()) {
long now = clock.currentTimeMillis();
@@ -222,15 +240,11 @@ class AndroidBluetoothPlugin
break;
} else if (ACTION_FOUND.equals(action)) {
BluetoothDevice d = i.getParcelableExtra(EXTRA_DEVICE);
// Ignore Bluetooth LE devices
if (SDK_INT < 18 || d.getType() != DEVICE_TYPE_LE) {
String address = d.getAddress();
if (LOG.isLoggable(INFO))
LOG.info("Discovered " +
scrubMacAddress(address));
if (!addresses.contains(address))
addresses.add(address);
}
String address = d.getAddress();
if (LOG.isLoggable(INFO))
LOG.info("Discovered " + scrubMacAddress(address));
if (!addresses.contains(address))
addresses.add(address);
}
now = clock.currentTimeMillis();
}
@@ -243,10 +257,10 @@ class AndroidBluetoothPlugin
} finally {
LOG.info("Cancelling discovery");
adapter.cancelDiscovery();
app.unregisterReceiver(receiver);
appContext.unregisterReceiver(receiver);
}
// Shuffle the addresses so we don't always try the same one first
shuffle(addresses);
Collections.shuffle(addresses);
return addresses;
}

View File

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

View File

@@ -2,48 +2,32 @@ package org.briarproject.bramble.plugin.bluetooth;
import android.bluetooth.BluetoothSocket;
import org.briarproject.bramble.api.io.TimeoutMonitor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.duplex.AbstractDuplexTransportConnection;
import org.briarproject.bramble.api.system.AndroidWakeLock;
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_ADDRESS;
import static org.briarproject.bramble.util.AndroidUtils.isValidBluetoothAddress;
@NotNullByDefault
class AndroidBluetoothTransportConnection
extends AbstractDuplexTransportConnection {
private final BluetoothConnectionLimiter connectionLimiter;
private final BluetoothConnectionLimiter connectionManager;
private final BluetoothSocket socket;
private final InputStream in;
private final AndroidWakeLock wakeLock;
AndroidBluetoothTransportConnection(Plugin plugin,
BluetoothConnectionLimiter connectionLimiter,
AndroidWakeLockManager wakeLockManager,
TimeoutMonitor timeoutMonitor,
BluetoothSocket socket) throws IOException {
BluetoothConnectionLimiter connectionManager,
BluetoothSocket socket) {
super(plugin);
this.connectionLimiter = connectionLimiter;
this.connectionManager = connectionManager;
this.socket = socket;
in = timeoutMonitor.createTimeoutInputStream(
socket.getInputStream(), plugin.getMaxIdleTime() * 2);
wakeLock = wakeLockManager.createWakeLock("BluetoothConnection");
wakeLock.acquire();
String address = socket.getRemoteDevice().getAddress();
if (isValidBluetoothAddress(address)) remote.put(PROP_ADDRESS, address);
}
@Override
protected InputStream getInputStream() {
return in;
protected InputStream getInputStream() throws IOException {
return socket.getInputStream();
}
@Override
@@ -55,10 +39,8 @@ class AndroidBluetoothTransportConnection
protected void closeConnection(boolean exception) throws IOException {
try {
socket.close();
in.close();
} finally {
wakeLock.release();
connectionLimiter.connectionClosed(this);
connectionManager.connectionClosed(this);
}
}
}

View File

@@ -1,32 +1,25 @@
package org.briarproject.bramble.plugin.tcp;
import android.annotation.TargetApi;
import android.app.Application;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import org.briarproject.bramble.PoliteExecutor;
import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.network.event.NetworkStatusEvent;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.settings.Settings;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.List;
import java.util.Collection;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
@@ -35,26 +28,31 @@ import javax.net.SocketFactory;
import static android.content.Context.CONNECTIVITY_SERVICE;
import static android.content.Context.WIFI_SERVICE;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.os.Build.VERSION.SDK_INT;
import static java.util.Collections.emptyList;
import static java.util.Collections.list;
import static java.util.Collections.singletonList;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
import static org.briarproject.bramble.api.plugin.LanTcpConstants.DEFAULT_PREF_PLUGIN_ENABLE;
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE;
import static org.briarproject.bramble.util.IoUtils.tryToClose;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
class AndroidLanTcpPlugin extends LanTcpPlugin {
class AndroidLanTcpPlugin extends LanTcpPlugin implements EventListener {
private static final Logger LOG =
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 ConnectivityManager connectivityManager;
@Nullable
@@ -62,160 +60,53 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
private volatile SocketFactory socketFactory;
AndroidLanTcpPlugin(Executor ioExecutor,
Executor wakefulIoExecutor,
Application app,
Backoff backoff,
PluginCallback callback,
int maxLatency,
int maxIdleTime,
int connectionTimeout) {
super(ioExecutor, wakefulIoExecutor, backoff, callback, maxLatency,
maxIdleTime, connectionTimeout);
AndroidLanTcpPlugin(Executor ioExecutor, Context appContext,
Backoff backoff, PluginCallback callback, int maxLatency,
int maxIdleTime) {
super(ioExecutor, backoff, callback, maxLatency, maxIdleTime);
// Don't execute more than one connection status check at a time
connectionStatusExecutor =
new PoliteExecutor("AndroidLanTcpPlugin", ioExecutor, 1);
connectivityManager = (ConnectivityManager)
requireNonNull(app.getSystemService(CONNECTIVITY_SERVICE));
wifiManager = (WifiManager) app.getSystemService(WIFI_SERVICE);
ConnectivityManager connectivityManager = (ConnectivityManager)
appContext.getSystemService(CONNECTIVITY_SERVICE);
if (connectivityManager == null) throw new AssertionError();
this.connectivityManager = connectivityManager;
wifiManager = (WifiManager) appContext.getApplicationContext()
.getSystemService(WIFI_SERVICE);
socketFactory = SocketFactory.getDefault();
}
@Override
public void start() {
if (used.getAndSet(true)) throw new IllegalStateException();
initialisePortProperty();
Settings settings = callback.getSettings();
state.setStarted(settings.getBoolean(PREF_PLUGIN_ENABLE,
DEFAULT_PREF_PLUGIN_ENABLE));
running = true;
updateConnectionStatus();
}
@Override
public void stop() {
running = false;
tryToClose(socket);
}
@Override
protected Socket createSocket() throws IOException {
return socketFactory.createSocket();
}
@Override
protected List<InetAddress> getUsableLocalInetAddresses(boolean ipv4) {
InetAddress addr = getWifiAddress(ipv4);
return addr == null ? emptyList() : singletonList(addr);
}
@Nullable
private InetAddress getWifiAddress(boolean ipv4) {
Pair<InetAddress, Boolean> wifi = getWifiIpv4Address();
if (ipv4) return wifi == null ? null : wifi.getFirst();
// If there's no wifi IPv4 address, we might be a client on an
// IPv6-only wifi network. We can only detect this on API 21+
if (wifi == null) {
return SDK_INT >= 21 ? getWifiClientIpv6Address() : null;
}
// Use the wifi IPv4 address to determine which interface's IPv6
// address we should return (if the interface has a suitable address)
return getIpv6AddressForInterface(wifi.getFirst());
}
/**
* Returns a {@link Pair} where the first element is the IPv4 address of
* the wifi interface and the second element is true if this device is
* providing an access point, or false if this device is a client. Returns
* null if this device isn't connected to wifi as an access point or client.
*/
@Nullable
private Pair<InetAddress, Boolean> getWifiIpv4Address() {
if (wifiManager == null) return null;
// If we're connected to a wifi network, return its address
protected Collection<InetAddress> getLocalIpAddresses() {
// If the device doesn't have wifi, don't open any sockets
if (wifiManager == null) return emptyList();
// If we're connected to a wifi network, use that network
WifiInfo info = wifiManager.getConnectionInfo();
if (info != null && info.getIpAddress() != 0) {
return new Pair<>(intToInetAddress(info.getIpAddress()), false);
}
List<InterfaceAddress> ifAddrs = getLocalInterfaceAddresses();
// If we're providing a normal access point, return its address
for (InterfaceAddress ifAddr : ifAddrs) {
if (isAndroidWifiApAddress(ifAddr)) {
return new Pair<>(ifAddr.getAddress(), true);
}
}
// If we're providing a wifi direct access point, return its address
for (InterfaceAddress ifAddr : ifAddrs) {
if (isAndroidWifiDirectApAddress(ifAddr)) {
return new Pair<>(ifAddr.getAddress(), true);
}
}
// Not connected to wifi
return null;
}
/**
* Returns true if the given address belongs to a network provided by an
* Android access point (including the access point's own address).
* <p>
* The access point's address is usually 192.168.43.1, but at least one
* device (Honor 8A) may use other addresses in the range 192.168.43.0/24.
*/
private boolean isAndroidWifiApAddress(InterfaceAddress ifAddr) {
if (ifAddr.getNetworkPrefixLength() != 24) return false;
byte[] ip = ifAddr.getAddress().getAddress();
return ip.length == 4
&& ip[0] == (byte) 192
&& ip[1] == (byte) 168
&& ip[2] == (byte) 43;
}
/**
* Returns true if the given address belongs to a network provided by an
* Android wifi direct legacy mode access point (including the access
* point's own address).
*/
private boolean isAndroidWifiDirectApAddress(InterfaceAddress ifAddr) {
if (ifAddr.getNetworkPrefixLength() != 24) return false;
byte[] ip = ifAddr.getAddress().getAddress();
return ip.length == 4
&& ip[0] == (byte) 192
&& ip[1] == (byte) 168
&& ip[2] == (byte) 49;
}
/**
* Returns a link-local IPv6 address for the wifi client interface, or null
* if there's no such interface or it doesn't have a suitable address.
*/
@TargetApi(21)
@Nullable
private InetAddress getWifiClientIpv6Address() {
for (Network net : connectivityManager.getAllNetworks()) {
NetworkCapabilities caps =
connectivityManager.getNetworkCapabilities(net);
if (caps == null || !caps.hasTransport(TRANSPORT_WIFI)) continue;
LinkProperties props = connectivityManager.getLinkProperties(net);
if (props == null) continue;
for (LinkAddress linkAddress : props.getLinkAddresses()) {
InetAddress addr = linkAddress.getAddress();
if (isIpv6LinkLocalAddress(addr)) return addr;
}
}
return null;
}
/**
* Returns a link-local IPv6 address for the interface with the given IPv4
* address, or null if the interface doesn't have a suitable address.
*/
@Nullable
private InetAddress getIpv6AddressForInterface(InetAddress ipv4) {
try {
NetworkInterface iface = NetworkInterface.getByInetAddress(ipv4);
if (iface == null) return null;
for (InetAddress addr : list(iface.getInetAddresses())) {
if (isIpv6LinkLocalAddress(addr)) return addr;
}
// No suitable address
return null;
} catch (SocketException e) {
logException(LOG, WARNING, e);
return null;
}
if (info != null && info.getIpAddress() != 0)
return singletonList(intToInetAddress(info.getIpAddress()));
// If we're running an access point, return its address
if (super.getLocalIpAddresses().contains(WIFI_AP_ADDRESS))
return singletonList(WIFI_AP_ADDRESS);
// No suitable addresses
return emptyList();
}
private InetAddress intToInetAddress(int ip) {
@@ -237,11 +128,9 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
private SocketFactory getSocketFactory() {
if (SDK_INT < 21) return SocketFactory.getDefault();
for (Network net : connectivityManager.getAllNetworks()) {
NetworkCapabilities caps =
connectivityManager.getNetworkCapabilities(net);
if (caps != null && caps.hasTransport(TRANSPORT_WIFI)) {
NetworkInfo info = connectivityManager.getNetworkInfo(net);
if (info != null && info.getType() == TYPE_WIFI)
return net.getSocketFactory();
}
}
LOG.warning("Could not find suitable socket factory");
return SocketFactory.getDefault();
@@ -249,59 +138,30 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
@Override
public void eventOccurred(Event e) {
super.eventOccurred(e);
if (e instanceof NetworkStatusEvent) updateConnectionStatus();
}
private void updateConnectionStatus() {
connectionStatusExecutor.execute(() -> {
State s = getState();
if (s != ACTIVE && s != INACTIVE) return;
Pair<InetAddress, Boolean> wifi = getPreferredWifiAddress();
if (wifi == null) {
LOG.info("Not connected to wifi");
socketFactory = SocketFactory.getDefault();
// Server sockets may not have been closed automatically when
// interface was taken down. If any sockets are open, closing
// them here will cause the sockets to be cleared and the state
// to be updated in acceptContactConnections()
if (s == ACTIVE) {
LOG.info("Closing server sockets");
tryToClose(state.getServerSocket(true), LOG, WARNING);
tryToClose(state.getServerSocket(false), LOG, WARNING);
}
} else if (wifi.getSecond()) {
if (!running) return;
Collection<InetAddress> addrs = getLocalIpAddresses();
if (addrs.contains(WIFI_AP_ADDRESS)) {
LOG.info("Providing wifi hotspot");
// There's no corresponding Network object and thus no way
// to get a suitable socket factory, so we won't be able to
// make outgoing connections on API 21+ if another network
// has internet access
socketFactory = SocketFactory.getDefault();
bind();
if (socket == null || socket.isClosed()) bind();
} else if (addrs.isEmpty()) {
LOG.info("Not connected to wifi");
socketFactory = SocketFactory.getDefault();
tryToClose(socket);
} else {
LOG.info("Connected to wifi");
socketFactory = getSocketFactory();
bind();
if (socket == null || socket.isClosed()) bind();
}
});
}
/**
* Returns a {@link Pair} where the first element is an IP address (IPv4 if
* available, otherwise IPv6) of the wifi interface and the second element
* is true if this device is providing an access point, or false if this
* device is a client. Returns null if this device isn't connected to wifi
* as an access point or client.
*/
@Nullable
private Pair<InetAddress, Boolean> getPreferredWifiAddress() {
Pair<InetAddress, Boolean> wifi = getWifiIpv4Address();
// If there's no wifi IPv4 address, we might be a client on an
// IPv6-only wifi network. We can only detect this on API 21+
if (wifi == null && SDK_INT >= 21) {
InetAddress ipv6 = getWifiClientIpv6Address();
if (ipv6 != null) return new Pair<>(ipv6, false);
}
return wifi;
}
}

View File

@@ -1,9 +1,8 @@
package org.briarproject.bramble.plugin.tcp;
import android.app.Application;
import android.content.Context;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.BackoffFactory;
@@ -11,12 +10,10 @@ import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.system.WakefulIoExecutor;
import java.util.concurrent.Executor;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID;
@@ -24,29 +21,23 @@ import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID;
@NotNullByDefault
public class AndroidLanTcpPluginFactory implements DuplexPluginFactory {
private static final int MAX_LATENCY = 30_000; // 30 seconds
private static final int MAX_IDLE_TIME = 30_000; // 30 seconds
private static final int CONNECTION_TIMEOUT = 3_000; // 3 seconds
private static final int MIN_POLLING_INTERVAL = 60_000; // 1 minute
private static final int MAX_POLLING_INTERVAL = 600_000; // 10 mins
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 MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins
private static final double BACKOFF_BASE = 1.2;
private final Executor ioExecutor, wakefulIoExecutor;
private final Executor ioExecutor;
private final EventBus eventBus;
private final BackoffFactory backoffFactory;
private final Application app;
private final Context appContext;
@Inject
public AndroidLanTcpPluginFactory(@IoExecutor Executor ioExecutor,
@WakefulIoExecutor Executor wakefulIoExecutor,
EventBus eventBus,
BackoffFactory backoffFactory,
Application app) {
public AndroidLanTcpPluginFactory(Executor ioExecutor, EventBus eventBus,
BackoffFactory backoffFactory, Context appContext) {
this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor;
this.eventBus = eventBus;
this.backoffFactory = backoffFactory;
this.app = app;
this.appContext = appContext;
}
@Override
@@ -64,8 +55,7 @@ public class AndroidLanTcpPluginFactory implements DuplexPluginFactory {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE);
AndroidLanTcpPlugin plugin = new AndroidLanTcpPlugin(ioExecutor,
wakefulIoExecutor, app, backoff, callback,
MAX_LATENCY, MAX_IDLE_TIME, CONNECTION_TIMEOUT);
appContext, backoff, callback, MAX_LATENCY, MAX_IDLE_TIME);
eventBus.addListener(plugin);
return plugin;
}

View File

@@ -1,9 +1,10 @@
package org.briarproject.bramble.plugin.tor;
import android.app.Application;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.PowerManager;
import org.briarproject.bramble.api.battery.BatteryManager;
import org.briarproject.bramble.api.network.NetworkManager;
@@ -11,76 +12,48 @@ import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.system.AndroidWakeLock;
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.ResourceProvider;
import org.briarproject.bramble.util.AndroidUtils;
import org.briarproject.bramble.util.RenewableWakeLock;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.concurrent.ScheduledExecutorService;
import javax.net.SocketFactory;
import static android.os.Build.VERSION.SDK_INT;
import static java.util.Arrays.asList;
import static java.util.logging.Level.INFO;
import static java.util.logging.Logger.getLogger;
import static android.content.Context.MODE_PRIVATE;
import static android.content.Context.POWER_SERVICE;
import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
import static java.util.concurrent.TimeUnit.MINUTES;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
class AndroidTorPlugin extends TorPlugin {
private static final List<String> LIBRARY_ARCHITECTURES =
asList("armeabi-v7a", "arm64-v8a", "x86", "x86_64");
private final Context appContext;
private final RenewableWakeLock wakeLock;
private static final String TOR_LIB_NAME = "libtor.so";
private static final String OBFS4_LIB_NAME = "libobfs4proxy.so";
private static final Logger LOG =
getLogger(AndroidTorPlugin.class.getName());
private final Application app;
private final AndroidWakeLock wakeLock;
private final File torLib, obfs4Lib;
AndroidTorPlugin(Executor ioExecutor,
Executor wakefulIoExecutor,
Application app,
NetworkManager networkManager,
LocationUtils locationUtils,
SocketFactory torSocketFactory,
Clock clock,
ResourceProvider resourceProvider,
AndroidTorPlugin(Executor ioExecutor, ScheduledExecutorService scheduler,
Context appContext, NetworkManager networkManager,
LocationUtils locationUtils, SocketFactory torSocketFactory,
Clock clock, ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider,
BatteryManager batteryManager,
AndroidWakeLockManager wakeLockManager,
Backoff backoff,
BatteryManager batteryManager, Backoff backoff,
TorRendezvousCrypto torRendezvousCrypto,
PluginCallback callback,
String architecture,
int maxLatency,
int maxIdleTime,
File torDirectory) {
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
torSocketFactory, clock, resourceProvider,
circumventionProvider, batteryManager, backoff,
torRendezvousCrypto, callback, architecture, maxLatency,
maxIdleTime, torDirectory);
this.app = app;
wakeLock = wakeLockManager.createWakeLock("TorPlugin");
String nativeLibDir = app.getApplicationInfo().nativeLibraryDir;
torLib = new File(nativeLibDir, TOR_LIB_NAME);
obfs4Lib = new File(nativeLibDir, OBFS4_LIB_NAME);
PluginCallback callback, String architecture, int maxLatency,
int maxIdleTime) {
super(ioExecutor, networkManager, locationUtils, torSocketFactory,
clock, resourceProvider, circumventionProvider, batteryManager,
backoff, torRendezvousCrypto, callback, architecture, maxLatency, maxIdleTime,
appContext.getDir("tor", MODE_PRIVATE));
this.appContext = appContext;
PowerManager pm = (PowerManager)
appContext.getSystemService(POWER_SERVICE);
if (pm == null) throw new AssertionError();
wakeLock = new RenewableWakeLock(pm, scheduler, PARTIAL_WAKE_LOCK,
getWakeLockTag(), 1, MINUTES);
}
@Override
@@ -91,8 +64,8 @@ class AndroidTorPlugin extends TorPlugin {
@Override
protected long getLastUpdateTime() {
try {
PackageManager pm = app.getPackageManager();
PackageInfo pi = pm.getPackageInfo(app.getPackageName(), 0);
PackageManager pm = appContext.getPackageManager();
PackageInfo pi = pm.getPackageInfo(appContext.getPackageName(), 0);
return pi.lastUpdateTime;
} catch (NameNotFoundException e) {
throw new AssertionError(e);
@@ -101,6 +74,7 @@ class AndroidTorPlugin extends TorPlugin {
@Override
protected void enableNetwork(boolean enable) throws IOException {
if (!running) return;
if (enable) wakeLock.acquire();
super.enableNetwork(enable);
if (!enable) wakeLock.release();
@@ -112,111 +86,16 @@ class AndroidTorPlugin extends TorPlugin {
wakeLock.release();
}
@Override
protected File getTorExecutableFile() {
return torLib.exists() ? torLib : super.getTorExecutableFile();
}
@Override
protected File getObfs4ExecutableFile() {
return obfs4Lib.exists() ? obfs4Lib : super.getObfs4ExecutableFile();
}
@Override
protected void installTorExecutable() throws IOException {
File extracted = super.getTorExecutableFile();
if (torLib.exists()) {
// If an older version left behind a Tor binary, delete it
if (extracted.exists()) {
if (extracted.delete()) LOG.info("Deleted Tor binary");
else LOG.info("Failed to delete Tor binary");
}
} else if (SDK_INT < 29) {
// The binary wasn't extracted at install time. Try to extract it
extractLibraryFromApk(TOR_LIB_NAME, extracted);
} else {
// No point extracting the binary, we won't be allowed to execute it
throw new FileNotFoundException(torLib.getAbsolutePath());
}
}
@Override
protected void installObfs4Executable() throws IOException {
File extracted = super.getObfs4ExecutableFile();
if (obfs4Lib.exists()) {
// If an older version left behind an obfs4 binary, delete it
if (extracted.exists()) {
if (extracted.delete()) LOG.info("Deleted obfs4 binary");
else LOG.info("Failed to delete obfs4 binary");
}
} else if (SDK_INT < 29) {
// The binary wasn't extracted at install time. Try to extract it
extractLibraryFromApk(OBFS4_LIB_NAME, extracted);
} else {
// No point extracting the binary, we won't be allowed to execute it
throw new FileNotFoundException(obfs4Lib.getAbsolutePath());
}
}
private void extractLibraryFromApk(String libName, File dest)
throws IOException {
File sourceDir = new File(app.getApplicationInfo().sourceDir);
if (sourceDir.isFile()) {
// Look for other APK files in the same directory, if we're allowed
File parent = sourceDir.getParentFile();
if (parent != null) sourceDir = parent;
}
List<String> libPaths = getSupportedLibraryPaths(libName);
for (File apk : findApkFiles(sourceDir)) {
ZipInputStream zin = new ZipInputStream(new FileInputStream(apk));
for (ZipEntry e = zin.getNextEntry(); e != null;
e = zin.getNextEntry()) {
if (libPaths.contains(e.getName())) {
if (LOG.isLoggable(INFO)) {
LOG.info("Extracting " + e.getName()
+ " from " + apk.getAbsolutePath());
}
extract(zin, dest); // Zip input stream will be closed
return;
}
}
zin.close();
}
throw new FileNotFoundException(libName);
}
/**
* Returns all files with the extension .apk or .APK under the given root.
*/
private List<File> findApkFiles(File root) {
List<File> files = new ArrayList<>();
findApkFiles(root, files);
return files;
}
private void findApkFiles(File f, List<File> files) {
if (f.isFile() && f.getName().toLowerCase().endsWith(".apk")) {
files.add(f);
} else if (f.isDirectory()) {
File[] children = f.listFiles();
if (children != null) {
for (File child : children) findApkFiles(child, files);
private String getWakeLockTag() {
PackageManager pm = appContext.getPackageManager();
for (PackageInfo info : pm.getInstalledPackages(0)) {
String name = info.packageName.toLowerCase();
if (name.startsWith("com.huawei.powergenie")) {
return "LocationManagerService";
} else if (name.startsWith("com.evenwell.powermonitor")) {
return "AudioIn";
}
}
}
/**
* Returns the paths at which libraries with the given name would be found
* inside an APK file, for all architectures supported by the device, in
* order of preference.
*/
private List<String> getSupportedLibraryPaths(String libName) {
List<String> architectures = new ArrayList<>();
for (String abi : AndroidUtils.getSupportedArchitectures()) {
if (LIBRARY_ARCHITECTURES.contains(abi)) {
architectures.add("lib/" + abi + "/" + libName);
}
}
return architectures;
return getClass().getSimpleName();
}
}

View File

@@ -1,33 +1,29 @@
package org.briarproject.bramble.plugin.tor;
import android.app.Application;
import android.content.Context;
import android.os.Build;
import org.briarproject.bramble.api.battery.BatteryManager;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.network.NetworkManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.BackoffFactory;
import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.TorConstants;
import org.briarproject.bramble.api.plugin.TorDirectory;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.ResourceProvider;
import org.briarproject.bramble.api.system.WakefulIoExecutor;
import org.briarproject.bramble.util.AndroidUtils;
import java.io.File;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.logging.Logger;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import javax.net.SocketFactory;
@Immutable
@@ -43,8 +39,9 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins
private static final double BACKOFF_BASE = 1.2;
private final Executor ioExecutor, wakefulIoExecutor;
private final Application app;
private final Executor ioExecutor;
private final ScheduledExecutorService scheduler;
private final Context appContext;
private final NetworkManager networkManager;
private final LocationUtils locationUtils;
private final EventBus eventBus;
@@ -53,28 +50,18 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
private final ResourceProvider resourceProvider;
private final CircumventionProvider circumventionProvider;
private final BatteryManager batteryManager;
private final AndroidWakeLockManager wakeLockManager;
private final Clock clock;
private final File torDirectory;
@Inject
public AndroidTorPluginFactory(@IoExecutor Executor ioExecutor,
@WakefulIoExecutor Executor wakefulIoExecutor,
Application app,
NetworkManager networkManager,
LocationUtils locationUtils,
EventBus eventBus,
SocketFactory torSocketFactory,
BackoffFactory backoffFactory,
ResourceProvider resourceProvider,
public AndroidTorPluginFactory(Executor ioExecutor,
ScheduledExecutorService scheduler, Context appContext,
NetworkManager networkManager, LocationUtils locationUtils,
EventBus eventBus, SocketFactory torSocketFactory,
BackoffFactory backoffFactory, ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider,
BatteryManager batteryManager,
AndroidWakeLockManager wakeLockManager,
Clock clock,
@TorDirectory File torDirectory) {
BatteryManager batteryManager, Clock clock) {
this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor;
this.app = app;
this.scheduler = scheduler;
this.appContext = appContext;
this.networkManager = networkManager;
this.locationUtils = locationUtils;
this.eventBus = eventBus;
@@ -83,9 +70,7 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
this.resourceProvider = resourceProvider;
this.circumventionProvider = circumventionProvider;
this.batteryManager = batteryManager;
this.wakeLockManager = wakeLockManager;
this.clock = clock;
this.torDirectory = torDirectory;
}
@Override
@@ -104,15 +89,9 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
// Check that we have a Tor binary for this architecture
String architecture = null;
for (String abi : AndroidUtils.getSupportedArchitectures()) {
if (abi.startsWith("x86_64")) {
architecture = "x86_64";
break;
} else if (abi.startsWith("x86")) {
if (abi.startsWith("x86")) {
architecture = "x86";
break;
} else if (abi.startsWith("arm64")) {
architecture = "arm64";
break;
} else if (abi.startsWith("armeabi")) {
architecture = "arm";
break;
@@ -122,18 +101,17 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
LOG.info("Tor is not supported on this architecture");
return null;
}
// Use position-independent executable
architecture += "_pie";
// Use position-independent executable for SDK >= 16
if (Build.VERSION.SDK_INT >= 16) architecture += "_pie";
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE);
TorRendezvousCrypto torRendezvousCrypto = new TorRendezvousCryptoImpl();
AndroidTorPlugin plugin = new AndroidTorPlugin(ioExecutor,
wakefulIoExecutor, app, networkManager, locationUtils,
torSocketFactory, clock, resourceProvider,
circumventionProvider, batteryManager, wakeLockManager,
AndroidTorPlugin plugin = new AndroidTorPlugin(ioExecutor, scheduler,
appContext, networkManager, locationUtils, torSocketFactory,
clock, resourceProvider, circumventionProvider, batteryManager,
backoff, torRendezvousCrypto, callback, architecture,
MAX_LATENCY, MAX_IDLE_TIME, torDirectory);
MAX_LATENCY, MAX_IDLE_TIME);
eventBus.addListener(plugin);
return plugin;
}

View File

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

View File

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

View File

@@ -61,12 +61,12 @@ class AndroidLocationUtils implements LocationUtils {
private String getCountryFromPhoneNetwork() {
Object o = appContext.getSystemService(TELEPHONY_SERVICE);
TelephonyManager tm = (TelephonyManager) o;
return tm == null ? "" : tm.getNetworkCountryIso();
return tm.getNetworkCountryIso();
}
private String getCountryFromSimCard() {
Object o = appContext.getSystemService(TELEPHONY_SERVICE);
TelephonyManager tm = (TelephonyManager) o;
return tm == null ? "" : tm.getSimCountryIso();
return tm.getSimCountryIso();
}
}

View File

@@ -23,7 +23,6 @@ import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static android.content.Context.WIFI_SERVICE;
import static android.os.Build.VERSION.SDK_INT;
import static android.provider.Settings.Secure.ANDROID_ID;
@Immutable
@@ -75,7 +74,8 @@ class AndroidSecureRandomProvider extends UnixSecureRandomProvider {
// Silence strict mode
StrictMode.ThreadPolicy tp = StrictMode.allowThreadDiskWrites();
super.writeSeed();
if (SDK_INT <= 18) applyOpenSslFix();
if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT <= 18)
applyOpenSslFix();
StrictMode.setThreadPolicy(tp);
}

View File

@@ -1,17 +1,12 @@
package org.briarproject.bramble.system;
import org.briarproject.bramble.api.event.EventExecutor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.ResourceProvider;
import org.briarproject.bramble.api.system.SecureRandomProvider;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import javax.inject.Singleton;
@@ -21,23 +16,6 @@ import dagger.Provides;
@Module
public class AndroidSystemModule {
private final ScheduledExecutorService scheduledExecutorService;
public AndroidSystemModule() {
// Discard tasks that are submitted during shutdown
RejectedExecutionHandler policy =
new ScheduledThreadPoolExecutor.DiscardPolicy();
scheduledExecutorService = new ScheduledThreadPoolExecutor(1, policy);
}
@Provides
@Singleton
ScheduledExecutorService provideScheduledExecutorService(
LifecycleManager lifecycleManager) {
lifecycleManager.registerForShutdown(scheduledExecutorService);
return scheduledExecutorService;
}
@Provides
@Singleton
SecureRandomProvider provideSecureRandomProvider(
@@ -69,11 +47,4 @@ public class AndroidSystemModule {
ResourceProvider provideResourceProvider(AndroidResourceProvider provider) {
return provider;
}
@Provides
@Singleton
AndroidWakeLockManager provideWakeLockManager(
AndroidWakeLockManagerImpl wakeLockManager) {
return wakeLockManager;
}
}

View File

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

View File

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

View File

@@ -1,74 +0,0 @@
package org.briarproject.bramble.system;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.AndroidWakeLock;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
import static java.util.logging.Level.FINE;
import static java.util.logging.Logger.getLogger;
/**
* A wrapper around a {@link SharedWakeLock} that provides the more convenient
* semantics of {@link AndroidWakeLock} (i.e. calls to acquire() and release()
* don't need to be balanced).
*/
@ThreadSafe
@NotNullByDefault
class AndroidWakeLockImpl implements AndroidWakeLock {
private static final Logger LOG =
getLogger(AndroidWakeLockImpl.class.getName());
private static final AtomicInteger INSTANCE_ID = new AtomicInteger(0);
private final SharedWakeLock sharedWakeLock;
private final String tag;
private final Object lock = new Object();
@GuardedBy("lock")
private boolean held = false;
AndroidWakeLockImpl(SharedWakeLock sharedWakeLock, String tag) {
this.sharedWakeLock = sharedWakeLock;
this.tag = tag + "_" + INSTANCE_ID.getAndIncrement();
}
@Override
public void acquire() {
synchronized (lock) {
if (held) {
if (LOG.isLoggable(FINE)) {
LOG.fine(tag + " already acquired");
}
} else {
if (LOG.isLoggable(FINE)) {
LOG.fine(tag + " acquiring shared wake lock");
}
held = true;
sharedWakeLock.acquire();
}
}
}
@Override
public void release() {
synchronized (lock) {
if (held) {
if (LOG.isLoggable(FINE)) {
LOG.fine(tag + " releasing shared wake lock");
}
held = false;
sharedWakeLock.release();
} else {
if (LOG.isLoggable(FINE)) {
LOG.fine(tag + " already released");
}
}
}
}
}

View File

@@ -1,119 +0,0 @@
package org.briarproject.bramble.system;
import android.app.Application;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.PowerManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.AndroidWakeLock;
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import javax.inject.Inject;
import static android.content.Context.POWER_SERVICE;
import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
@NotNullByDefault
class AndroidWakeLockManagerImpl implements AndroidWakeLockManager {
/**
* How often to replace the wake lock.
*/
private static final long LOCK_DURATION_MS = MINUTES.toMillis(1);
/**
* Automatically release the lock this many milliseconds after it's due
* to have been replaced and released.
*/
private static final long SAFETY_MARGIN_MS = SECONDS.toMillis(30);
private final SharedWakeLock sharedWakeLock;
@Inject
AndroidWakeLockManagerImpl(Application app,
ScheduledExecutorService scheduledExecutorService) {
PowerManager powerManager = (PowerManager)
requireNonNull(app.getSystemService(POWER_SERVICE));
String tag = getWakeLockTag(app);
sharedWakeLock = new RenewableWakeLock(powerManager,
scheduledExecutorService, PARTIAL_WAKE_LOCK, tag,
LOCK_DURATION_MS, SAFETY_MARGIN_MS);
}
@Override
public AndroidWakeLock createWakeLock(String tag) {
return new AndroidWakeLockImpl(sharedWakeLock, tag);
}
@Override
public void runWakefully(Runnable r, String tag) {
AndroidWakeLock wakeLock = createWakeLock(tag);
wakeLock.acquire();
try {
r.run();
} finally {
wakeLock.release();
}
}
@Override
public void executeWakefully(Runnable r, Executor executor, String tag) {
AndroidWakeLock wakeLock = createWakeLock(tag);
wakeLock.acquire();
try {
executor.execute(() -> {
try {
r.run();
} finally {
// Release the wake lock if the task throws an exception
wakeLock.release();
}
});
} catch (Exception e) {
// Release the wake lock if the executor throws an exception when
// we submit the task (in which case the release() call above won't
// happen)
wakeLock.release();
throw e;
}
}
@Override
public void executeWakefully(Runnable r, String tag) {
AndroidWakeLock wakeLock = createWakeLock(tag);
wakeLock.acquire();
try {
new Thread(() -> {
try {
r.run();
} finally {
wakeLock.release();
}
}).start();
} catch (Exception e) {
wakeLock.release();
throw e;
}
}
private String getWakeLockTag(Context ctx) {
PackageManager pm = ctx.getPackageManager();
for (PackageInfo info : pm.getInstalledPackages(0)) {
String name = info.packageName.toLowerCase();
if (name.startsWith("com.huawei.powergenie")) {
return "LocationManagerService";
} else if (name.startsWith("com.evenwell.powermonitor")) {
return "AudioIn";
}
}
return ctx.getPackageName();
}
}

View File

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

View File

@@ -1,130 +0,0 @@
package org.briarproject.bramble.system;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.FINE;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
@ThreadSafe
@NotNullByDefault
class RenewableWakeLock implements SharedWakeLock {
private static final Logger LOG =
getLogger(RenewableWakeLock.class.getName());
private final PowerManager powerManager;
private final ScheduledExecutorService scheduledExecutorService;
private final int levelAndFlags;
private final String tag;
private final long durationMs, safetyMarginMs;
private final Object lock = new Object();
@GuardedBy("lock")
@Nullable
private WakeLock wakeLock;
@GuardedBy("lock")
@Nullable
private Future<?> future;
@GuardedBy("lock")
private int refCount = 0;
@GuardedBy("lock")
private long acquired = 0;
RenewableWakeLock(PowerManager powerManager,
ScheduledExecutorService scheduledExecutorService,
int levelAndFlags,
String tag,
long durationMs,
long safetyMarginMs) {
this.powerManager = powerManager;
this.scheduledExecutorService = scheduledExecutorService;
this.levelAndFlags = levelAndFlags;
this.tag = tag;
this.durationMs = durationMs;
this.safetyMarginMs = safetyMarginMs;
}
@Override
public void acquire() {
synchronized (lock) {
refCount++;
if (refCount == 1) {
if (LOG.isLoggable(INFO)) {
LOG.info("Acquiring wake lock " + tag);
}
wakeLock = powerManager.newWakeLock(levelAndFlags, tag);
// We do our own reference counting so we can replace the lock
// TODO: Check whether using a ref-counted wake lock affects
// power management apps
wakeLock.setReferenceCounted(false);
wakeLock.acquire(durationMs + safetyMarginMs);
future = scheduledExecutorService.schedule(this::renew,
durationMs, MILLISECONDS);
acquired = android.os.SystemClock.elapsedRealtime();
} else if (LOG.isLoggable(FINE)) {
LOG.fine("Wake lock " + tag + " has " + refCount + " holders");
}
}
}
private void renew() {
if (LOG.isLoggable(INFO)) LOG.info("Renewing wake lock " + tag);
synchronized (lock) {
if (wakeLock == null) {
LOG.info("Already released");
return;
}
if (LOG.isLoggable(FINE)) {
LOG.fine("Wake lock " + tag + " has " + refCount + " holders");
}
long now = android.os.SystemClock.elapsedRealtime();
long expiry = acquired + durationMs + safetyMarginMs;
if (now > expiry && LOG.isLoggable(WARNING)) {
LOG.warning("Wake lock expired " + (now - expiry) + " ms ago");
}
WakeLock oldWakeLock = wakeLock;
wakeLock = powerManager.newWakeLock(levelAndFlags, tag);
wakeLock.setReferenceCounted(false);
wakeLock.acquire(durationMs + safetyMarginMs);
oldWakeLock.release();
future = scheduledExecutorService.schedule(this::renew, durationMs,
MILLISECONDS);
acquired = now;
}
}
@Override
public void release() {
synchronized (lock) {
refCount--;
if (refCount == 0) {
if (LOG.isLoggable(INFO)) {
LOG.info("Releasing wake lock " + tag);
}
requireNonNull(future).cancel(false);
future = null;
requireNonNull(wakeLock).release();
wakeLock = null;
acquired = 0;
} else if (LOG.isLoggable(FINE)) {
LOG.fine("Wake lock " + tag + " has " + refCount + " holders");
}
}
}
}

View File

@@ -1,22 +0,0 @@
package org.briarproject.bramble.system;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.AndroidWakeLock;
@NotNullByDefault
interface SharedWakeLock {
/**
* Acquires the wake lock. This increments the wake lock's reference count,
* so unlike {@link AndroidWakeLock#acquire()} every call to this method
* must be followed by a balancing call to {@link #release()}.
*/
void acquire();
/**
* Releases the wake lock. This decrements the wake lock's reference count,
* so unlike {@link AndroidWakeLock#release()} every call to this method
* must follow a balancing call to {@link #acquire()}.
*/
void release();
}

View File

@@ -6,25 +6,15 @@ import android.content.Context;
import android.os.Build;
import android.provider.Settings;
import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
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.Arrays;
import java.util.Collection;
import java.util.List;
import javax.annotation.Nullable;
import static android.content.Context.MODE_PRIVATE;
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 {
// 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";
@SuppressWarnings("deprecation")
public static Collection<String> getSupportedArchitectures() {
List<String> abis = new ArrayList<>();
if (SDK_INT >= 21) {
abis.addAll(asList(Build.SUPPORTED_ABIS));
abis.addAll(Arrays.asList(Build.SUPPORTED_ABIS));
} else {
abis.add(Build.CPU_ABI);
if (Build.CPU_ABI2 != null) abis.add(Build.CPU_ABI2);
@@ -45,76 +36,25 @@ public class AndroidUtils {
public static String getBluetoothAddress(Context ctx,
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
@SuppressLint("HardwareIds")
String address = adapter.getAddress();
if (isValidBluetoothAddress(address)) {
return new Pair<>(address, "adapter");
}
if (isValidBluetoothAddress(address)) return address;
// Return the address from settings if it's valid and not fake
address = Settings.Secure.getString(ctx.getContentResolver(),
"bluetooth_address");
if (isValidBluetoothAddress(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");
}
if (isValidBluetoothAddress(address)) return 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)
&& BluetoothAdapter.checkBluetoothAddress(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) {
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

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

View File

@@ -16,10 +16,13 @@ import org.junit.Test;
import java.io.File;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
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.util.StringUtils.toHexString;
public class AndroidAccountManagerTest extends BrambleMockTestCase {
@@ -37,8 +40,11 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
private final Application app;
private final ApplicationInfo applicationInfo;
private final String encryptedKeyHex = toHexString(getRandomBytes(123));
private final File testDir = getTestDirectory();
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 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
public void testDeleteAccountClearsSharedPrefsAndDeletesFiles()
throws Exception {
// Directories 'code_cache', 'lib' and 'shared_prefs' should be spared
File codeCacheDir = new File(testDir, "code_cache");
File codeCacheFile = new File(codeCacheDir, "file");
// Directories 'lib' and 'shared_prefs' should be spared
File libDir = new File(testDir, "lib");
File libFile = new File(libDir, "file");
File sharedPrefsDir = new File(testDir, "shared_prefs");
@@ -113,8 +140,6 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
assertTrue(dbDir.mkdirs());
assertTrue(keyDir.mkdirs());
assertTrue(codeCacheDir.mkdirs());
assertTrue(codeCacheFile.createNewFile());
assertTrue(libDir.mkdirs());
assertTrue(libFile.createNewFile());
assertTrue(sharedPrefsDir.mkdirs());
@@ -130,8 +155,6 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
assertFalse(dbDir.exists());
assertFalse(keyDir.exists());
assertTrue(codeCacheDir.exists());
assertTrue(codeCacheFile.exists());
assertTrue(libDir.exists());
assertTrue(libFile.exists());
assertTrue(sharedPrefsDir.exists());

View File

@@ -1,46 +1,44 @@
dependencyVerification {
verify = [
'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:shared:26.5.1:shared-26.5.1.jar:ccc2f3b00ec17b11401610ba68553544fc8fc517120e84439ac6eb86b875e18d',
'com.android.tools.analytics-library:tracker:26.5.1:tracker-26.5.1.jar:3a76984c0fe2e847ca7a8b35b4780ef0447a9d1666946cb8e60466318e0ab5ae',
'com.android.tools.build:aapt2-proto:0.4.0:aapt2-proto-0.4.0.jar:fac0435e08898f89eeeb9ca236bea707155ff816c12205ced285ad53604133ca',
'com.android.tools.build:apksig:3.5.1:apksig-3.5.1.jar:1fd33e7f009a2a0da766cfeec4211a09f548034b015c289a66d75dd8a9302f4a',
'com.android.tools.build:apkzlib:3.5.1:apkzlib-3.5.1.jar:9f330167cbe973b7db407692f74f4f6453b7ffa5f2048934b06280c2ceee60fa',
'com.android.tools.build:builder-model:3.5.1:builder-model-3.5.1.jar:39ea3c82b76b6e0c9f9fa88d93e0edc1dd4a0f1dfae0ef6fbf2d451da47e5450',
'com.android.tools.build:builder-test-api:3.5.1:builder-test-api-3.5.1.jar:a1b59305584cbcaa078fdc9cfb80871012755b822dd32e8da19add6f7bbcb762',
'com.android.tools.build:builder:3.5.1:builder-3.5.1.jar:e3a8d382434c5f60990730c4719fc814e85a898a33a1e96c1df8d627d3c6eea6',
'com.android.tools.build:gradle-api:3.5.1:gradle-api-3.5.1.jar:be9b41859bace11998f66b04ed944f87e413f3ad6da3c4665587699da125addc',
'com.android.tools.build:manifest-merger:26.5.1:manifest-merger-26.5.1.jar:dcad9ecb967251f4d750f55a4204a2b400e8fbfe5cb930a1d0d5dbe10ae8bdfc',
'com.android.tools.ddms:ddmlib:26.5.1:ddmlib-26.5.1.jar:b081aef2a4ed3f4d47cae4cdb128469735f25a114e026d37123bf9ffdec742a8',
'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.5.1:kotlin-compiler-26.5.1.jar:5aed762dd54875b77ae7018d97c05756ff0c5b9fd02ec595dd396ccd14cc22cb',
'com.android.tools.external.org-jetbrains:uast:26.5.1:uast-26.5.1.jar:4bc8653d6c0943f40fee963a149e36c6baa45683d2530968a13f5007e3c40740',
'com.android.tools.layoutlib:layoutlib-api:26.5.1:layoutlib-api-26.5.1.jar:88732f11396c427273e515d23042e35633f4fe4295528a99b866aa2adf0efd9c',
'com.android.tools.lint:lint-api:26.5.1:lint-api-26.5.1.jar:ec33fcd72bfaf70dd841e03fbfd93f109c2e575aec146067c606689c3972f0de',
'com.android.tools.lint:lint-checks:26.5.1:lint-checks-26.5.1.jar:a1b9607d484aaae7a71dcecdc76f8003d8239af226c776894a2cf63f9e6c60d7',
'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.5.1:lint-gradle-26.5.1.jar:59465b56cf7db77c656d5f8195d721c3d48b6bdd0502d774de335bfe4baff00b',
'com.android.tools.lint:lint:26.5.1:lint-26.5.1.jar:336e4b04ec6f8b0f25879131b7a7862d77df83a1879ee5b71be26128755f8e2e',
'com.android.tools:annotations:26.5.1:annotations-26.5.1.jar:2c43c82f8c59d8f7a61e3239e1a2dc9f69dc342ec09af9b7c9f69b25337c0b6e',
'com.android.tools:common:26.5.1:common-26.5.1.jar:eccfa54486ed54c4e3123cc42195d023bd0dd21bcd2f0e4868e8c6fc70f8ef6b',
'com.android.tools:dvlib:26.5.1:dvlib-26.5.1.jar:46f93ad498b4756e7d867d2fe38c38890a80e7407a4ae459e4a8c8d5c5aeacfe',
'com.android.tools:repository:26.5.1:repository-26.5.1.jar:2b3ee791aa4c3e8ce60498c161a27ca7228816fc630eed4d9f25f2f36a106dce',
'com.android.tools:sdk-common:26.5.1:sdk-common-26.5.1.jar:365f749676c3574676fd465177c8a492f340816db2b520d6ed114d3b6e77bea7',
'com.android.tools:sdklib:26.5.1:sdklib-26.5.1.jar:007da104afb27c8c682a1628023fe9ec438249c8d15ef0fd6624c5bb8e23b696',
'com.android.tools.analytics-library:protos:26.4.0:protos-26.4.0.jar:ad760915586797d39319f402837b378bff3bb4ed583e3e0c48c965631fb2135f',
'com.android.tools.analytics-library:shared:26.4.0:shared-26.4.0.jar:1332106a905d48909c81268c9e414946de3e83487db394c6073b0a9b5c3d0ed2',
'com.android.tools.analytics-library:tracker:26.4.0:tracker-26.4.0.jar:d0020cfbfd4cd75935f2972d6a24089840d4a10df6f3ef2a796093217dd37796',
'com.android.tools.build:apksig:3.4.0:apksig-3.4.0.jar:91d5a1866139c69756280355a6f61b4d619d0516841580114f45a10f2177327e',
'com.android.tools.build:apkzlib:3.4.0:apkzlib-3.4.0.jar:8653c85f5fdf1dde840e8b8af7396aeb79c34b66e541b5860059616006535592',
'com.android.tools.build:builder-model:3.4.0:builder-model-3.4.0.jar:a88f138124a9f016a70bcb4760359a502f65c7deed56507ee4014f4dd9ea853b',
'com.android.tools.build:builder-test-api:3.4.0:builder-test-api-3.4.0.jar:31089ab1ec19ca7687a010867d2f3807513c805b8226979706f4247b5d4df26f',
'com.android.tools.build:builder:3.4.0:builder-3.4.0.jar:476221b5203a7f50089bf185ed95000a34b6f5020ef0a17815afd58606922679',
'com.android.tools.build:gradle-api:3.4.0:gradle-api-3.4.0.jar:215eca38f6719213c2f492b4d622cdd11676c66c9871f8a2aed0c66d00175628',
'com.android.tools.build:manifest-merger:26.4.0:manifest-merger-26.4.0.jar:29e45e690dedd165035e97c21c2ca94d0bd4ec16b6b210daa26669a582b6f220',
'com.android.tools.ddms:ddmlib:26.4.0:ddmlib-26.4.0.jar:93f56fe4630c3166adbd6c51d7bb602d96abb91b07ba5b1165fdcd071e88c940',
'com.android.tools.external.com-intellij:intellij-core:26.4.0:intellij-core-26.4.0.jar:30cb0e879d4424de9677a50b537fb628636b4a50f5470af5e52437980c41421f',
'com.android.tools.external.com-intellij:kotlin-compiler:26.4.0:kotlin-compiler-26.4.0.jar:dd1fe225c31a0e012dc025336363a5b783e2c5c20ffb69e77f8f57e89420d998',
'com.android.tools.external.org-jetbrains:uast:26.4.0:uast-26.4.0.jar:f25f3285b775a983327583ff6584dea54e447813ef69e0ce08b05a45b5f4aab0',
'com.android.tools.layoutlib:layoutlib-api:26.4.0:layoutlib-api-26.4.0.jar:52128f5cf293b224072be361919bfd416e59480ab7264ddcdbbf046b0d7a12e3',
'com.android.tools.lint:lint-api:26.4.0:lint-api-26.4.0.jar:fdb8fca8ae4c254f438338d03d72605e00ed106f2d5550405af41ca1c8509401',
'com.android.tools.lint:lint-checks:26.4.0:lint-checks-26.4.0.jar:4ff52d40488cd3e22b9c6b2eb67784e0c3269d0b42ef9d17689cd75a7b2bceb4',
'com.android.tools.lint:lint-gradle-api:26.4.0:lint-gradle-api-26.4.0.jar:714b7a85c7d2aa10daeab16e969fe7530c659d0728a7f24021da456870418d0f',
'com.android.tools.lint:lint-gradle:26.4.0:lint-gradle-26.4.0.jar:b8c130d273f522388734457e1b96790f41528fcec6fda9e8eaa4e4d95a07cfbb',
'com.android.tools.lint:lint:26.4.0:lint-26.4.0.jar:83aa062fb0405b60ed358d858c8c2955e1bae44a455b498068c6a60988755f00',
'com.android.tools:annotations:26.4.0:annotations-26.4.0.jar:a7955b8e19c3a2a861d6faa43a58b7c0d46ea9112188ee3e235c6f9f439ecc1a',
'com.android.tools:common:26.4.0:common-26.4.0.jar:ea40b94b3c1284ea7700f011388e2906a8363a66abd902891722b3c557984852',
'com.android.tools:dvlib:26.4.0:dvlib-26.4.0.jar:23af89c535b01ba36ceed1b6b309b672814eba624e643cd7dedf0519edad50cc',
'com.android.tools:repository:26.4.0:repository-26.4.0.jar:3d1763ab46199374dc6d94129bba11c70f1d5857e2c81a3ac4898abca40b176b',
'com.android.tools:sdk-common:26.4.0:sdk-common-26.4.0.jar:78a522525b30ffc6b7bf1299c831d24ce385f68a9f4878f8f752e9baefa31b0f',
'com.android.tools:sdklib:26.4.0:sdklib-26.4.0.jar:b854c23892013a326d761cf071c72cf3e038ed0469d10f4a356829fa56e4c132',
'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.gson:gson:2.8.5:gson-2.8.5.jar:233a0149fc365c9f6edbd683cfe266b19bdc773be98eabdaf6b3c924b48e7d81',
'com.google.dagger:dagger-compiler:2.24:dagger-compiler-2.24.jar:3c5afb955fb188da485cb2c048eff37dce0e1530b9780a0f2f7187d16d1ccc1f',
'com.google.dagger:dagger-producers:2.24:dagger-producers-2.24.jar:f10f45b95191954d5d6b043fca9e62fb621d21bf70634b8f8476c7988b504c3a',
'com.google.dagger:dagger-spi:2.24:dagger-spi-2.24.jar:c038445d14dbcb4054e61bf49e05009edf26fce4fdc7ec1a9db544784f68e718',
'com.google.dagger:dagger:2.24:dagger-2.24.jar:550a6e46a6dfcdf1d764887b6090cea94f783327e50e5c73754f18facfc70b64',
'com.google.errorprone:error_prone_annotations:2.2.0:error_prone_annotations-2.2.0.jar:6ebd22ca1b9d8ec06d41de8d64e0596981d9607b42035f9ed374f9de271a481a',
'com.google.code.gson:gson:2.8.0:gson-2.8.0.jar:c6221763bd79c4f1c3dc7f750b5f29a0bb38b367b81314c4f71896e340c40825',
'com.google.dagger:dagger-compiler:2.22.1:dagger-compiler-2.22.1.jar:e5f28302cbe70a79d3620cddebfb8ec0736814f3980ffe1e673bfe3342f507d3',
'com.google.dagger:dagger-producers:2.22.1:dagger-producers-2.22.1.jar:f834a0082014213a68ff06a0f048d750178d02196c58b0b15beb367d32b97e35',
'com.google.dagger:dagger-spi:2.22.1:dagger-spi-2.22.1.jar:4b0b922793b3bcb91b99fabb75dba77c68afd7ae4c5f0c4fd6ba681f0a291c7d',
'com.google.dagger:dagger:2.22.1:dagger-2.22.1.jar:329d4340f24c4f5717af016c097e90668bfea2a5376e6aa9964b01cef3fd241a',
'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.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:27.0.1-jre:guava-27.0.1-jre.jar:e1c814fd04492a27c38e0317eabeaa1b3e950ec8010239e400fe90ad6c9107b4',
'com.google.guava:guava:27.1-jre:guava-27.1-jre.jar:4a5aa70cc968a4d137e599ad37553e5cfeed2265e8c193476d7119036c536fe7',
'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava:listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar:b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99',
'com.google.guava:guava:25.0-jre:guava-25.0-jre.jar:3fd4341776428c7e0e5c18a7c10de129475b69ab9d30aeafbb5c277bb6074fa9',
'com.google.guava:guava:26.0-jre:guava-26.0-jre.jar:a0e9cabad665bc20bcd2b01f108e5fc03f756e13aea80abaadb9f407033bea2c',
'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.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.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',
'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.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',
@@ -69,22 +66,22 @@ dependencyVerification {
'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: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:tor-android:0.3.5.10:tor-android-0.3.5.10.zip:edd83bf557fcff2105eaa0bdb3f607a6852ebe7360920929ae3039dd5f4774c5',
'org.briarproject:obfs4proxy-android:0.0.9:obfs4proxy-android-0.0.9.zip:9b7e9181535ea8d8bbe8ae6338e08cf4c5fc1e357a779393e0ce49586d459ae0',
'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-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.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-runtime:2.2.11:jaxb-runtime-2.2.11.jar:a874f2351cfba8e2946be3002d10c18a6da8f21b52ba2acf52f2b85d5520ed70',
'org.glassfish.jaxb:txw2:2.2.11:txw2-2.2.11.jar:272a3ccad45a4511351920cd2a8633c53cab8d5220c7a92954da5526bb5eafea',
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
'org.jetbrains.kotlin:kotlin-reflect:1.3.50:kotlin-reflect-1.3.50.jar:64583199ea5a54aefd1bd1595288925f784226ee562d1dd279011c6075b3d7a4',
'org.jetbrains.kotlin:kotlin-stdlib-common:1.3.50:kotlin-stdlib-common-1.3.50.jar:8ce678e88e4ba018b66dacecf952471e4d7dfee156a8a819760a5a5ff29d323c',
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.50:kotlin-stdlib-jdk7-1.3.50.jar:9a026639e76212f8d57b86d55b075394c2e009f1979110751d34c05c5f75d57b',
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.50:kotlin-stdlib-jdk8-1.3.50.jar:1b351fb6e09c14b55525c74c1f4cf48942eae43c348b7bc764a5e6e423d4da0c',
'org.jetbrains.kotlin:kotlin-stdlib:1.3.50:kotlin-stdlib-1.3.50.jar:e6f05746ee0366d0b52825a090fac474dcf44082c9083bbb205bd16976488d6c',
'org.jetbrains.kotlin:kotlin-reflect:1.3.21:kotlin-reflect-1.3.21.jar:a3065c822633191e0a3e3ee12a29bec234fc4b2864a6bb87ef48cce3e9e0c26a',
'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.21:kotlin-stdlib-jdk7-1.3.21.jar:a87875604fd42140da6938ae4d35ee61081f4482536efc6d2615b8b626a198af',
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.21:kotlin-stdlib-jdk8-1.3.21.jar:5823ed66ac122a1c55442ebca5a209a843ccd87f562edc31a787f3d2e47f74d4',
'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:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
'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'
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'
testImplementation 'junit:junit:4.12'

View File

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

View File

@@ -1,6 +1,5 @@
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.identity.IdentityManager;
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
* before {@link #createAccount(String, String)} or {@link #signIn(String)}
* has been called, and true after {@link #createAccount(String, String)}
* or {@link #signIn(String)} has returned true, until
* {@link #deleteAccount()} is called or the process exits.
* or {@link #signIn(String)} has returned true, until the process exits.
*/
boolean hasDatabaseKey();
@@ -24,22 +22,25 @@ public interface AccountManager {
* before {@link #createAccount(String, String)} or {@link #signIn(String)}
* has been called, and non-null after
* {@link #createAccount(String, String)} or {@link #signIn(String)} has
* returned true, until {@link #deleteAccount()} is called or the process
* exits.
* returned true, until the process exits.
*/
@Nullable
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();
/**
* Creates an identity with the given name and registers it with the
* {@link IdentityManager}. Creates a database key, encrypts it with the
* given password and stores it on disk. {@link #accountExists()} will
* return true after this method returns true.
* given password and stores it on disk.
* <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);
@@ -53,19 +54,17 @@ public interface AccountManager {
* Loads the encrypted database key from disk and decrypts it with the
* given password.
*
* @throws DecryptionException If the database key could not be loaded and
* decrypted.
* @return true if the database key was successfully loaded and decrypted.
*/
void signIn(String password) throws DecryptionException;
boolean signIn(String password);
/**
* Loads the encrypted database key from disk, decrypts it with the old
* password, encrypts it with the new password, and stores it on disk,
* replacing the old key.
*
* @throws DecryptionException If the database key could not be loaded and
* decrypted.
* @return true if the database key was successfully loaded, re-encrypted
* and stored.
*/
void changePassword(String oldPassword, String newPassword)
throws DecryptionException;
boolean changePassword(String oldPassword, String newPassword);
}

View File

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

View File

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

View File

@@ -4,10 +4,8 @@ import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.UnsupportedVersionException;
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.NoSuchContactException;
import org.briarproject.bramble.api.db.PendingContactExistsException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
@@ -119,14 +117,9 @@ public interface ContactManager {
* @throws FormatException If the link is invalid
* @throws GeneralSecurityException If the pending contact's handshake
* public key is invalid
* @throws ContactExistsException If a contact with the same handshake
* public key already exists
* @throws PendingContactExistsException If a pending contact with the same
* handshake public key already exists
*/
PendingContact addPendingContact(String link, String alias)
throws DbException, FormatException, GeneralSecurityException,
ContactExistsException, PendingContactExistsException;
throws DbException, FormatException, GeneralSecurityException;
/**
* 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
* given password. The ciphertext will be decryptable using the same
* 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,
@Nullable KeyStrengthener keyStrengthener);
byte[] encryptWithPassword(byte[] plaintext, String password);
/**
* Decrypts and authenticates the given ciphertext that has been read from
* storage. The encryption and authentication keys are derived from the
* given password.
*
* @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
* given password. Returns null if the ciphertext cannot be decrypted and
* authenticated (for example, if the password is wrong).
*/
byte[] decryptWithPassword(byte[] ciphertext, String password,
@Nullable KeyStrengthener keyStrengthener)
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);
@Nullable
byte[] decryptWithPassword(byte[] ciphertext, String password);
/**
* 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.
*/
void addPendingContact(Transaction txn, PendingContact p, AuthorId local)
void addPendingContact(Transaction txn, PendingContact p)
throws DbException;
/**

View File

@@ -1,29 +1,13 @@
package org.briarproject.bramble.api.db;
import org.briarproject.bramble.api.crypto.KeyStrengthener;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.File;
import javax.annotation.Nullable;
@NotNullByDefault
public interface DatabaseConfig {
/**
* Returns the directory where the database stores its data.
*/
File getDatabaseDirectory();
/**
* Returns the directory where the encrypted database key is stored.
*/
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;
import org.briarproject.bramble.api.contact.PendingContact;
/**
* Thrown when a duplicate pending contact is added to the database. This
* exception may occur due to concurrent updates and does not indicate a
* database error.
*/
public class PendingContactExistsException extends DbException {
private final PendingContact pendingContact;
public PendingContactExistsException(PendingContact pendingContact) {
this.pendingContact = pendingContact;
}
public PendingContact getPendingContact() {
return pendingContact;
}
}

View File

@@ -18,8 +18,6 @@ public interface EventBus {
/**
* Asynchronously notifies all listeners of an event. Listeners are
* notified on the {@link EventExecutor}.
* <p>
* This method can safely be called while holding a lock.
*/
void broadcast(Event e);
}

View File

@@ -1,19 +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.
* <p>
* The returned stream must be {@link InputStream#close() closed} when it's
* no longer needed to ensure that resources held by the timeout monitor
* are released.
*
* @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

@@ -5,7 +5,6 @@ import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.Wakeful;
import java.util.concurrent.ExecutorService;
@@ -66,7 +65,6 @@ public interface LifecycleManager {
* Opens the {@link DatabaseComponent} using the given key and starts any
* registered {@link Service Services}.
*/
@Wakeful
StartResult startServices(SecretKey dbKey);
/**
@@ -74,7 +72,6 @@ public interface LifecycleManager {
* registered {@link ExecutorService ExecutorServices}, and closes the
* {@link DatabaseComponent}.
*/
@Wakeful
void stopServices();
/**
@@ -107,7 +104,6 @@ public interface LifecycleManager {
*
* @param txn A read-write transaction
*/
@Wakeful
void onDatabaseOpened(Transaction txn) throws DbException;
}
}

View File

@@ -1,20 +1,16 @@
package org.briarproject.bramble.api.lifecycle;
import org.briarproject.bramble.api.system.Wakeful;
public interface Service {
/**
* Starts the service. This method must not be called concurrently with
* Starts the service.This method must not be called concurrently with
* {@link #stopService()}.
*/
@Wakeful
void startService() throws ServiceException;
/**
* Stops the service. This method must not be called concurrently with
* {@link #startService()}.
*/
@Wakeful
void stopService() throws ServiceException;
}

View File

@@ -6,16 +6,8 @@ public interface BluetoothConstants {
int UUID_BYTES = 16;
// Transport properties
String PROP_ADDRESS = "address";
String PROP_UUID = "uuid";
// Local settings (not shared with contacts)
String PREF_ADDRESS_IS_REFLECTED = "addressIsReflected";
String PREF_EVER_CONNECTED = "everConnected";
// Default values for local settings
boolean DEFAULT_PREF_PLUGIN_ENABLE = false;
boolean DEFAULT_PREF_ADDRESS_IS_REFLECTED = false;
boolean DEFAULT_PREF_EVER_CONNECTED = false;
String PREF_BT_ENABLE = "enable";
}

View File

@@ -1,11 +1,8 @@
package org.briarproject.bramble.api.connection;
package org.briarproject.bramble.api.plugin;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
@NotNullByDefault

View File

@@ -0,0 +1,67 @@
package org.briarproject.bramble.api.plugin;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent;
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
import java.util.Collection;
/**
* Keeps track of which contacts are currently connected by which transports.
*/
@NotNullByDefault
public interface ConnectionRegistry {
/**
* Registers a connection with the given contact over the given transport.
* Broadcasts {@link ConnectionOpenedEvent}. Also broadcasts
* {@link ContactConnectedEvent} if this is the only connection with the
* contact.
*/
void registerConnection(ContactId c, TransportId t, boolean incoming);
/**
* Unregisters a connection with the given contact over the given transport.
* Broadcasts {@link ConnectionClosedEvent}. Also broadcasts
* {@link ContactDisconnectedEvent} if this is the only connection with
* the contact.
*/
void unregisterConnection(ContactId c, TransportId t, boolean incoming);
/**
* Returns any contacts that are connected via the given transport.
*/
Collection<ContactId> getConnectedContacts(TransportId t);
/**
* Returns true if the given contact is connected via the given transport.
*/
boolean isConnected(ContactId c, TransportId t);
/**
* Returns true if the given contact is connected via any transport.
*/
boolean isConnected(ContactId c);
/**
* Registers a connection with the given pending contact. Broadcasts
* {@link RendezvousConnectionOpenedEvent} if this is the only connection
* with the pending contact.
*
* @return True if this is the only connection with the pending contact,
* false if it is redundant and should be closed
*/
boolean registerConnection(PendingContactId p);
/**
* Unregisters a connection with the given pending contact. Broadcasts
* {@link RendezvousConnectionClosedEvent}.
*/
void unregisterConnection(PendingContactId p, boolean success);
}

View File

@@ -4,15 +4,10 @@ public interface LanTcpConstants {
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_PORT = "port";
String PROP_IPV6 = "ipv6";
// Local settings (not shared with contacts)
// a local setting
String PREF_LAN_IP_PORTS = "ipPorts";
String PREF_IPV6 = "ipv6";
// Default value for PREF_PLUGIN_ENABLE
boolean DEFAULT_PREF_PLUGIN_ENABLE = true;
}

View File

@@ -3,56 +3,12 @@ package org.briarproject.bramble.api.plugin;
import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.settings.SettingsManager;
import org.briarproject.bramble.api.system.Wakeful;
import java.util.Collection;
@NotNullByDefault
public interface Plugin {
enum State {
/**
* The plugin has not finished starting or has been stopped.
*/
STARTING_STOPPING,
/**
* The plugin is disabled by settings. Use {@link #getReasonsDisabled()}
* to find out which settings are responsible.
*/
DISABLED,
/**
* The plugin is being enabled and can't yet make or receive
* connections.
*/
ENABLING,
/**
* The plugin is enabled and can make or receive connections.
*/
ACTIVE,
/**
* The plugin is enabled but can't make or receive connections
*/
INACTIVE
}
/**
* The string for the boolean preference
* to use with the {@link SettingsManager} to enable or disable the plugin.
*/
String PREF_PLUGIN_ENABLE = "enable";
/**
* Reason flag returned by {@link #getReasonsDisabled()} to indicate that
* the plugin has been disabled by the user.
*/
int REASON_USER = 1;
/**
* Returns the plugin's transport identifier.
*/
@@ -71,28 +27,17 @@ public interface Plugin {
/**
* Starts the plugin.
*/
@Wakeful
void start() throws PluginException;
/**
* Stops the plugin.
*/
@Wakeful
void stop() throws PluginException;
/**
* Returns the current state of the plugin.
* Returns true if the plugin is running.
*/
State getState();
/**
* Returns a set of flags indicating why the plugin is
* {@link State#DISABLED disabled}, or 0 if the plugin is not disabled.
* <p>
* The flags used are plugin-specific, except the generic flag
* {@link #REASON_USER}, which may be used by any plugin.
*/
int getReasonsDisabled();
boolean isRunning();
/**
* Returns true if the plugin should be polled periodically to attempt to
@@ -109,7 +54,6 @@ public interface Plugin {
* Attempts to create connections using the given transport properties,
* passing any created connections to the corresponding handlers.
*/
@Wakeful
void poll(Collection<Pair<TransportProperties, ConnectionHandler>>
properties);
}

View File

@@ -1,15 +1,9 @@
package org.briarproject.bramble.api.plugin;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Plugin.State;
import org.briarproject.bramble.api.plugin.event.TransportActiveEvent;
import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent;
import org.briarproject.bramble.api.plugin.event.TransportStateEvent;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.settings.Settings;
import java.util.Collection;
/**
* An interface through which a transport plugin interacts with the rest of
* the application.
@@ -27,11 +21,6 @@ public interface PluginCallback extends ConnectionHandler {
*/
TransportProperties getLocalProperties();
/**
* Returns the plugin's remote transport properties.
*/
Collection<TransportProperties> getRemoteProperties();
/**
* Merges the given settings with the plugin's settings
*/
@@ -43,17 +32,12 @@ public interface PluginCallback extends ConnectionHandler {
void mergeLocalProperties(TransportProperties p);
/**
* Informs the callback of the plugin's current state.
* <p>
* If the current state is different from the previous state, the callback
* will broadcast a {@link TransportStateEvent}. If the current state is
* {@link State#ACTIVE} and the previous state was not
* {@link State#ACTIVE}, the callback will broadcast a
* {@link TransportActiveEvent}. If the current state is not
* {@link State#ACTIVE} and the previous state was {@link State#ACTIVE},
* the callback will broadcast a {@link TransportInactiveEvent}.
* <p>
* This method can safely be called while holding a lock.
* Signals that the transport is enabled.
*/
void pluginStateChanged(State state);
void transportEnabled();
/**
* Signals that the transport is disabled.
*/
void transportDisabled();
}

View File

@@ -5,8 +5,6 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@NotNullByDefault
public interface PluginConfig {
@@ -16,11 +14,4 @@ public interface PluginConfig {
Collection<SimplexPluginFactory> getSimplexFactories();
boolean shouldPoll();
/**
* Returns a map representing transport preferences. For each entry in the
* map, connections via the transports identified by the value are
* preferred to connections via the transport identified by the key.
*/
Map<TransportId, List<TransportId>> getTransportPreferences();
}

View File

@@ -41,17 +41,4 @@ public interface PluginManager {
* Returns any duplex plugins that support rendezvous.
*/
Collection<DuplexPlugin> getRendezvousPlugins();
/**
* Enables or disables the plugin
* identified by the given {@link TransportId}.
* <p>
* Note that this applies the change asynchronously
* and there are no order guarantees.
* <p>
* If no plugin with the given {@link TransportId} is registered,
* this is a no-op.
*/
void setPluginEnabled(TransportId t, boolean enabled);
}

View File

@@ -1,12 +1,9 @@
package org.briarproject.bramble.api.plugin;
import static java.util.concurrent.TimeUnit.DAYS;
public interface TorConstants {
TransportId ID = new TransportId("org.briarproject.bramble.tor");
// Transport properties
String PROP_ONION_V2 = "onion";
String PROP_ONION_V3 = "onion3";
@@ -16,45 +13,14 @@ public interface TorConstants {
int CONNECT_TO_PROXY_TIMEOUT = 5000; // Milliseconds
int EXTRA_SOCKET_TIMEOUT = 30000; // Milliseconds
// Local settings (not shared with contacts)
String PREF_TOR_NETWORK = "network2";
String PREF_TOR_PORT = "port";
String PREF_TOR_MOBILE = "useMobileData";
String PREF_TOR_ONLY_WHEN_CHARGING = "onlyWhenCharging";
String HS_PRIVATE_KEY_V2 = "onionPrivKey";
String HS_PRIVATE_KEY_V3 = "onionPrivKey3";
String HS_V3_CREATED = "onionPrivKey3Created";
/**
* How long to publish a v3 hidden service before retiring the v2 service.
*/
long V3_MIGRATION_PERIOD_MS = DAYS.toMillis(180);
// Values for PREF_TOR_NETWORK
int PREF_TOR_NETWORK_AUTOMATIC = 0;
int PREF_TOR_NETWORK_WITHOUT_BRIDGES = 1;
int PREF_TOR_NETWORK_WITH_BRIDGES = 2;
// TODO: Remove when settings migration code is removed
int PREF_TOR_NETWORK_NEVER = 3;
// Default values for local settings
boolean DEFAULT_PREF_PLUGIN_ENABLE = true;
int DEFAULT_PREF_TOR_NETWORK = PREF_TOR_NETWORK_AUTOMATIC;
boolean DEFAULT_PREF_TOR_MOBILE = true;
boolean DEFAULT_PREF_TOR_ONLY_WHEN_CHARGING = false;
/**
* Reason flag returned by {@link Plugin#getReasonsDisabled()}.
*/
int REASON_BATTERY = 2;
/**
* Reason flag returned by {@link Plugin#getReasonsDisabled()}.
*/
int REASON_MOBILE_DATA = 4;
/**
* Reason flag returned by {@link Plugin#getReasonsDisabled()}.
*/
int REASON_COUNTRY_BLOCKED = 8;
}

View File

@@ -4,7 +4,4 @@ public interface WanTcpConstants {
TransportId ID = new TransportId("org.briarproject.bramble.wan");
// Default value for PREF_PLUGIN_ENABLE
boolean DEFAULT_PREF_PLUGIN_ENABLE = false;
}

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

View File

@@ -8,7 +8,6 @@ import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.rendezvous.KeyMaterialSource;
import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint;
import org.briarproject.bramble.api.system.Wakeful;
import javax.annotation.Nullable;
@@ -22,7 +21,6 @@ public interface DuplexPlugin extends Plugin {
* Attempts to create and return a connection using the given transport
* properties. Returns null if a connection cannot be created.
*/
@Wakeful
@Nullable
DuplexTransportConnection createConnection(TransportProperties p);

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.plugin.TransportConnectionReader;
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
@@ -24,10 +23,4 @@ public interface DuplexTransportConnection {
* for writing to the connection.
*/
TransportConnectionWriter getWriter();
/**
* Returns a possibly empty set of {@link TransportProperties} describing
* the remote peer.
*/
TransportProperties getRemoteProperties();
}

View File

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

View File

@@ -13,14 +13,13 @@ public class ConnectionClosedEvent extends Event {
private final ContactId contactId;
private final TransportId transportId;
private final boolean incoming, exception;
private final boolean incoming;
public ConnectionClosedEvent(ContactId contactId, TransportId transportId,
boolean incoming, boolean exception) {
boolean incoming) {
this.contactId = contactId;
this.transportId = transportId;
this.incoming = incoming;
this.exception = exception;
}
public ContactId getContactId() {
@@ -34,8 +33,4 @@ public class ConnectionClosedEvent extends Event {
public boolean isIncoming() {
return incoming;
}
public boolean isException() {
return exception;
}
}

View File

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

View File

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

View File

@@ -2,22 +2,20 @@ package org.briarproject.bramble.api.plugin.event;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Plugin.State;
import org.briarproject.bramble.api.plugin.TransportId;
import javax.annotation.concurrent.Immutable;
/**
* An event that is broadcast when a plugin enters the {@link State#ACTIVE}
* state.
* An event that is broadcast when a transport is disabled.
*/
@Immutable
@NotNullByDefault
public class TransportActiveEvent extends Event {
public class TransportDisabledEvent extends Event {
private final TransportId transportId;
public TransportActiveEvent(TransportId transportId) {
public TransportDisabledEvent(TransportId transportId) {
this.transportId = transportId;
}

View File

@@ -2,22 +2,20 @@ package org.briarproject.bramble.api.plugin.event;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Plugin.State;
import org.briarproject.bramble.api.plugin.TransportId;
import javax.annotation.concurrent.Immutable;
/**
* An event that is broadcast when a plugin leaves the {@link State#ACTIVE}
* state.
* An event that is broadcast when a transport is enabled.
*/
@Immutable
@NotNullByDefault
public class TransportInactiveEvent extends Event {
public class TransportEnabledEvent extends Event {
private final TransportId transportId;
public TransportInactiveEvent(TransportId transportId) {
public TransportEnabledEvent(TransportId transportId) {
this.transportId = transportId;
}

View File

@@ -1,32 +0,0 @@
package org.briarproject.bramble.api.plugin.event;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Plugin.State;
import org.briarproject.bramble.api.plugin.TransportId;
import javax.annotation.concurrent.Immutable;
/**
* An event that is broadcast when the {@link State state} of a plugin changes.
*/
@Immutable
@NotNullByDefault
public class TransportStateEvent extends Event {
private final TransportId transportId;
private final State state;
public TransportStateEvent(TransportId transportId, State state) {
this.transportId = transportId;
this.state = state;
}
public TransportId getTransportId() {
return transportId;
}
public State getState() {
return state;
}
}

View File

@@ -5,7 +5,6 @@ import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.system.Wakeful;
import javax.annotation.Nullable;
@@ -19,7 +18,6 @@ public interface SimplexPlugin extends Plugin {
* Attempts to create and return a reader for the given transport
* properties. Returns null if a reader cannot be created.
*/
@Wakeful
@Nullable
TransportConnectionReader createReader(TransportProperties p);
@@ -27,7 +25,6 @@ public interface SimplexPlugin extends Plugin {
* Attempts to create and return a writer for the given transport
* properties. Returns null if a writer cannot be created.
*/
@Wakeful
@Nullable
TransportConnectionWriter createWriter(TransportProperties p);
}

View File

@@ -11,33 +11,4 @@ public interface TransportPropertyConstants {
* The maximum length of a property's key or value in UTF-8 bytes.
*/
int MAX_PROPERTY_LENGTH = 100;
/**
* Prefix for keys that represent reflected properties.
*/
String REFLECTED_PROPERTY_PREFIX = "u:";
/**
* 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,
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.
*/

View File

@@ -1,27 +0,0 @@
package org.briarproject.bramble.api.properties.event;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.properties.TransportProperties;
import javax.annotation.concurrent.Immutable;
/**
* An event that is broadcast when {@link TransportProperties} are received
* from a contact.
*/
@Immutable
@NotNullByDefault
public class RemoteTransportPropertiesUpdatedEvent extends Event {
private final TransportId transportId;
public RemoteTransportPropertiesUpdatedEvent(TransportId transportId) {
this.transportId = transportId;
}
public TransportId getTransportId() {
return transportId;
}
}

View File

@@ -1,23 +0,0 @@
package org.briarproject.bramble.api.sync;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* A record containing a nonce for choosing between redundant sessions.
*/
@Immutable
@NotNullByDefault
public class Priority {
private final byte[] nonce;
public Priority(byte[] nonce) {
this.nonce = nonce;
}
public byte[] getNonce() {
return nonce;
}
}

View File

@@ -1,13 +0,0 @@
package org.briarproject.bramble.api.sync;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
/**
* An interface for handling a {@link Priority} record received by an
* incoming {@link SyncSession}.
*/
@NotNullByDefault
public interface PriorityHandler {
void handle(Priority p);
}

View File

@@ -10,5 +10,4 @@ public interface RecordTypes {
byte OFFER = 2;
byte REQUEST = 3;
byte VERSIONS = 4;
byte PRIORITY = 5;
}

View File

@@ -49,10 +49,4 @@ public interface SyncConstants {
* simultaneously.
*/
int MAX_SUPPORTED_VERSIONS = 10;
/**
* The length of the priority nonce used for choosing between redundant
* connections.
*/
int PRIORITY_NONCE_BYTES = 16;
}

View File

@@ -28,8 +28,4 @@ public interface SyncRecordReader {
boolean hasVersions() throws IOException;
Versions readVersions() throws IOException;
boolean hasPriority() throws IOException;
Priority readPriority() throws IOException;
}

View File

@@ -17,7 +17,5 @@ public interface SyncRecordWriter {
void writeVersions(Versions v) throws IOException;
void writePriority(Priority p) throws IOException;
void flush() throws IOException;
}

View File

@@ -2,23 +2,18 @@ package org.briarproject.bramble.api.sync;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.transport.StreamWriter;
import java.io.InputStream;
import javax.annotation.Nullable;
@NotNullByDefault
public interface SyncSessionFactory {
SyncSession createIncomingSession(ContactId c, InputStream in,
PriorityHandler handler);
SyncSession createIncomingSession(ContactId c, InputStream in);
SyncSession createSimplexOutgoingSession(ContactId c, TransportId t,
int maxLatency, StreamWriter streamWriter);
SyncSession createSimplexOutgoingSession(ContactId c, int maxLatency,
StreamWriter streamWriter);
SyncSession createDuplexOutgoingSession(ContactId c, TransportId t,
int maxLatency, int maxIdleTime, StreamWriter streamWriter,
@Nullable Priority priority);
SyncSession createDuplexOutgoingSession(ContactId c, int maxLatency,
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,6 +1,5 @@
package org.briarproject.bramble.api.plugin;
package org.briarproject.bramble.api.system;
import java.io.File;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@@ -12,11 +11,15 @@ import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Annotation for injecting the {@link File directory} where the Tor plugin
* should store its state.
* Annotation for injecting a scheduled executor service
* that can be used to schedule the execution of tasks.
* <p>
* The service should <b>only</b> be used for running tasks on other executors
* at scheduled times.
* No significant work should be run by the service itself!
*/
@Qualifier
@Target({FIELD, METHOD, PARAMETER})
@Retention(RUNTIME)
public @interface TorDirectory {
}
public @interface Scheduler {
}

View File

@@ -1,43 +0,0 @@
package org.briarproject.bramble.api.system;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
/**
* A service that can be used to schedule the execution of tasks.
*/
@NotNullByDefault
public interface TaskScheduler {
/**
* Submits the given task to the given executor after the given delay.
* <p>
* If the platform supports wake locks, a wake lock will be held while
* submitting and running the task.
*/
Cancellable schedule(Runnable task, Executor executor, long delay,
TimeUnit unit);
/**
* Submits the given task to the given executor after the given delay,
* and then repeatedly with the given interval between executions
* (measured from the end of one execution to the beginning of the next).
* <p>
* If the platform supports wake locks, a wake lock will be held while
* submitting and running the task.
*/
Cancellable scheduleWithFixedDelay(Runnable task, Executor executor,
long delay, long interval, TimeUnit unit);
interface Cancellable {
/**
* Cancels the task if it has not already started running. If the task
* is {@link #scheduleWithFixedDelay(Runnable, Executor, long, long, TimeUnit) periodic},
* all future executions of the task are cancelled.
*/
void cancel();
}
}

View File

@@ -1,19 +0,0 @@
package org.briarproject.bramble.api.system;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Annotation for methods that must be called while holding a wake lock, if
* the platform supports wake locks.
*/
@Qualifier
@Target(METHOD)
@Retention(RUNTIME)
public @interface Wakeful {
}

View File

@@ -1,26 +0,0 @@
package org.briarproject.bramble.api.system;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Annotation for injecting the executor for long-running IO tasks that should
* run without sleeping. Also used for annotating methods that should run on
* this executor.
* <p>
* The contract of this executor is that tasks may be run concurrently, and
* submitting a task will never block. Tasks must not run indefinitely. Tasks
* submitted during shutdown are discarded.
*/
@Qualifier
@Target({FIELD, METHOD, PARAMETER})
@Retention(RUNTIME)
public @interface WakefulIoExecutor {
}

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);
}
}
public static boolean isNonEmptyDirectory(File f) {
if (!f.isDirectory()) return false;
File[] children = f.listFiles();
return children != null && children.length > 0;
}
}

View File

@@ -2,17 +2,13 @@ package org.briarproject.bramble.util;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import javax.annotation.Nullable;
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
import static org.briarproject.bramble.util.StringUtils.isValidMac;
import static org.briarproject.bramble.util.StringUtils.toHexString;
@NotNullByDefault
public class PrivacyUtils {
@@ -23,7 +19,7 @@ public class PrivacyUtils {
@Nullable
public static String scrubMacAddress(@Nullable String address) {
if (isNullOrEmpty(address) || !isValidMac(address)) return address;
if (address == null || address.length() == 0) return null;
// this is a fake address we need to know about
if (address.equals("02:00:00:00:00:00")) return address;
// keep first and last octet of MAC address
@@ -31,37 +27,39 @@ public class PrivacyUtils {
+ address.substring(14, 17);
}
@Nullable
public static String scrubInetAddress(InetAddress address) {
if (address instanceof Inet4Address) {
// Don't scrub local IPv4 addresses
if (address.isLoopbackAddress() || address.isLinkLocalAddress() ||
address.isSiteLocalAddress()) {
return address.getHostAddress();
}
// Keep first and last octet of non-local IPv4 addresses
return scrubIpv4Address(address.getAddress());
} else {
// Keep first and last octet of IPv6 addresses
return scrubIpv6Address(address.getAddress());
}
// don't scrub link and site local addresses
if (address.isLinkLocalAddress() || address.isSiteLocalAddress())
return address.toString();
// completely scrub IPv6 addresses
if (address instanceof Inet6Address) return "[scrubbed]";
// keep first and last octet of IPv4 addresses
return scrubInetAddress(address.toString());
}
private static String scrubIpv4Address(byte[] ipv4) {
return (ipv4[0] & 0xFF) + ".[scrubbed]." + (ipv4[3] & 0xFF);
}
private static String scrubIpv6Address(byte[] ipv6) {
String hex = toHexString(ipv6).toLowerCase();
return hex.substring(0, 2) + "[scrubbed]" + hex.substring(30);
@Nullable
public static String scrubInetAddress(@Nullable String address) {
if (address == null) return null;
int firstDot = address.indexOf(".");
if (firstDot == -1) return "[scrubbed]";
String prefix = address.substring(0, firstDot + 1);
int lastDot = address.lastIndexOf(".");
String suffix = address.substring(lastDot, address.length());
return prefix + "[scrubbed]" + suffix;
}
@Nullable
public static String scrubSocketAddress(InetSocketAddress address) {
return scrubInetAddress(address.getAddress());
InetAddress inetAddress = address.getAddress();
return scrubInetAddress(inetAddress);
}
@Nullable
public static String scrubSocketAddress(SocketAddress address) {
if (address instanceof InetSocketAddress)
return scrubSocketAddress((InetSocketAddress) address);
return "[scrubbed]";
return scrubInetAddress(address.toString());
}
}

View File

@@ -2,7 +2,7 @@ dependencyVerification {
verify = [
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
'com.google.dagger:dagger: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',
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
'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.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 '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-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'
}

View File

@@ -9,6 +9,7 @@ import org.briarproject.bramble.plugin.PluginModule;
import org.briarproject.bramble.properties.PropertiesModule;
import org.briarproject.bramble.rendezvous.RendezvousModule;
import org.briarproject.bramble.sync.validation.ValidationModule;
import org.briarproject.bramble.system.SystemModule;
import org.briarproject.bramble.transport.TransportModule;
import org.briarproject.bramble.versioning.VersioningModule;
@@ -30,26 +31,26 @@ public interface BrambleCoreEagerSingletons {
void inject(RendezvousModule.EagerSingletons init);
void inject(SystemModule.EagerSingletons init);
void inject(TransportModule.EagerSingletons init);
void inject(ValidationModule.EagerSingletons init);
void inject(VersioningModule.EagerSingletons init);
class Helper {
public static void injectEagerSingletons(BrambleCoreEagerSingletons c) {
c.inject(new ContactModule.EagerSingletons());
c.inject(new CryptoExecutorModule.EagerSingletons());
c.inject(new DatabaseExecutorModule.EagerSingletons());
c.inject(new IdentityModule.EagerSingletons());
c.inject(new LifecycleModule.EagerSingletons());
c.inject(new RendezvousModule.EagerSingletons());
c.inject(new PluginModule.EagerSingletons());
c.inject(new PropertiesModule.EagerSingletons());
c.inject(new TransportModule.EagerSingletons());
c.inject(new ValidationModule.EagerSingletons());
c.inject(new VersioningModule.EagerSingletons());
}
default void injectBrambleCoreEagerSingletons() {
inject(new ContactModule.EagerSingletons());
inject(new CryptoExecutorModule.EagerSingletons());
inject(new DatabaseExecutorModule.EagerSingletons());
inject(new IdentityModule.EagerSingletons());
inject(new LifecycleModule.EagerSingletons());
inject(new RendezvousModule.EagerSingletons());
inject(new PluginModule.EagerSingletons());
inject(new PropertiesModule.EagerSingletons());
inject(new SystemModule.EagerSingletons());
inject(new TransportModule.EagerSingletons());
inject(new ValidationModule.EagerSingletons());
inject(new VersioningModule.EagerSingletons());
}
}

View File

@@ -1,7 +1,6 @@
package org.briarproject.bramble;
import org.briarproject.bramble.client.ClientModule;
import org.briarproject.bramble.connection.ConnectionModule;
import org.briarproject.bramble.contact.ContactModule;
import org.briarproject.bramble.crypto.CryptoExecutorModule;
import org.briarproject.bramble.crypto.CryptoModule;
@@ -10,7 +9,6 @@ import org.briarproject.bramble.db.DatabaseExecutorModule;
import org.briarproject.bramble.db.DatabaseModule;
import org.briarproject.bramble.event.EventModule;
import org.briarproject.bramble.identity.IdentityModule;
import org.briarproject.bramble.io.IoModule;
import org.briarproject.bramble.keyagreement.KeyAgreementModule;
import org.briarproject.bramble.lifecycle.LifecycleModule;
import org.briarproject.bramble.plugin.PluginModule;
@@ -21,7 +19,7 @@ import org.briarproject.bramble.rendezvous.RendezvousModule;
import org.briarproject.bramble.settings.SettingsModule;
import org.briarproject.bramble.sync.SyncModule;
import org.briarproject.bramble.sync.validation.ValidationModule;
import org.briarproject.bramble.system.ClockModule;
import org.briarproject.bramble.system.SystemModule;
import org.briarproject.bramble.transport.TransportModule;
import org.briarproject.bramble.versioning.VersioningModule;
@@ -29,8 +27,6 @@ import dagger.Module;
@Module(includes = {
ClientModule.class,
ClockModule.class,
ConnectionModule.class,
ContactModule.class,
CryptoModule.class,
CryptoExecutorModule.class,
@@ -39,7 +35,6 @@ import dagger.Module;
DatabaseExecutorModule.class,
EventModule.class,
IdentityModule.class,
IoModule.class,
KeyAgreementModule.class,
LifecycleModule.class,
PluginModule.class,
@@ -49,9 +44,14 @@ import dagger.Module;
RendezvousModule.class,
SettingsModule.class,
SyncModule.class,
SystemModule.class,
TransportModule.class,
ValidationModule.class,
VersioningModule.class
})
public class BrambleCoreModule {
public static void initEagerSingletons(BrambleCoreEagerSingletons c) {
c.injectBrambleCoreEagerSingletons();
}
}

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