mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
Compare commits
64 Commits
alpha-1.5.
...
client-ver
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
72136cc627 | ||
|
|
411ace13aa | ||
|
|
952ee42ad1 | ||
|
|
f61b09d5a9 | ||
|
|
8f735d176e | ||
|
|
c47253fc5f | ||
|
|
7a0fb74c09 | ||
|
|
882f536b8d | ||
|
|
74f8e84a9b | ||
|
|
23df2a41c2 | ||
|
|
c77eaf16d9 | ||
|
|
9a6bb4b203 | ||
|
|
3d237a9104 | ||
|
|
fa216ffc6f | ||
|
|
a34631d36c | ||
|
|
45cda191e5 | ||
|
|
2495b6f5c0 | ||
|
|
03fc504f7d | ||
|
|
d19062e319 | ||
|
|
fdb429ab7a | ||
|
|
d0c59a6d75 | ||
|
|
3bb39c2aa3 | ||
|
|
917fc5e5b6 | ||
|
|
caa078585b | ||
|
|
e68c0c7f4b | ||
|
|
a6b3749fb6 | ||
|
|
a8f6e8e4bd | ||
|
|
4d884601f0 | ||
|
|
b71198d9b1 | ||
|
|
079c6e0475 | ||
|
|
3a0f8ed85c | ||
|
|
57f7501780 | ||
|
|
3cc5699fe0 | ||
|
|
7d761710e6 | ||
|
|
7461d3c943 | ||
|
|
9291613175 | ||
|
|
ce6739a9fd | ||
|
|
1f1a97f62d | ||
|
|
7a33d26533 | ||
|
|
f2c85f37be | ||
|
|
8e3fa872fd | ||
|
|
0d1e81ebdb | ||
|
|
bded4e7bc8 | ||
|
|
bf1a5cf218 | ||
|
|
dd7a638984 | ||
|
|
942222131e | ||
|
|
643757e407 | ||
|
|
7c530ad7a3 | ||
|
|
23b2dfa4a8 | ||
|
|
ce10e6770f | ||
|
|
b88dbee881 | ||
|
|
0ca21ad4c0 | ||
|
|
a14f62dcc3 | ||
|
|
f0c1ebcc1b | ||
|
|
4a4b04bec3 | ||
|
|
6f57ec8281 | ||
|
|
0eb0bbdc99 | ||
|
|
76344344d2 | ||
|
|
fbc32830bd | ||
|
|
145117a1dc | ||
|
|
6ed55bcd7d | ||
|
|
f8015272f4 | ||
|
|
462f57c966 | ||
|
|
8d20c5d8b8 |
@@ -11,10 +11,10 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 16
|
minSdkVersion 21
|
||||||
targetSdkVersion 31
|
targetSdkVersion 33
|
||||||
versionCode 10501
|
versionCode 10504
|
||||||
versionName "1.5.1"
|
versionName "1.5.4"
|
||||||
consumerProguardFiles 'proguard-rules.txt'
|
consumerProguardFiles 'proguard-rules.txt'
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
@@ -40,7 +40,7 @@ configurations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api 'org.briarproject:dont-kill-me-lib:0.2.6'
|
api 'org.briarproject:dont-kill-me-lib:0.2.7'
|
||||||
|
|
||||||
// In theory this dependency shouldn't be needed, but without it Android Studio's linter will
|
// In theory this dependency shouldn't be needed, but without it Android Studio's linter will
|
||||||
// complain about unresolved symbols for bramble-api test classes in bramble-android tests,
|
// complain about unresolved symbols for bramble-api test classes in bramble-android tests,
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
package org.briarproject.android.dontkillmelib.wakelock;
|
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import dagger.Module;
|
|
||||||
import dagger.Provides;
|
|
||||||
|
|
||||||
@Module
|
|
||||||
public class AndroidWakeLockModule {
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
AndroidWakeLockManager provideWakeLockManager(
|
|
||||||
AndroidWakeLockManagerImpl wakeLockManager) {
|
|
||||||
return wakeLockManager;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
package org.briarproject.bramble;
|
package org.briarproject.bramble;
|
||||||
|
|
||||||
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockModule;
|
|
||||||
import org.briarproject.bramble.battery.AndroidBatteryModule;
|
import org.briarproject.bramble.battery.AndroidBatteryModule;
|
||||||
import org.briarproject.bramble.io.DnsModule;
|
import org.briarproject.bramble.io.DnsModule;
|
||||||
import org.briarproject.bramble.network.AndroidNetworkModule;
|
import org.briarproject.bramble.network.AndroidNetworkModule;
|
||||||
|
import org.briarproject.bramble.plugin.tor.CircumventionModule;
|
||||||
import org.briarproject.bramble.reporting.ReportingModule;
|
import org.briarproject.bramble.reporting.ReportingModule;
|
||||||
import org.briarproject.bramble.socks.SocksModule;
|
import org.briarproject.bramble.socks.SocksModule;
|
||||||
import org.briarproject.bramble.system.AndroidSystemModule;
|
import org.briarproject.bramble.system.AndroidSystemModule;
|
||||||
import org.briarproject.bramble.system.AndroidTaskSchedulerModule;
|
import org.briarproject.bramble.system.AndroidTaskSchedulerModule;
|
||||||
|
import org.briarproject.bramble.system.AndroidWakeLockModule;
|
||||||
import org.briarproject.bramble.system.AndroidWakefulIoExecutorModule;
|
import org.briarproject.bramble.system.AndroidWakefulIoExecutorModule;
|
||||||
import org.briarproject.bramble.system.DefaultThreadFactoryModule;
|
import org.briarproject.bramble.system.DefaultThreadFactoryModule;
|
||||||
import org.briarproject.onionwrapper.CircumventionModule;
|
|
||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import javax.annotation.Nullable;
|
|||||||
import javax.annotation.concurrent.GuardedBy;
|
import javax.annotation.concurrent.GuardedBy;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
import static org.briarproject.bramble.util.IoUtils.deleteFileOrDir;
|
import static org.briarproject.bramble.util.IoUtils.deleteFileOrDir;
|
||||||
@@ -105,15 +104,11 @@ class AndroidAccountManager extends AccountManagerImpl
|
|||||||
}
|
}
|
||||||
files.add(appContext.getFilesDir());
|
files.add(appContext.getFilesDir());
|
||||||
addIfNotNull(files, appContext.getExternalCacheDir());
|
addIfNotNull(files, appContext.getExternalCacheDir());
|
||||||
if (SDK_INT >= 19) {
|
for (File file : appContext.getExternalCacheDirs()) {
|
||||||
for (File file : appContext.getExternalCacheDirs()) {
|
addIfNotNull(files, file);
|
||||||
addIfNotNull(files, file);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (SDK_INT >= 21) {
|
for (File file : appContext.getExternalMediaDirs()) {
|
||||||
for (File file : appContext.getExternalMediaDirs()) {
|
addIfNotNull(files, file);
|
||||||
addIfNotNull(files, file);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Clear the cache directory but don't delete it
|
// Clear the cache directory but don't delete it
|
||||||
File cacheDir = appContext.getCacheDir();
|
File cacheDir = appContext.getCacheDir();
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import android.content.BroadcastReceiver;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
|
import android.os.PowerManager;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.battery.BatteryManager;
|
import org.briarproject.bramble.api.battery.BatteryManager;
|
||||||
import org.briarproject.bramble.api.battery.event.BatteryEvent;
|
import org.briarproject.bramble.api.battery.event.BatteryEvent;
|
||||||
@@ -16,10 +17,17 @@ import java.util.logging.Logger;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
|
||||||
import static android.content.Intent.ACTION_BATTERY_CHANGED;
|
import static android.content.Intent.ACTION_BATTERY_CHANGED;
|
||||||
import static android.content.Intent.ACTION_POWER_CONNECTED;
|
import static android.content.Intent.ACTION_POWER_CONNECTED;
|
||||||
import static android.content.Intent.ACTION_POWER_DISCONNECTED;
|
import static android.content.Intent.ACTION_POWER_DISCONNECTED;
|
||||||
import static android.os.BatteryManager.EXTRA_PLUGGED;
|
import static android.os.BatteryManager.EXTRA_PLUGGED;
|
||||||
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
|
import static android.os.PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED;
|
||||||
|
import static android.os.PowerManager.ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED;
|
||||||
|
import static android.os.PowerManager.ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED;
|
||||||
|
import static android.os.PowerManager.ACTION_POWER_SAVE_MODE_CHANGED;
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
import static java.util.logging.Logger.getLogger;
|
import static java.util.logging.Logger.getLogger;
|
||||||
|
|
||||||
@@ -57,6 +65,12 @@ class AndroidBatteryManager implements BatteryManager, Service {
|
|||||||
IntentFilter filter = new IntentFilter();
|
IntentFilter filter = new IntentFilter();
|
||||||
filter.addAction(ACTION_POWER_CONNECTED);
|
filter.addAction(ACTION_POWER_CONNECTED);
|
||||||
filter.addAction(ACTION_POWER_DISCONNECTED);
|
filter.addAction(ACTION_POWER_DISCONNECTED);
|
||||||
|
filter.addAction(ACTION_POWER_SAVE_MODE_CHANGED);
|
||||||
|
if (SDK_INT >= 23) filter.addAction(ACTION_DEVICE_IDLE_MODE_CHANGED);
|
||||||
|
if (SDK_INT >= 33) {
|
||||||
|
filter.addAction(ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED);
|
||||||
|
filter.addAction(ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED);
|
||||||
|
}
|
||||||
appContext.registerReceiver(batteryReceiver, filter);
|
appContext.registerReceiver(batteryReceiver, filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,6 +90,33 @@ class AndroidBatteryManager implements BatteryManager, Service {
|
|||||||
eventBus.broadcast(new BatteryEvent(true));
|
eventBus.broadcast(new BatteryEvent(true));
|
||||||
else if (ACTION_POWER_DISCONNECTED.equals(action))
|
else if (ACTION_POWER_DISCONNECTED.equals(action))
|
||||||
eventBus.broadcast(new BatteryEvent(false));
|
eventBus.broadcast(new BatteryEvent(false));
|
||||||
|
else if (SDK_INT >= 23 &&
|
||||||
|
ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action) &&
|
||||||
|
LOG.isLoggable(INFO)) {
|
||||||
|
LOG.info("Device idle mode changed to: " +
|
||||||
|
getPowerManager(ctx).isDeviceIdleMode());
|
||||||
|
} else if (SDK_INT >= 23 &&
|
||||||
|
ACTION_POWER_SAVE_MODE_CHANGED.equals(action) &&
|
||||||
|
LOG.isLoggable(INFO)) {
|
||||||
|
LOG.info("Power save mode changed to: " +
|
||||||
|
getPowerManager(ctx).isPowerSaveMode());
|
||||||
|
} else if (SDK_INT >= 33 && LOG.isLoggable(INFO) &&
|
||||||
|
ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED.equals(action)) {
|
||||||
|
PowerManager powerManager =
|
||||||
|
ctx.getSystemService(PowerManager.class);
|
||||||
|
LOG.info("Low power standby now is: " +
|
||||||
|
powerManager.isLowPowerStandbyEnabled());
|
||||||
|
} else if (SDK_INT >= 33 && LOG.isLoggable(INFO) &&
|
||||||
|
ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED.equals(action)) {
|
||||||
|
PowerManager powerManager = getPowerManager(ctx);
|
||||||
|
LOG.info("Light idle mode now is: " +
|
||||||
|
powerManager.isDeviceLightIdleMode());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequiresApi(api = 23)
|
||||||
|
private PowerManager getPowerManager(Context ctx) {
|
||||||
|
return ctx.getSystemService(PowerManager.class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.plugin.bluetooth;
|
package org.briarproject.bramble.plugin.bluetooth;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.bluetooth.BluetoothAdapter;
|
import android.bluetooth.BluetoothAdapter;
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
@@ -49,7 +50,6 @@ import static android.bluetooth.BluetoothAdapter.STATE_ON;
|
|||||||
import static android.bluetooth.BluetoothDevice.ACTION_FOUND;
|
import static android.bluetooth.BluetoothDevice.ACTION_FOUND;
|
||||||
import static android.bluetooth.BluetoothDevice.DEVICE_TYPE_LE;
|
import static android.bluetooth.BluetoothDevice.DEVICE_TYPE_LE;
|
||||||
import static android.bluetooth.BluetoothDevice.EXTRA_DEVICE;
|
import static android.bluetooth.BluetoothDevice.EXTRA_DEVICE;
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
|
||||||
import static java.util.Collections.shuffle;
|
import static java.util.Collections.shuffle;
|
||||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
@@ -60,6 +60,7 @@ import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
|
|||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
|
@SuppressLint("MissingPermission")
|
||||||
class AndroidBluetoothPlugin extends
|
class AndroidBluetoothPlugin extends
|
||||||
AbstractBluetoothPlugin<BluetoothSocket, BluetoothServerSocket> {
|
AbstractBluetoothPlugin<BluetoothSocket, BluetoothServerSocket> {
|
||||||
|
|
||||||
@@ -253,7 +254,7 @@ class AndroidBluetoothPlugin extends
|
|||||||
} else if (ACTION_FOUND.equals(action)) {
|
} else if (ACTION_FOUND.equals(action)) {
|
||||||
BluetoothDevice d = i.getParcelableExtra(EXTRA_DEVICE);
|
BluetoothDevice d = i.getParcelableExtra(EXTRA_DEVICE);
|
||||||
// Ignore Bluetooth LE devices
|
// Ignore Bluetooth LE devices
|
||||||
if (SDK_INT < 18 || d.getType() != DEVICE_TYPE_LE) {
|
if (d.getType() != DEVICE_TYPE_LE) {
|
||||||
String address = d.getAddress();
|
String address = d.getAddress();
|
||||||
if (LOG.isLoggable(INFO))
|
if (LOG.isLoggable(INFO))
|
||||||
LOG.info("Discovered " +
|
LOG.info("Discovered " +
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.briarproject.bramble.plugin.tcp;
|
package org.briarproject.bramble.plugin.tcp;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
import android.net.LinkAddress;
|
import android.net.LinkAddress;
|
||||||
@@ -37,7 +36,6 @@ import javax.net.SocketFactory;
|
|||||||
import static android.content.Context.CONNECTIVITY_SERVICE;
|
import static android.content.Context.CONNECTIVITY_SERVICE;
|
||||||
import static android.content.Context.WIFI_SERVICE;
|
import static android.content.Context.WIFI_SERVICE;
|
||||||
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
|
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
|
||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
import static java.util.Collections.list;
|
import static java.util.Collections.list;
|
||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
@@ -118,7 +116,7 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
|
|||||||
// If there's no wifi IPv4 address, we might be a client on an
|
// 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+
|
// IPv6-only wifi network. We can only detect this on API 21+
|
||||||
if (wifi == null) {
|
if (wifi == null) {
|
||||||
return SDK_INT >= 21 ? getWifiClientIpv6Address() : null;
|
return getWifiClientIpv6Address();
|
||||||
}
|
}
|
||||||
// Use the wifi IPv4 address to determine which interface's IPv6
|
// Use the wifi IPv4 address to determine which interface's IPv6
|
||||||
// address we should return (if the interface has a suitable address)
|
// address we should return (if the interface has a suitable address)
|
||||||
@@ -172,7 +170,6 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
|
|||||||
* Returns a link-local IPv6 address for the wifi client interface, or null
|
* 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.
|
* if there's no such interface or it doesn't have a suitable address.
|
||||||
*/
|
*/
|
||||||
@TargetApi(21)
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private InetAddress getWifiClientIpv6Address() {
|
private InetAddress getWifiClientIpv6Address() {
|
||||||
// https://issuetracker.google.com/issues/175055271
|
// https://issuetracker.google.com/issues/175055271
|
||||||
@@ -234,7 +231,6 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
|
|||||||
// On API 21 and later, a socket that is not created with the wifi
|
// On API 21 and later, a socket that is not created with the wifi
|
||||||
// network's socket factory may try to connect via another network
|
// network's socket factory may try to connect via another network
|
||||||
private SocketFactory getSocketFactory() {
|
private SocketFactory getSocketFactory() {
|
||||||
if (SDK_INT < 21) return SocketFactory.getDefault();
|
|
||||||
// https://issuetracker.google.com/issues/175055271
|
// https://issuetracker.google.com/issues/175055271
|
||||||
try {
|
try {
|
||||||
for (Network net : connectivityManager.getAllNetworks()) {
|
for (Network net : connectivityManager.getAllNetworks()) {
|
||||||
@@ -302,7 +298,7 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
|
|||||||
Pair<InetAddress, Boolean> wifi = getWifiIpv4Address();
|
Pair<InetAddress, Boolean> wifi = getWifiIpv4Address();
|
||||||
// If there's no wifi IPv4 address, we might be a client on an
|
// 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+
|
// IPv6-only wifi network. We can only detect this on API 21+
|
||||||
if (wifi == null && SDK_INT >= 21) {
|
if (wifi == null) {
|
||||||
InetAddress ipv6 = getWifiClientIpv6Address();
|
InetAddress ipv6 = getWifiClientIpv6Address();
|
||||||
if (ipv6 != null) return new Pair<>(ipv6, false);
|
if (ipv6 != null) return new Pair<>(ipv6, false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,11 +16,11 @@ import org.briarproject.bramble.api.plugin.TorControlPort;
|
|||||||
import org.briarproject.bramble.api.plugin.TorDirectory;
|
import org.briarproject.bramble.api.plugin.TorDirectory;
|
||||||
import org.briarproject.bramble.api.plugin.TorSocksPort;
|
import org.briarproject.bramble.api.plugin.TorSocksPort;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
|
||||||
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.onionwrapper.AndroidTorWrapper;
|
import org.briarproject.onionwrapper.AndroidTorWrapper;
|
||||||
import org.briarproject.onionwrapper.CircumventionProvider;
|
import org.briarproject.onionwrapper.CircumventionProvider;
|
||||||
|
import org.briarproject.onionwrapper.LocationUtils;
|
||||||
import org.briarproject.onionwrapper.TorWrapper;
|
import org.briarproject.onionwrapper.TorWrapper;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|||||||
@@ -1,72 +0,0 @@
|
|||||||
package org.briarproject.bramble.system;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.app.Application;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.telephony.TelephonyManager;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import static android.content.Context.TELEPHONY_SERVICE;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
class AndroidLocationUtils implements LocationUtils {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
Logger.getLogger(AndroidLocationUtils.class.getName());
|
|
||||||
|
|
||||||
private final Context appContext;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
AndroidLocationUtils(Application app) {
|
|
||||||
appContext = app.getApplicationContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This guesses the current country from the first of these sources that
|
|
||||||
* succeeds (also in order of likelihood of being correct):
|
|
||||||
*
|
|
||||||
* <ul>
|
|
||||||
* <li>Phone network. This works even when no SIM card is inserted, or a
|
|
||||||
* foreign SIM card is inserted.</li>
|
|
||||||
* <li>SIM card. This is only an heuristic and assumes the user is not
|
|
||||||
* roaming.</li>
|
|
||||||
* <li>User locale. This is an even worse heuristic.</li>
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* Note: this is very similar to <a href="https://android.googlesource.com/platform/frameworks/base/+/cd92588%5E/location/java/android/location/CountryDetector.java">
|
|
||||||
* this API</a> except it seems that Google doesn't want us to use it for
|
|
||||||
* some reason - both that class and {@code Context.COUNTRY_CODE} are
|
|
||||||
* annotated {@code @hide}.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
@SuppressLint("DefaultLocale")
|
|
||||||
public String getCurrentCountry() {
|
|
||||||
String countryCode = getCountryFromPhoneNetwork();
|
|
||||||
if (!TextUtils.isEmpty(countryCode)) return countryCode.toUpperCase();
|
|
||||||
LOG.info("Falling back to SIM card country");
|
|
||||||
countryCode = getCountryFromSimCard();
|
|
||||||
if (!TextUtils.isEmpty(countryCode)) return countryCode.toUpperCase();
|
|
||||||
LOG.info("Falling back to user-defined locale");
|
|
||||||
return Locale.getDefault().getCountry();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getCountryFromPhoneNetwork() {
|
|
||||||
Object o = appContext.getSystemService(TELEPHONY_SERVICE);
|
|
||||||
TelephonyManager tm = (TelephonyManager) o;
|
|
||||||
return tm == null ? "" : tm.getNetworkCountryIso();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getCountryFromSimCard() {
|
|
||||||
Object o = appContext.getSystemService(TELEPHONY_SERVICE);
|
|
||||||
TelephonyManager tm = (TelephonyManager) o;
|
|
||||||
return tm == null ? "" : tm.getSimCountryIso();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -31,8 +31,6 @@ import static android.provider.Settings.Secure.ANDROID_ID;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class AndroidSecureRandomProvider extends UnixSecureRandomProvider {
|
class AndroidSecureRandomProvider extends UnixSecureRandomProvider {
|
||||||
|
|
||||||
private static final int SEED_LENGTH = 32;
|
|
||||||
|
|
||||||
private final Context appContext;
|
private final Context appContext;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@@ -72,27 +70,6 @@ class AndroidSecureRandomProvider extends UnixSecureRandomProvider {
|
|||||||
// Silence strict mode
|
// Silence strict mode
|
||||||
StrictMode.ThreadPolicy tp = StrictMode.allowThreadDiskWrites();
|
StrictMode.ThreadPolicy tp = StrictMode.allowThreadDiskWrites();
|
||||||
super.writeSeed();
|
super.writeSeed();
|
||||||
if (SDK_INT <= 18) applyOpenSslFix();
|
|
||||||
StrictMode.setThreadPolicy(tp);
|
StrictMode.setThreadPolicy(tp);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
|
|
||||||
private void applyOpenSslFix() {
|
|
||||||
byte[] seed = new UnixSecureRandomSpi().engineGenerateSeed(
|
|
||||||
SEED_LENGTH);
|
|
||||||
try {
|
|
||||||
// Seed the OpenSSL PRNG
|
|
||||||
Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto")
|
|
||||||
.getMethod("RAND_seed", byte[].class)
|
|
||||||
.invoke(null, (Object) seed);
|
|
||||||
// Mix the output of the Linux PRNG into the OpenSSL PRNG
|
|
||||||
int bytesRead = (Integer) Class.forName(
|
|
||||||
"org.apache.harmony.xnet.provider.jsse.NativeCrypto")
|
|
||||||
.getMethod("RAND_load_file", String.class, long.class)
|
|
||||||
.invoke(null, "/dev/urandom", 1024);
|
|
||||||
if (bytesRead != 1024) throw new IOException();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new SecurityException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
package org.briarproject.bramble.system;
|
package org.briarproject.bramble.system;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.event.EventExecutor;
|
import org.briarproject.bramble.api.event.EventExecutor;
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
|
||||||
import org.briarproject.bramble.api.system.ResourceProvider;
|
import org.briarproject.bramble.api.system.ResourceProvider;
|
||||||
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||||
|
import org.briarproject.onionwrapper.AndroidLocationUtilsFactory;
|
||||||
|
import org.briarproject.onionwrapper.LocationUtils;
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.RejectedExecutionHandler;
|
import java.util.concurrent.RejectedExecutionHandler;
|
||||||
@@ -45,8 +48,9 @@ public class AndroidSystemModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
LocationUtils provideLocationUtils(AndroidLocationUtils locationUtils) {
|
@Singleton
|
||||||
return locationUtils;
|
LocationUtils provideLocationUtils(Application app) {
|
||||||
|
return AndroidLocationUtilsFactory.createAndroidLocationUtils(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package org.briarproject.bramble.system;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
|
||||||
|
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManager;
|
||||||
|
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManagerFactory;
|
||||||
|
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.Provides;
|
||||||
|
|
||||||
|
@Module
|
||||||
|
public class AndroidWakeLockModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
AndroidWakeLockManager provideWakeLockManager(Application app,
|
||||||
|
ScheduledExecutorService scheduledExecutorService) {
|
||||||
|
return AndroidWakeLockManagerFactory.createAndroidWakeLockManager(app,
|
||||||
|
scheduledExecutorService);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,14 +11,10 @@ import org.briarproject.bramble.api.Pair;
|
|||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Scanner;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
@@ -29,7 +25,6 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
|||||||
import static android.os.Build.VERSION.SDK_INT;
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
import static android.os.Process.myPid;
|
import static android.os.Process.myPid;
|
||||||
import static android.os.Process.myUid;
|
import static android.os.Process.myUid;
|
||||||
import static java.lang.Runtime.getRuntime;
|
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static org.briarproject.nullsafety.NullSafety.requireNonNull;
|
import static org.briarproject.nullsafety.NullSafety.requireNonNull;
|
||||||
|
|
||||||
@@ -43,14 +38,7 @@ public class AndroidUtils {
|
|||||||
private static final String STORED_LOGCAT = "dev-logcat";
|
private static final String STORED_LOGCAT = "dev-logcat";
|
||||||
|
|
||||||
public static Collection<String> getSupportedArchitectures() {
|
public static Collection<String> getSupportedArchitectures() {
|
||||||
List<String> abis = new ArrayList<>();
|
return asList(Build.SUPPORTED_ABIS);
|
||||||
if (SDK_INT >= 21) {
|
|
||||||
abis.addAll(asList(Build.SUPPORTED_ABIS));
|
|
||||||
} else {
|
|
||||||
abis.add(Build.CPU_ABI);
|
|
||||||
if (Build.CPU_ABI2 != null) abis.add(Build.CPU_ABI2);
|
|
||||||
}
|
|
||||||
return abis;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean hasBtConnectPermission(Context ctx) {
|
public static boolean hasBtConnectPermission(Context ctx) {
|
||||||
@@ -75,10 +63,12 @@ public class AndroidUtils {
|
|||||||
return new Pair<>(address, "adapter");
|
return new Pair<>(address, "adapter");
|
||||||
}
|
}
|
||||||
// Return the address from settings if it's valid and not fake
|
// Return the address from settings if it's valid and not fake
|
||||||
address = Settings.Secure.getString(ctx.getContentResolver(),
|
if (SDK_INT < 33) {
|
||||||
"bluetooth_address");
|
address = Settings.Secure.getString(ctx.getContentResolver(),
|
||||||
if (isValidBluetoothAddress(address)) {
|
"bluetooth_address");
|
||||||
return new Pair<>(address, "settings");
|
if (isValidBluetoothAddress(address)) {
|
||||||
|
return new Pair<>(address, "settings");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Try to get the address via reflection
|
// Try to get the address via reflection
|
||||||
address = getBluetoothAddressByReflection(adapter);
|
address = getBluetoothAddressByReflection(adapter);
|
||||||
@@ -136,19 +126,6 @@ public class AndroidUtils {
|
|||||||
return new String[] {"image/jpeg", "image/png", "image/gif"};
|
return new String[] {"image/jpeg", "image/png", "image/gif"};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public static String getSystemProperty(String propName) {
|
|
||||||
try {
|
|
||||||
Process p = getRuntime().exec("getprop " + propName);
|
|
||||||
Scanner s = new Scanner(p.getInputStream());
|
|
||||||
String line = s.nextLine();
|
|
||||||
s.close();
|
|
||||||
return line;
|
|
||||||
} catch (SecurityException | IOException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isUiThread() {
|
public static boolean isUiThread() {
|
||||||
return Looper.myLooper() == Looper.getMainLooper();
|
return Looper.myLooper() == Looper.getMainLooper();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,6 +87,10 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
|
|||||||
File potatoFile = new File(potatoDir, "file");
|
File potatoFile = new File(potatoDir, "file");
|
||||||
File filesDir = new File(testDir, "filesDir");
|
File filesDir = new File(testDir, "filesDir");
|
||||||
File externalCacheDir = new File(testDir, "externalCacheDir");
|
File externalCacheDir = new File(testDir, "externalCacheDir");
|
||||||
|
File externalCacheDir1 = new File(testDir, "externalCacheDir1");
|
||||||
|
File externalCacheDir2 = new File(testDir, "externalCacheDir2");
|
||||||
|
File externalMediaDir1 = new File(testDir, "externalMediaDir1");
|
||||||
|
File externalMediaDir2 = new File(testDir, "externalMediaDir2");
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(prefs).edit();
|
oneOf(prefs).edit();
|
||||||
@@ -109,6 +113,12 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
|
|||||||
will(returnValue(cacheDir));
|
will(returnValue(cacheDir));
|
||||||
oneOf(app).getExternalCacheDir();
|
oneOf(app).getExternalCacheDir();
|
||||||
will(returnValue(externalCacheDir));
|
will(returnValue(externalCacheDir));
|
||||||
|
oneOf(app).getExternalCacheDirs();
|
||||||
|
will(returnValue(
|
||||||
|
new File[] {externalCacheDir1, externalCacheDir2}));
|
||||||
|
oneOf(app).getExternalMediaDirs();
|
||||||
|
will(returnValue(
|
||||||
|
new File[] {externalMediaDir1, externalMediaDir2}));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
assertTrue(dbDir.mkdirs());
|
assertTrue(dbDir.mkdirs());
|
||||||
@@ -125,6 +135,10 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
|
|||||||
assertTrue(potatoFile.createNewFile());
|
assertTrue(potatoFile.createNewFile());
|
||||||
assertTrue(filesDir.mkdirs());
|
assertTrue(filesDir.mkdirs());
|
||||||
assertTrue(externalCacheDir.mkdirs());
|
assertTrue(externalCacheDir.mkdirs());
|
||||||
|
assertTrue(externalCacheDir1.mkdirs());
|
||||||
|
assertTrue(externalCacheDir2.mkdirs());
|
||||||
|
assertTrue(externalMediaDir1.mkdirs());
|
||||||
|
assertTrue(externalMediaDir2.mkdirs());
|
||||||
|
|
||||||
accountManager.deleteAccount();
|
accountManager.deleteAccount();
|
||||||
|
|
||||||
@@ -142,6 +156,10 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
|
|||||||
assertFalse(potatoFile.exists());
|
assertFalse(potatoFile.exists());
|
||||||
assertFalse(filesDir.exists());
|
assertFalse(filesDir.exists());
|
||||||
assertFalse(externalCacheDir.exists());
|
assertFalse(externalCacheDir.exists());
|
||||||
|
assertFalse(externalCacheDir1.exists());
|
||||||
|
assertFalse(externalCacheDir2.exists());
|
||||||
|
assertFalse(externalMediaDir1.exists());
|
||||||
|
assertFalse(externalMediaDir2.exists());
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ dependencyVerification {
|
|||||||
'cglib:cglib:3.2.8:cglib-3.2.8.jar:3f64de999ecc5595dc84ca8ff0879d8a34c8623f9ef3c517a53ed59023fcb9db',
|
'cglib:cglib:3.2.8:cglib-3.2.8.jar:3f64de999ecc5595dc84ca8ff0879d8a34c8623f9ef3c517a53ed59023fcb9db',
|
||||||
'com.google.code.findbugs:annotations:3.0.1:annotations-3.0.1.jar:6b47ff0a6de0ce17cbedc3abb0828ca5bce3009d53ea47b3723ff023c4742f79',
|
'com.google.code.findbugs:annotations:3.0.1:annotations-3.0.1.jar:6b47ff0a6de0ce17cbedc3abb0828ca5bce3009d53ea47b3723ff023c4742f79',
|
||||||
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
|
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
|
||||||
'com.google.dagger:dagger-compiler:2.43.2:dagger-compiler-2.43.2.jar:298c020ee6ed2f4cc651ebbfdb7f8de329b07c44b618d65be117846a850e2a03',
|
'com.google.dagger:dagger-compiler:2.45:dagger-compiler-2.45.jar:5617dfb994537dba5b41f3744a6dd13ec3cd99789c065e0d5c6fa9f21cf7ca25',
|
||||||
'com.google.dagger:dagger-producers:2.43.2:dagger-producers-2.43.2.jar:e7f5d9ffc85d48a49c8e22e02833d418f7ccad5d7512f529964db5127ab915ff',
|
'com.google.dagger:dagger-producers:2.45:dagger-producers-2.45.jar:a05abb4c3ccf6bb0f056bdcb5ef973898ecf172952ab5948a824aeea6c86ecaa',
|
||||||
'com.google.dagger:dagger-spi:2.43.2:dagger-spi-2.43.2.jar:3bae8d9dadeaaa5927da6f094389a560c12c05fec3d2711d2fa79292c7a7d7ad',
|
'com.google.dagger:dagger-spi:2.45:dagger-spi-2.45.jar:7cd6f0b09d88e64a9c97bc80e544ab8ac8fdee9301754413585a74cf64222b27',
|
||||||
'com.google.dagger:dagger:2.43.2:dagger-2.43.2.jar:c89681f7cbbf8c527bf4ac2748515d617fdb54a1d425c08d914fdc28192b5fe4',
|
'com.google.dagger:dagger:2.45:dagger-2.45.jar:f011cae7d2c0fb7ea17c34e05bc10e768b1081a5892ad019cf1fdb0e125c49c1',
|
||||||
'com.google.devtools.ksp:symbol-processing-api:1.7.0-1.0.6:symbol-processing-api-1.7.0-1.0.6.jar:adc29417be5ca9ff42118105fea4e36d9ef44987abfc41432309371a60198941',
|
'com.google.devtools.ksp:symbol-processing-api:1.7.0-1.0.6:symbol-processing-api-1.7.0-1.0.6.jar:adc29417be5ca9ff42118105fea4e36d9ef44987abfc41432309371a60198941',
|
||||||
'com.google.errorprone:error_prone_annotations:2.7.1:error_prone_annotations-2.7.1.jar:cd5257c08a246cf8628817ae71cb822be192ef91f6881ca4a3fcff4f1de1cff3',
|
'com.google.errorprone:error_prone_annotations:2.7.1:error_prone_annotations-2.7.1.jar:cd5257c08a246cf8628817ae71cb822be192ef91f6881ca4a3fcff4f1de1cff3',
|
||||||
'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30',
|
'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30',
|
||||||
@@ -17,6 +17,7 @@ dependencyVerification {
|
|||||||
'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:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava:listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar:b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99',
|
||||||
'com.google.j2objc:j2objc-annotations:1.3:j2objc-annotations-1.3.jar:21af30c92267bd6122c0e0b4d20cccb6641a37eaf956c6540ec471d584e64a7b',
|
'com.google.j2objc:j2objc-annotations:1.3:j2objc-annotations-1.3.jar:21af30c92267bd6122c0e0b4d20cccb6641a37eaf956c6540ec471d584e64a7b',
|
||||||
'com.squareup:javapoet:1.13.0:javapoet-1.13.0.jar:4c7517e848a71b36d069d12bb3bf46a70fd4cda3105d822b0ed2e19c00b69291',
|
'com.squareup:javapoet:1.13.0:javapoet-1.13.0.jar:4c7517e848a71b36d069d12bb3bf46a70fd4cda3105d822b0ed2e19c00b69291',
|
||||||
|
'com.squareup:kotlinpoet:1.11.0:kotlinpoet-1.11.0.jar:2887ada1ca03dd83baa2758640d87e840d1907564db0ef88d2289c868a980492',
|
||||||
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
|
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
|
||||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||||
'junit:junit:4.13.2:junit-4.13.2.jar:8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3',
|
'junit:junit:4.13.2:junit-4.13.2.jar:8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3',
|
||||||
@@ -24,12 +25,12 @@ dependencyVerification {
|
|||||||
'net.jcip:jcip-annotations:1.0:jcip-annotations-1.0.jar:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0',
|
'net.jcip:jcip-annotations:1.0:jcip-annotations-1.0.jar:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0',
|
||||||
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
|
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
|
||||||
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
|
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
|
||||||
'org.briarproject:dont-kill-me-lib:0.2.6:dont-kill-me-lib-0.2.6.aar:8a4cc201143227c0865c2edfba035f71109bf02e1ab26444fa3e42d3c569960f',
|
'org.briarproject:dont-kill-me-lib:0.2.7:dont-kill-me-lib-0.2.7.aar:8a9540941fd927e1c127096a7a9b4aa61ce2f2965d2e24f849be92f9e57213c4',
|
||||||
'org.briarproject:jtorctl:0.5:jtorctl-0.5.jar:43f8c7d390169772b9a2c82ab806c8414c136a2a8636c555e22754bb7260793b',
|
'org.briarproject:jtorctl:0.5:jtorctl-0.5.jar:43f8c7d390169772b9a2c82ab806c8414c136a2a8636c555e22754bb7260793b',
|
||||||
'org.briarproject:null-safety:0.1:null-safety-0.1.jar:161760de5e838cb982bafa973df820675d4397098e9a91637a36a306d43ba011',
|
'org.briarproject:null-safety:0.1:null-safety-0.1.jar:161760de5e838cb982bafa973df820675d4397098e9a91637a36a306d43ba011',
|
||||||
'org.briarproject:obfs4proxy-android:0.0.14-tor2:obfs4proxy-android-0.0.14-tor2.jar:a0a93770d6760ce57d9dbd31cc7177687374e00c3361dac22ab75e3b6e0f289e',
|
'org.briarproject:obfs4proxy-android:0.0.14-tor2:obfs4proxy-android-0.0.14-tor2.jar:a0a93770d6760ce57d9dbd31cc7177687374e00c3361dac22ab75e3b6e0f289e',
|
||||||
'org.briarproject:onionwrapper-android:0.0.1:onionwrapper-android-0.0.1.aar:959115946586daa090f057645cf75992407a59025e221c3bf88d2aa930ef3919',
|
'org.briarproject:onionwrapper-android:0.0.4:onionwrapper-android-0.0.4.aar:d761854dac454616b3e0ca099b2cd17060365ce4316afe495cc7ae86b6c81d15',
|
||||||
'org.briarproject:onionwrapper-core:0.0.1:onionwrapper-core-0.0.1.jar:a1937506b00ee6620e909a500e5d004be81f94a6f7d7c898e1a9e841a8ae8a2a',
|
'org.briarproject:onionwrapper-core:0.0.4:onionwrapper-core-0.0.4.jar:28a01a62e96aa763989a8afc325abd3bee54f8021269f91aa48b247a6e717870',
|
||||||
'org.briarproject:snowflake-android:2.5.1:snowflake-android-2.5.1.jar:88ec81c17b1b6fa884d06839dec0330e328b45c89f88c970a213ce91ca8eac87',
|
'org.briarproject:snowflake-android:2.5.1:snowflake-android-2.5.1.jar:88ec81c17b1b6fa884d06839dec0330e328b45c89f88c970a213ce91ca8eac87',
|
||||||
'org.briarproject:tor-android:0.4.7.13-2:tor-android-0.4.7.13-2.jar:453fd463b234a2104edd7f0d02d0649cbb5c5efbe47a76df3828f55a3f90f8b5',
|
'org.briarproject:tor-android:0.4.7.13-2:tor-android-0.4.7.13-2.jar:453fd463b234a2104edd7f0d02d0649cbb5c5efbe47a76df3828f55a3f90f8b5',
|
||||||
'org.checkerframework:checker-compat-qual:2.5.5:checker-compat-qual-2.5.5.jar:11d134b245e9cacc474514d2d66b5b8618f8039a1465cdc55bbc0b34e0008b7a',
|
'org.checkerframework:checker-compat-qual:2.5.5:checker-compat-qual-2.5.5.jar:11d134b245e9cacc474514d2d66b5b8618f8039a1465cdc55bbc0b34e0008b7a',
|
||||||
@@ -41,6 +42,7 @@ dependencyVerification {
|
|||||||
'org.jacoco:org.jacoco.ant:0.8.7:org.jacoco.ant-0.8.7.jar:97ca96a382c3f23a44d8eb4c4e6c3742a30cb8005774a76ced0fc4806ce49605',
|
'org.jacoco:org.jacoco.ant:0.8.7:org.jacoco.ant-0.8.7.jar:97ca96a382c3f23a44d8eb4c4e6c3742a30cb8005774a76ced0fc4806ce49605',
|
||||||
'org.jacoco:org.jacoco.core:0.8.7:org.jacoco.core-0.8.7.jar:ad7739b5fb5969aa1a8aead3d74ed54dc82ed012f1f10f336bd1b96e71c1a13c',
|
'org.jacoco:org.jacoco.core:0.8.7:org.jacoco.core-0.8.7.jar:ad7739b5fb5969aa1a8aead3d74ed54dc82ed012f1f10f336bd1b96e71c1a13c',
|
||||||
'org.jacoco:org.jacoco.report:0.8.7:org.jacoco.report-0.8.7.jar:cc89258623700a6c932592153cb528785876b6da183d5431f97efbba6f020e5b',
|
'org.jacoco:org.jacoco.report:0.8.7:org.jacoco.report-0.8.7.jar:cc89258623700a6c932592153cb528785876b6da183d5431f97efbba6f020e5b',
|
||||||
|
'org.jetbrains.kotlin:kotlin-reflect:1.6.10:kotlin-reflect-1.6.10.jar:3277ac102ae17aad10a55abec75ff5696c8d109790396434b496e75087854203',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib-common:1.7.0:kotlin-stdlib-common-1.7.0.jar:59c6ff64fe9a6604afce03e8aaa75f83586c6030ac71fb0b34ee7cdefed3618f',
|
'org.jetbrains.kotlin:kotlin-stdlib-common:1.7.0:kotlin-stdlib-common-1.7.0.jar:59c6ff64fe9a6604afce03e8aaa75f83586c6030ac71fb0b34ee7cdefed3618f',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib-common:1.8.0:kotlin-stdlib-common-1.8.0.jar:78ef93b59e603cc0fe51def9bd4c037b07cbace3b3b7806d1a490a42bc1f4cb2',
|
'org.jetbrains.kotlin:kotlin-stdlib-common:1.8.0:kotlin-stdlib-common-1.8.0.jar:78ef93b59e603cc0fe51def9bd4c037b07cbace3b3b7806d1a490a42bc1f4cb2',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.0:kotlin-stdlib-jdk7-1.7.0.jar:07e91be9b2ca20672d2bdb7e181b766e73453a2da13492b5ddaee8fa47aea239',
|
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.0:kotlin-stdlib-jdk7-1.7.0.jar:07e91be9b2ca20672d2bdb7e181b766e73453a2da13492b5ddaee8fa47aea239',
|
||||||
|
|||||||
@@ -54,6 +54,38 @@ public interface CryptoComponent {
|
|||||||
KeyPair ourKeyPair, byte[]... inputs)
|
KeyPair ourKeyPair, byte[]... inputs)
|
||||||
throws GeneralSecurityException;
|
throws GeneralSecurityException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derives a shared secret from two static and two ephemeral key pairs.
|
||||||
|
* <p>
|
||||||
|
* Do not use this method for new protocols. The shared secret can be
|
||||||
|
* re-derived using the ephemeral public keys and both static private
|
||||||
|
* keys, so keys derived from the shared secret should not be used if
|
||||||
|
* forward secrecy is required. Use {@link #deriveSharedSecret(String,
|
||||||
|
* PublicKey, PublicKey, KeyPair, KeyPair, boolean, byte[]...)} instead.
|
||||||
|
* <p>
|
||||||
|
* TODO: Remove this after a reasonable migration period (added 2023-03-10).
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
|
* @param label A namespaced label indicating the purpose of this shared
|
||||||
|
* secret, to prevent it from being repurposed or colliding with a shared
|
||||||
|
* secret derived for another purpose
|
||||||
|
* @param theirStaticPublicKey The static public key of the remote party
|
||||||
|
* @param theirEphemeralPublicKey The ephemeral public key of the remote
|
||||||
|
* party
|
||||||
|
* @param ourStaticKeyPair The static key pair of the local party
|
||||||
|
* @param ourEphemeralKeyPair The ephemeral key pair of the local party
|
||||||
|
* @param alice True if the local party is Alice
|
||||||
|
* @param inputs Additional inputs that will be included in the
|
||||||
|
* derivation of the shared secret
|
||||||
|
* @return The shared secret
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
SecretKey deriveSharedSecretBadly(String label,
|
||||||
|
PublicKey theirStaticPublicKey, PublicKey theirEphemeralPublicKey,
|
||||||
|
KeyPair ourStaticKeyPair, KeyPair ourEphemeralKeyPair,
|
||||||
|
boolean alice, byte[]... inputs)
|
||||||
|
throws GeneralSecurityException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Derives a shared secret from two static and two ephemeral key pairs.
|
* Derives a shared secret from two static and two ephemeral key pairs.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.briarproject.bramble.api.network;
|
|||||||
|
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@@ -27,4 +28,20 @@ public class NetworkStatus {
|
|||||||
public boolean isIpv6Only() {
|
public boolean isIpv6Only() {
|
||||||
return ipv6Only;
|
return ipv6Only;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return (connected ? 1 : 0) | (wifi ? 2 : 0) | (ipv6Only ? 4 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(@Nullable Object o) {
|
||||||
|
if (o instanceof NetworkStatus) {
|
||||||
|
NetworkStatus s = (NetworkStatus) o;
|
||||||
|
return connected == s.connected
|
||||||
|
&& wifi == s.wifi
|
||||||
|
&& ipv6Only == s.ipv6Only;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,8 +32,15 @@ public interface RecordReader {
|
|||||||
* 'accept' or 'ignore' predicates
|
* 'accept' or 'ignore' predicates
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
Record readRecord(Predicate<Record> accept, Predicate<Record> ignore)
|
Record readRecord(RecordPredicate accept, RecordPredicate ignore)
|
||||||
throws IOException;
|
throws IOException;
|
||||||
|
|
||||||
void close() throws IOException;
|
void close() throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface that reifies the generic interface {@code Predicate<Record>}
|
||||||
|
* for easier testing.
|
||||||
|
*/
|
||||||
|
interface RecordPredicate extends Predicate<Record> {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.system;
|
|
||||||
|
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
public interface LocationUtils {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the country the device is currently located in, or "" if it cannot
|
|
||||||
* be determined.
|
|
||||||
* <p>
|
|
||||||
* The country codes are formatted upper-case and as per <a href="
|
|
||||||
* https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">ISO 3166-1 alpha 2</a>.
|
|
||||||
*/
|
|
||||||
String getCurrentCountry();
|
|
||||||
}
|
|
||||||
@@ -6,6 +6,7 @@ import java.net.Inet4Address;
|
|||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
@@ -51,7 +52,7 @@ public class PrivacyUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static String scrubIpv6Address(byte[] ipv6) {
|
private static String scrubIpv6Address(byte[] ipv6) {
|
||||||
String hex = toHexString(ipv6).toLowerCase();
|
String hex = toHexString(ipv6).toLowerCase(Locale.US);
|
||||||
return hex.substring(0, 2) + "[scrubbed]" + hex.substring(30);
|
return hex.substring(0, 2) + "[scrubbed]" + hex.substring(30);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ dependencyVerification {
|
|||||||
'com.fasterxml.jackson.core:jackson-annotations:2.13.4:jackson-annotations-2.13.4.jar:ac5b27a634942391ca113850ee7db01df1499a240174021263501c05fc653b44',
|
'com.fasterxml.jackson.core:jackson-annotations:2.13.4:jackson-annotations-2.13.4.jar:ac5b27a634942391ca113850ee7db01df1499a240174021263501c05fc653b44',
|
||||||
'com.google.code.findbugs:annotations:3.0.1:annotations-3.0.1.jar:6b47ff0a6de0ce17cbedc3abb0828ca5bce3009d53ea47b3723ff023c4742f79',
|
'com.google.code.findbugs:annotations:3.0.1:annotations-3.0.1.jar:6b47ff0a6de0ce17cbedc3abb0828ca5bce3009d53ea47b3723ff023c4742f79',
|
||||||
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
|
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
|
||||||
'com.google.dagger:dagger:2.43.2:dagger-2.43.2.jar:c89681f7cbbf8c527bf4ac2748515d617fdb54a1d425c08d914fdc28192b5fe4',
|
'com.google.dagger:dagger:2.45:dagger-2.45.jar:f011cae7d2c0fb7ea17c34e05bc10e768b1081a5892ad019cf1fdb0e125c49c1',
|
||||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||||
'junit:junit:4.13.2:junit-4.13.2.jar:8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3',
|
'junit:junit:4.13.2:junit-4.13.2.jar:8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3',
|
||||||
'net.bytebuddy:byte-buddy:1.9.12:byte-buddy-1.9.12.jar:3688c3d434bebc3edc5516296a2ed0f47b65e451071b4afecad84f902f0efc11',
|
'net.bytebuddy:byte-buddy:1.9.12:byte-buddy-1.9.12.jar:3688c3d434bebc3edc5516296a2ed0f47b65e451071b4afecad84f902f0efc11',
|
||||||
@@ -12,8 +12,8 @@ dependencyVerification {
|
|||||||
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
|
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
|
||||||
'org.briarproject:null-safety:0.1:null-safety-0.1.jar:161760de5e838cb982bafa973df820675d4397098e9a91637a36a306d43ba011',
|
'org.briarproject:null-safety:0.1:null-safety-0.1.jar:161760de5e838cb982bafa973df820675d4397098e9a91637a36a306d43ba011',
|
||||||
'org.codehaus.mojo.signature:java16:1.1:java16-1.1.signature:53799223a2c98dba2d0add810bed76315460df285c69e4f397ae6098f87dd619',
|
'org.codehaus.mojo.signature:java16:1.1:java16-1.1.signature:53799223a2c98dba2d0add810bed76315460df285c69e4f397ae6098f87dd619',
|
||||||
'org.codehaus.mojo:animal-sniffer-ant-tasks:1.20:animal-sniffer-ant-tasks-1.20.jar:bb7d2498144118311d968bb08ff6fae3fc535fb1cb9cca8b8e9ea65b189422ac',
|
'org.codehaus.mojo:animal-sniffer-ant-tasks:1.22:animal-sniffer-ant-tasks-1.22.jar:3f6afeb3e09301d2d7179ed1db21e3ad8846c1e38415ad832a395138ae3f4218',
|
||||||
'org.codehaus.mojo:animal-sniffer:1.20:animal-sniffer-1.20.jar:80c422523c38db91260c6d78e5ee4b012862ab61cc55020c9e243dd7b5c62249',
|
'org.codehaus.mojo:animal-sniffer:1.22:animal-sniffer-1.22.jar:f18c11a25bdd8b520b9c6a28cbb6f33007c812ab0051b6be3f0778e660aa501c',
|
||||||
'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9',
|
'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9',
|
||||||
'org.hamcrest:hamcrest-library:2.1:hamcrest-library-2.1.jar:b7e2b6895b3b679f0e47b6380fda391b225e9b78505db9d8bdde8d3cc8d52a21',
|
'org.hamcrest:hamcrest-library:2.1:hamcrest-library-2.1.jar:b7e2b6895b3b679f0e47b6380fda391b225e9b78505db9d8bdde8d3cc8d52a21',
|
||||||
'org.hamcrest:hamcrest:2.1:hamcrest-2.1.jar:ba93b2e3a562322ba432f0a1b53addcc55cb188253319a020ed77f824e692050',
|
'org.hamcrest:hamcrest:2.1:hamcrest-2.1.jar:ba93b2e3a562322ba432f0a1b53addcc55cb188253319a020ed77f824e692050',
|
||||||
@@ -24,6 +24,6 @@ dependencyVerification {
|
|||||||
'org.jmock:jmock:2.12.0:jmock-2.12.0.jar:266d07314c0cd343c46ff8a55601272de8cf406807caf55e6f313295f83d10be',
|
'org.jmock:jmock:2.12.0:jmock-2.12.0.jar:266d07314c0cd343c46ff8a55601272de8cf406807caf55e6f313295f83d10be',
|
||||||
'org.objenesis:objenesis:3.0.1:objenesis-3.0.1.jar:7a8ff780b9ff48415d7c705f60030b0acaa616e7f823c98eede3b63508d4e984',
|
'org.objenesis:objenesis:3.0.1:objenesis-3.0.1.jar:7a8ff780b9ff48415d7c705f60030b0acaa616e7f823c98eede3b63508d4e984',
|
||||||
'org.ow2.asm:asm:7.1:asm-7.1.jar:4ab2fa2b6d2cc9ccb1eaa05ea329c407b47b13ed2915f62f8c4b8cc96258d4de',
|
'org.ow2.asm:asm:7.1:asm-7.1.jar:4ab2fa2b6d2cc9ccb1eaa05ea329c407b47b13ed2915f62f8c4b8cc96258d4de',
|
||||||
'org.ow2.asm:asm:9.1:asm-9.1.jar:cda4de455fab48ff0bcb7c48b4639447d4de859a7afc30a094a986f0936beba2',
|
'org.ow2.asm:asm:9.3:asm-9.3.jar:1263369b59e29c943918de11d6d6152e2ec6085ce63e5710516f8c67d368e4bc',
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ dependencies {
|
|||||||
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
||||||
|
|
||||||
testImplementation 'org.hsqldb:hsqldb:2.3.5' // The last version that supports Java 1.6
|
testImplementation 'org.hsqldb:hsqldb:2.3.5' // The last version that supports Java 1.6
|
||||||
testImplementation 'net.jodah:concurrentunit:0.4.2'
|
testImplementation 'net.jodah:concurrentunit:0.4.6'
|
||||||
testImplementation "junit:junit:$junit_version"
|
testImplementation "junit:junit:$junit_version"
|
||||||
testImplementation "org.jmock:jmock:$jmock_version"
|
testImplementation "org.jmock:jmock:$jmock_version"
|
||||||
testImplementation "org.jmock:jmock-junit4:$jmock_version"
|
testImplementation "org.jmock:jmock-junit4:$jmock_version"
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package org.briarproject.bramble.contact;
|
package org.briarproject.bramble.contact;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.FormatException;
|
import org.briarproject.bramble.api.FormatException;
|
||||||
import org.briarproject.bramble.api.Predicate;
|
|
||||||
import org.briarproject.bramble.api.client.ClientHelper;
|
import org.briarproject.bramble.api.client.ClientHelper;
|
||||||
import org.briarproject.bramble.api.contact.Contact;
|
import org.briarproject.bramble.api.contact.Contact;
|
||||||
import org.briarproject.bramble.api.contact.ContactExchangeManager;
|
import org.briarproject.bramble.api.contact.ContactExchangeManager;
|
||||||
@@ -24,6 +23,7 @@ import org.briarproject.bramble.api.properties.TransportProperties;
|
|||||||
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
||||||
import org.briarproject.bramble.api.record.Record;
|
import org.briarproject.bramble.api.record.Record;
|
||||||
import org.briarproject.bramble.api.record.RecordReader;
|
import org.briarproject.bramble.api.record.RecordReader;
|
||||||
|
import org.briarproject.bramble.api.record.RecordReader.RecordPredicate;
|
||||||
import org.briarproject.bramble.api.record.RecordReaderFactory;
|
import org.briarproject.bramble.api.record.RecordReaderFactory;
|
||||||
import org.briarproject.bramble.api.record.RecordWriter;
|
import org.briarproject.bramble.api.record.RecordWriter;
|
||||||
import org.briarproject.bramble.api.record.RecordWriterFactory;
|
import org.briarproject.bramble.api.record.RecordWriterFactory;
|
||||||
@@ -61,12 +61,12 @@ class ContactExchangeManagerImpl implements ContactExchangeManager {
|
|||||||
getLogger(ContactExchangeManagerImpl.class.getName());
|
getLogger(ContactExchangeManagerImpl.class.getName());
|
||||||
|
|
||||||
// Accept records with current protocol version, known record type
|
// Accept records with current protocol version, known record type
|
||||||
private static final Predicate<Record> ACCEPT = r ->
|
private static final RecordPredicate ACCEPT = r ->
|
||||||
r.getProtocolVersion() == PROTOCOL_VERSION &&
|
r.getProtocolVersion() == PROTOCOL_VERSION &&
|
||||||
isKnownRecordType(r.getRecordType());
|
isKnownRecordType(r.getRecordType());
|
||||||
|
|
||||||
// Ignore records with current protocol version, unknown record type
|
// Ignore records with current protocol version, unknown record type
|
||||||
private static final Predicate<Record> IGNORE = r ->
|
private static final RecordPredicate IGNORE = r ->
|
||||||
r.getProtocolVersion() == PROTOCOL_VERSION &&
|
r.getProtocolVersion() == PROTOCOL_VERSION &&
|
||||||
!isKnownRecordType(r.getRecordType());
|
!isKnownRecordType(r.getRecordType());
|
||||||
|
|
||||||
|
|||||||
@@ -5,14 +5,31 @@ import static org.briarproject.bramble.api.crypto.CryptoConstants.MAC_BYTES;
|
|||||||
interface HandshakeConstants {
|
interface HandshakeConstants {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current version of the handshake protocol.
|
* The current major version of the handshake protocol.
|
||||||
*/
|
*/
|
||||||
byte PROTOCOL_VERSION = 0;
|
byte PROTOCOL_MAJOR_VERSION = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Label for deriving the master key.
|
* The current minor version of the handshake protocol.
|
||||||
*/
|
*/
|
||||||
String MASTER_KEY_LABEL = "org.briarproject.bramble.handshake/MASTER_KEY";
|
byte PROTOCOL_MINOR_VERSION = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Label for deriving the master key when using the deprecated v0.0 key
|
||||||
|
* derivation method.
|
||||||
|
* <p>
|
||||||
|
* TODO: Remove this after a reasonable migration period (added 2023-03-10).
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
String MASTER_KEY_LABEL_0_0 =
|
||||||
|
"org.briarproject.bramble.handshake/MASTER_KEY";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Label for deriving the master key when using the v0.1 key derivation
|
||||||
|
* method.
|
||||||
|
*/
|
||||||
|
String MASTER_KEY_LABEL_0_1 =
|
||||||
|
"org.briarproject.bramble.handshake/MASTER_KEY_0_1";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Label for deriving Alice's proof of ownership from the master key.
|
* Label for deriving Alice's proof of ownership from the master key.
|
||||||
|
|||||||
@@ -13,11 +13,26 @@ interface HandshakeCrypto {
|
|||||||
KeyPair generateEphemeralKeyPair();
|
KeyPair generateEphemeralKeyPair();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Derives the master key from the given static and ephemeral keys.
|
* Derives the master key from the given static and ephemeral keys using
|
||||||
|
* the deprecated v0.0 key derivation method.
|
||||||
|
* <p>
|
||||||
|
* TODO: Remove this after a reasonable migration period (added 2023-03-10).
|
||||||
*
|
*
|
||||||
* @param alice Whether the local peer is Alice
|
* @param alice Whether the local peer is Alice
|
||||||
*/
|
*/
|
||||||
SecretKey deriveMasterKey(PublicKey theirStaticPublicKey,
|
@Deprecated
|
||||||
|
SecretKey deriveMasterKey_0_0(PublicKey theirStaticPublicKey,
|
||||||
|
PublicKey theirEphemeralPublicKey, KeyPair ourStaticKeyPair,
|
||||||
|
KeyPair ourEphemeralKeyPair, boolean alice)
|
||||||
|
throws GeneralSecurityException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derives the master key from the given static and ephemeral keys using
|
||||||
|
* the v0.1 key derivation method.
|
||||||
|
*
|
||||||
|
* @param alice Whether the local peer is Alice
|
||||||
|
*/
|
||||||
|
SecretKey deriveMasterKey_0_1(PublicKey theirStaticPublicKey,
|
||||||
PublicKey theirEphemeralPublicKey, KeyPair ourStaticKeyPair,
|
PublicKey theirEphemeralPublicKey, KeyPair ourStaticKeyPair,
|
||||||
KeyPair ourEphemeralKeyPair, boolean alice)
|
KeyPair ourEphemeralKeyPair, boolean alice)
|
||||||
throws GeneralSecurityException;
|
throws GeneralSecurityException;
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ import javax.inject.Inject;
|
|||||||
|
|
||||||
import static org.briarproject.bramble.contact.HandshakeConstants.ALICE_PROOF_LABEL;
|
import static org.briarproject.bramble.contact.HandshakeConstants.ALICE_PROOF_LABEL;
|
||||||
import static org.briarproject.bramble.contact.HandshakeConstants.BOB_PROOF_LABEL;
|
import static org.briarproject.bramble.contact.HandshakeConstants.BOB_PROOF_LABEL;
|
||||||
import static org.briarproject.bramble.contact.HandshakeConstants.MASTER_KEY_LABEL;
|
import static org.briarproject.bramble.contact.HandshakeConstants.MASTER_KEY_LABEL_0_0;
|
||||||
|
import static org.briarproject.bramble.contact.HandshakeConstants.MASTER_KEY_LABEL_0_1;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -32,7 +33,8 @@ class HandshakeCryptoImpl implements HandshakeCrypto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SecretKey deriveMasterKey(PublicKey theirStaticPublicKey,
|
@Deprecated
|
||||||
|
public SecretKey deriveMasterKey_0_0(PublicKey theirStaticPublicKey,
|
||||||
PublicKey theirEphemeralPublicKey, KeyPair ourStaticKeyPair,
|
PublicKey theirEphemeralPublicKey, KeyPair ourStaticKeyPair,
|
||||||
KeyPair ourEphemeralKeyPair, boolean alice) throws
|
KeyPair ourEphemeralKeyPair, boolean alice) throws
|
||||||
GeneralSecurityException {
|
GeneralSecurityException {
|
||||||
@@ -46,9 +48,29 @@ class HandshakeCryptoImpl implements HandshakeCrypto {
|
|||||||
alice ? ourEphemeral : theirEphemeral,
|
alice ? ourEphemeral : theirEphemeral,
|
||||||
alice ? theirEphemeral : ourEphemeral
|
alice ? theirEphemeral : ourEphemeral
|
||||||
};
|
};
|
||||||
return crypto.deriveSharedSecret(MASTER_KEY_LABEL, theirStaticPublicKey,
|
return crypto.deriveSharedSecretBadly(MASTER_KEY_LABEL_0_0,
|
||||||
theirEphemeralPublicKey, ourStaticKeyPair, ourEphemeralKeyPair,
|
theirStaticPublicKey, theirEphemeralPublicKey,
|
||||||
alice, inputs);
|
ourStaticKeyPair, ourEphemeralKeyPair, alice, inputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SecretKey deriveMasterKey_0_1(PublicKey theirStaticPublicKey,
|
||||||
|
PublicKey theirEphemeralPublicKey, KeyPair ourStaticKeyPair,
|
||||||
|
KeyPair ourEphemeralKeyPair, boolean alice) throws
|
||||||
|
GeneralSecurityException {
|
||||||
|
byte[] theirStatic = theirStaticPublicKey.getEncoded();
|
||||||
|
byte[] theirEphemeral = theirEphemeralPublicKey.getEncoded();
|
||||||
|
byte[] ourStatic = ourStaticKeyPair.getPublic().getEncoded();
|
||||||
|
byte[] ourEphemeral = ourEphemeralKeyPair.getPublic().getEncoded();
|
||||||
|
byte[][] inputs = {
|
||||||
|
alice ? ourStatic : theirStatic,
|
||||||
|
alice ? theirStatic : ourStatic,
|
||||||
|
alice ? ourEphemeral : theirEphemeral,
|
||||||
|
alice ? theirEphemeral : ourEphemeral
|
||||||
|
};
|
||||||
|
return crypto.deriveSharedSecret(MASTER_KEY_LABEL_0_1,
|
||||||
|
theirStaticPublicKey, theirEphemeralPublicKey,
|
||||||
|
ourStaticKeyPair, ourEphemeralKeyPair, alice, inputs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package org.briarproject.bramble.contact;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.FormatException;
|
import org.briarproject.bramble.api.FormatException;
|
||||||
import org.briarproject.bramble.api.Pair;
|
import org.briarproject.bramble.api.Pair;
|
||||||
import org.briarproject.bramble.api.Predicate;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactManager;
|
import org.briarproject.bramble.api.contact.ContactManager;
|
||||||
import org.briarproject.bramble.api.contact.HandshakeManager;
|
import org.briarproject.bramble.api.contact.HandshakeManager;
|
||||||
import org.briarproject.bramble.api.contact.PendingContact;
|
import org.briarproject.bramble.api.contact.PendingContact;
|
||||||
@@ -12,12 +11,12 @@ import org.briarproject.bramble.api.crypto.KeyPair;
|
|||||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.TransactionManager;
|
import org.briarproject.bramble.api.db.TransactionManager;
|
||||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||||
import org.briarproject.bramble.api.record.Record;
|
import org.briarproject.bramble.api.record.Record;
|
||||||
import org.briarproject.bramble.api.record.RecordReader;
|
import org.briarproject.bramble.api.record.RecordReader;
|
||||||
|
import org.briarproject.bramble.api.record.RecordReader.RecordPredicate;
|
||||||
import org.briarproject.bramble.api.record.RecordReaderFactory;
|
import org.briarproject.bramble.api.record.RecordReaderFactory;
|
||||||
import org.briarproject.bramble.api.record.RecordWriter;
|
import org.briarproject.bramble.api.record.RecordWriter;
|
||||||
import org.briarproject.bramble.api.record.RecordWriterFactory;
|
import org.briarproject.bramble.api.record.RecordWriterFactory;
|
||||||
@@ -28,15 +27,20 @@ import java.io.EOFException;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static java.util.Arrays.asList;
|
||||||
|
import static java.util.Collections.singletonList;
|
||||||
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES;
|
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES;
|
||||||
import static org.briarproject.bramble.contact.HandshakeConstants.PROOF_BYTES;
|
import static org.briarproject.bramble.contact.HandshakeConstants.PROOF_BYTES;
|
||||||
import static org.briarproject.bramble.contact.HandshakeConstants.PROTOCOL_VERSION;
|
import static org.briarproject.bramble.contact.HandshakeConstants.PROTOCOL_MAJOR_VERSION;
|
||||||
import static org.briarproject.bramble.contact.HandshakeRecordTypes.EPHEMERAL_PUBLIC_KEY;
|
import static org.briarproject.bramble.contact.HandshakeConstants.PROTOCOL_MINOR_VERSION;
|
||||||
import static org.briarproject.bramble.contact.HandshakeRecordTypes.PROOF_OF_OWNERSHIP;
|
import static org.briarproject.bramble.contact.HandshakeRecordTypes.RECORD_TYPE_EPHEMERAL_PUBLIC_KEY;
|
||||||
|
import static org.briarproject.bramble.contact.HandshakeRecordTypes.RECORD_TYPE_MINOR_VERSION;
|
||||||
|
import static org.briarproject.bramble.contact.HandshakeRecordTypes.RECORD_TYPE_PROOF_OF_OWNERSHIP;
|
||||||
import static org.briarproject.bramble.util.ValidationUtils.checkLength;
|
import static org.briarproject.bramble.util.ValidationUtils.checkLength;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@@ -44,12 +48,14 @@ import static org.briarproject.bramble.util.ValidationUtils.checkLength;
|
|||||||
class HandshakeManagerImpl implements HandshakeManager {
|
class HandshakeManagerImpl implements HandshakeManager {
|
||||||
|
|
||||||
// Ignore records with current protocol version, unknown record type
|
// Ignore records with current protocol version, unknown record type
|
||||||
private static final Predicate<Record> IGNORE = r ->
|
private static final RecordPredicate IGNORE = r ->
|
||||||
r.getProtocolVersion() == PROTOCOL_VERSION &&
|
r.getProtocolVersion() == PROTOCOL_MAJOR_VERSION &&
|
||||||
!isKnownRecordType(r.getRecordType());
|
!isKnownRecordType(r.getRecordType());
|
||||||
|
|
||||||
private static boolean isKnownRecordType(byte type) {
|
private static boolean isKnownRecordType(byte type) {
|
||||||
return type == EPHEMERAL_PUBLIC_KEY || type == PROOF_OF_OWNERSHIP;
|
return type == RECORD_TYPE_EPHEMERAL_PUBLIC_KEY ||
|
||||||
|
type == RECORD_TYPE_PROOF_OF_OWNERSHIP ||
|
||||||
|
type == RECORD_TYPE_MINOR_VERSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final TransactionManager db;
|
private final TransactionManager db;
|
||||||
@@ -61,7 +67,7 @@ class HandshakeManagerImpl implements HandshakeManager {
|
|||||||
private final RecordWriterFactory recordWriterFactory;
|
private final RecordWriterFactory recordWriterFactory;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
HandshakeManagerImpl(DatabaseComponent db,
|
HandshakeManagerImpl(TransactionManager db,
|
||||||
IdentityManager identityManager,
|
IdentityManager identityManager,
|
||||||
ContactManager contactManager,
|
ContactManager contactManager,
|
||||||
TransportCrypto transportCrypto,
|
TransportCrypto transportCrypto,
|
||||||
@@ -95,19 +101,31 @@ class HandshakeManagerImpl implements HandshakeManager {
|
|||||||
.createRecordWriter(out.getOutputStream());
|
.createRecordWriter(out.getOutputStream());
|
||||||
KeyPair ourEphemeralKeyPair =
|
KeyPair ourEphemeralKeyPair =
|
||||||
handshakeCrypto.generateEphemeralKeyPair();
|
handshakeCrypto.generateEphemeralKeyPair();
|
||||||
PublicKey theirEphemeralPublicKey;
|
Pair<Byte, PublicKey> theirMinorVersionAndKey;
|
||||||
if (alice) {
|
if (alice) {
|
||||||
|
sendMinorVersion(recordWriter);
|
||||||
sendPublicKey(recordWriter, ourEphemeralKeyPair.getPublic());
|
sendPublicKey(recordWriter, ourEphemeralKeyPair.getPublic());
|
||||||
theirEphemeralPublicKey = receivePublicKey(recordReader);
|
theirMinorVersionAndKey = receiveMinorVersionAndKey(recordReader);
|
||||||
} else {
|
} else {
|
||||||
theirEphemeralPublicKey = receivePublicKey(recordReader);
|
theirMinorVersionAndKey = receiveMinorVersionAndKey(recordReader);
|
||||||
|
sendMinorVersion(recordWriter);
|
||||||
sendPublicKey(recordWriter, ourEphemeralKeyPair.getPublic());
|
sendPublicKey(recordWriter, ourEphemeralKeyPair.getPublic());
|
||||||
}
|
}
|
||||||
|
byte theirMinorVersion = theirMinorVersionAndKey.getFirst();
|
||||||
|
PublicKey theirEphemeralPublicKey = theirMinorVersionAndKey.getSecond();
|
||||||
SecretKey masterKey;
|
SecretKey masterKey;
|
||||||
try {
|
try {
|
||||||
masterKey = handshakeCrypto.deriveMasterKey(theirStaticPublicKey,
|
if (theirMinorVersion > 0) {
|
||||||
theirEphemeralPublicKey, ourStaticKeyPair,
|
masterKey = handshakeCrypto.deriveMasterKey_0_1(
|
||||||
ourEphemeralKeyPair, alice);
|
theirStaticPublicKey, theirEphemeralPublicKey,
|
||||||
|
ourStaticKeyPair, ourEphemeralKeyPair, alice);
|
||||||
|
} else {
|
||||||
|
// TODO: Remove this branch after a reasonable migration
|
||||||
|
// period (added 2023-03-10).
|
||||||
|
masterKey = handshakeCrypto.deriveMasterKey_0_0(
|
||||||
|
theirStaticPublicKey, theirEphemeralPublicKey,
|
||||||
|
ourStaticKeyPair, ourEphemeralKeyPair, alice);
|
||||||
|
}
|
||||||
} catch (GeneralSecurityException e) {
|
} catch (GeneralSecurityException e) {
|
||||||
throw new FormatException();
|
throw new FormatException();
|
||||||
}
|
}
|
||||||
@@ -128,34 +146,91 @@ class HandshakeManagerImpl implements HandshakeManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void sendPublicKey(RecordWriter w, PublicKey k) throws IOException {
|
private void sendPublicKey(RecordWriter w, PublicKey k) throws IOException {
|
||||||
w.writeRecord(new Record(PROTOCOL_VERSION, EPHEMERAL_PUBLIC_KEY,
|
w.writeRecord(new Record(PROTOCOL_MAJOR_VERSION,
|
||||||
k.getEncoded()));
|
RECORD_TYPE_EPHEMERAL_PUBLIC_KEY, k.getEncoded()));
|
||||||
w.flush();
|
w.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
private PublicKey receivePublicKey(RecordReader r) throws IOException {
|
/**
|
||||||
byte[] key = readRecord(r, EPHEMERAL_PUBLIC_KEY).getPayload();
|
* Receives the remote peer's protocol minor version and ephemeral public
|
||||||
|
* key.
|
||||||
|
* <p>
|
||||||
|
* In version 0.1 of the protocol, each peer sends a minor version record
|
||||||
|
* followed by an ephemeral public key record.
|
||||||
|
* <p>
|
||||||
|
* In version 0.0 of the protocol, each peer sends an ephemeral public key
|
||||||
|
* record without a preceding minor version record.
|
||||||
|
* <p>
|
||||||
|
* Therefore the remote peer's minor version must be non-zero if a minor
|
||||||
|
* version record is received, and is assumed to be zero if no minor
|
||||||
|
* version record is received.
|
||||||
|
*/
|
||||||
|
private Pair<Byte, PublicKey> receiveMinorVersionAndKey(RecordReader r)
|
||||||
|
throws IOException {
|
||||||
|
byte theirMinorVersion;
|
||||||
|
PublicKey theirEphemeralPublicKey;
|
||||||
|
// The first record can be either a minor version record or an
|
||||||
|
// ephemeral public key record
|
||||||
|
Record first = readRecord(r, asList(RECORD_TYPE_MINOR_VERSION,
|
||||||
|
RECORD_TYPE_EPHEMERAL_PUBLIC_KEY));
|
||||||
|
if (first.getRecordType() == RECORD_TYPE_MINOR_VERSION) {
|
||||||
|
// The payload must be a single byte giving the remote peer's
|
||||||
|
// protocol minor version, which must be non-zero
|
||||||
|
byte[] payload = first.getPayload();
|
||||||
|
checkLength(payload, 1);
|
||||||
|
theirMinorVersion = payload[0];
|
||||||
|
if (theirMinorVersion == 0) throw new FormatException();
|
||||||
|
// The second record must be an ephemeral public key record
|
||||||
|
Record second = readRecord(r,
|
||||||
|
singletonList(RECORD_TYPE_EPHEMERAL_PUBLIC_KEY));
|
||||||
|
theirEphemeralPublicKey = parsePublicKey(second);
|
||||||
|
} else {
|
||||||
|
// The remote peer did not send a minor version record, so the
|
||||||
|
// remote peer's protocol minor version is assumed to be zero
|
||||||
|
// TODO: Remove this branch after a reasonable migration period
|
||||||
|
// (added 2023-03-10).
|
||||||
|
theirMinorVersion = 0;
|
||||||
|
theirEphemeralPublicKey = parsePublicKey(first);
|
||||||
|
}
|
||||||
|
return new Pair<>(theirMinorVersion, theirEphemeralPublicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PublicKey parsePublicKey(Record rec) throws IOException {
|
||||||
|
if (rec.getRecordType() != RECORD_TYPE_EPHEMERAL_PUBLIC_KEY) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
byte[] key = rec.getPayload();
|
||||||
checkLength(key, 1, MAX_AGREEMENT_PUBLIC_KEY_BYTES);
|
checkLength(key, 1, MAX_AGREEMENT_PUBLIC_KEY_BYTES);
|
||||||
return new AgreementPublicKey(key);
|
return new AgreementPublicKey(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendProof(RecordWriter w, byte[] proof) throws IOException {
|
private void sendProof(RecordWriter w, byte[] proof) throws IOException {
|
||||||
w.writeRecord(new Record(PROTOCOL_VERSION, PROOF_OF_OWNERSHIP, proof));
|
w.writeRecord(new Record(PROTOCOL_MAJOR_VERSION,
|
||||||
|
RECORD_TYPE_PROOF_OF_OWNERSHIP, proof));
|
||||||
w.flush();
|
w.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] receiveProof(RecordReader r) throws IOException {
|
private byte[] receiveProof(RecordReader r) throws IOException {
|
||||||
byte[] proof = readRecord(r, PROOF_OF_OWNERSHIP).getPayload();
|
Record rec = readRecord(r,
|
||||||
|
singletonList(RECORD_TYPE_PROOF_OF_OWNERSHIP));
|
||||||
|
byte[] proof = rec.getPayload();
|
||||||
checkLength(proof, PROOF_BYTES, PROOF_BYTES);
|
checkLength(proof, PROOF_BYTES, PROOF_BYTES);
|
||||||
return proof;
|
return proof;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Record readRecord(RecordReader r, byte expectedType)
|
private void sendMinorVersion(RecordWriter w) throws IOException {
|
||||||
|
w.writeRecord(new Record(PROTOCOL_MAJOR_VERSION,
|
||||||
|
RECORD_TYPE_MINOR_VERSION,
|
||||||
|
new byte[] {PROTOCOL_MINOR_VERSION}));
|
||||||
|
w.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Record readRecord(RecordReader r, List<Byte> expectedTypes)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
// Accept records with current protocol version, expected type only
|
// Accept records with current protocol version, expected types only
|
||||||
Predicate<Record> accept = rec ->
|
RecordPredicate accept = rec ->
|
||||||
rec.getProtocolVersion() == PROTOCOL_VERSION &&
|
rec.getProtocolVersion() == PROTOCOL_MAJOR_VERSION &&
|
||||||
rec.getRecordType() == expectedType;
|
expectedTypes.contains(rec.getRecordType());
|
||||||
Record rec = r.readRecord(accept, IGNORE);
|
Record rec = r.readRecord(accept, IGNORE);
|
||||||
if (rec == null) throw new EOFException();
|
if (rec == null) throw new EOFException();
|
||||||
return rec;
|
return rec;
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ package org.briarproject.bramble.contact;
|
|||||||
*/
|
*/
|
||||||
interface HandshakeRecordTypes {
|
interface HandshakeRecordTypes {
|
||||||
|
|
||||||
byte EPHEMERAL_PUBLIC_KEY = 0;
|
byte RECORD_TYPE_EPHEMERAL_PUBLIC_KEY = 0;
|
||||||
|
|
||||||
byte PROOF_OF_OWNERSHIP = 1;
|
byte RECORD_TYPE_PROOF_OF_OWNERSHIP = 1;
|
||||||
|
|
||||||
|
byte RECORD_TYPE_MINOR_VERSION = 2;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import java.security.NoSuchAlgorithmException;
|
|||||||
import java.security.Provider;
|
import java.security.Provider;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.security.Security;
|
import java.security.Security;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@@ -222,7 +223,8 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SecretKey deriveSharedSecret(String label,
|
@Deprecated
|
||||||
|
public SecretKey deriveSharedSecretBadly(String label,
|
||||||
PublicKey theirStaticPublicKey, PublicKey theirEphemeralPublicKey,
|
PublicKey theirStaticPublicKey, PublicKey theirEphemeralPublicKey,
|
||||||
KeyPair ourStaticKeyPair, KeyPair ourEphemeralKeyPair,
|
KeyPair ourStaticKeyPair, KeyPair ourEphemeralKeyPair,
|
||||||
boolean alice, byte[]... inputs) throws GeneralSecurityException {
|
boolean alice, byte[]... inputs) throws GeneralSecurityException {
|
||||||
@@ -250,6 +252,35 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
return new SecretKey(hash);
|
return new SecretKey(hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SecretKey deriveSharedSecret(String label,
|
||||||
|
PublicKey theirStaticPublicKey, PublicKey theirEphemeralPublicKey,
|
||||||
|
KeyPair ourStaticKeyPair, KeyPair ourEphemeralKeyPair,
|
||||||
|
boolean alice, byte[]... inputs) throws GeneralSecurityException {
|
||||||
|
PrivateKey ourStaticPrivateKey = ourStaticKeyPair.getPrivate();
|
||||||
|
PrivateKey ourEphemeralPrivateKey = ourEphemeralKeyPair.getPrivate();
|
||||||
|
byte[][] hashInputs = new byte[inputs.length + 3][];
|
||||||
|
// Alice ephemeral/Bob ephemeral
|
||||||
|
hashInputs[0] = performRawKeyAgreement(ourEphemeralPrivateKey,
|
||||||
|
theirEphemeralPublicKey);
|
||||||
|
// Alice static/Bob ephemeral, Bob static/Alice ephemeral
|
||||||
|
if (alice) {
|
||||||
|
hashInputs[1] = performRawKeyAgreement(ourStaticPrivateKey,
|
||||||
|
theirEphemeralPublicKey);
|
||||||
|
hashInputs[2] = performRawKeyAgreement(ourEphemeralPrivateKey,
|
||||||
|
theirStaticPublicKey);
|
||||||
|
} else {
|
||||||
|
hashInputs[1] = performRawKeyAgreement(ourEphemeralPrivateKey,
|
||||||
|
theirStaticPublicKey);
|
||||||
|
hashInputs[2] = performRawKeyAgreement(ourStaticPrivateKey,
|
||||||
|
theirEphemeralPublicKey);
|
||||||
|
}
|
||||||
|
arraycopy(inputs, 0, hashInputs, 3, inputs.length);
|
||||||
|
byte[] hash = hash(label, hashInputs);
|
||||||
|
if (hash.length != SecretKey.LENGTH) throw new IllegalStateException();
|
||||||
|
return new SecretKey(hash);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] sign(String label, byte[] toSign, PrivateKey privateKey)
|
public byte[] sign(String label, byte[] toSign, PrivateKey privateKey)
|
||||||
throws GeneralSecurityException {
|
throws GeneralSecurityException {
|
||||||
@@ -470,7 +501,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
arraycopy(publicKey, 0, address, 0, publicKey.length);
|
arraycopy(publicKey, 0, address, 0, publicKey.length);
|
||||||
arraycopy(checksum, 0, address, publicKey.length, ONION_CHECKSUM_BYTES);
|
arraycopy(checksum, 0, address, publicKey.length, ONION_CHECKSUM_BYTES);
|
||||||
address[address.length - 1] = ONION_HS_PROTOCOL_VERSION;
|
address[address.length - 1] = ONION_HS_PROTOCOL_VERSION;
|
||||||
return Base32.encode(address).toLowerCase();
|
return Base32.encode(address).toLowerCase(Locale.US);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
package org.briarproject.bramble.keyagreement;
|
package org.briarproject.bramble.keyagreement;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.Predicate;
|
|
||||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
|
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||||
import org.briarproject.bramble.api.record.Record;
|
import org.briarproject.bramble.api.record.Record;
|
||||||
import org.briarproject.bramble.api.record.RecordReader;
|
import org.briarproject.bramble.api.record.RecordReader;
|
||||||
|
import org.briarproject.bramble.api.record.RecordReader.RecordPredicate;
|
||||||
import org.briarproject.bramble.api.record.RecordReaderFactory;
|
import org.briarproject.bramble.api.record.RecordReaderFactory;
|
||||||
import org.briarproject.bramble.api.record.RecordWriter;
|
import org.briarproject.bramble.api.record.RecordWriter;
|
||||||
import org.briarproject.bramble.api.record.RecordWriterFactory;
|
import org.briarproject.bramble.api.record.RecordWriterFactory;
|
||||||
@@ -34,12 +34,12 @@ class KeyAgreementTransport {
|
|||||||
Logger.getLogger(KeyAgreementTransport.class.getName());
|
Logger.getLogger(KeyAgreementTransport.class.getName());
|
||||||
|
|
||||||
// Accept records with current protocol version, known record type
|
// Accept records with current protocol version, known record type
|
||||||
private static final Predicate<Record> ACCEPT = r ->
|
private static final RecordPredicate ACCEPT = r ->
|
||||||
r.getProtocolVersion() == PROTOCOL_VERSION &&
|
r.getProtocolVersion() == PROTOCOL_VERSION &&
|
||||||
isKnownRecordType(r.getRecordType());
|
isKnownRecordType(r.getRecordType());
|
||||||
|
|
||||||
// Ignore records with current protocol version, unknown record type
|
// Ignore records with current protocol version, unknown record type
|
||||||
private static final Predicate<Record> IGNORE = r ->
|
private static final RecordPredicate IGNORE = r ->
|
||||||
r.getProtocolVersion() == PROTOCOL_VERSION &&
|
r.getProtocolVersion() == PROTOCOL_VERSION &&
|
||||||
!isKnownRecordType(r.getRecordType());
|
!isKnownRecordType(r.getRecordType());
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package org.briarproject.bramble.plugin.tor;
|
||||||
|
|
||||||
|
import org.briarproject.onionwrapper.CircumventionProvider;
|
||||||
|
import org.briarproject.onionwrapper.CircumventionProviderFactory;
|
||||||
|
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.Provides;
|
||||||
|
|
||||||
|
@Module
|
||||||
|
public class CircumventionModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
CircumventionProvider provideCircumventionProvider() {
|
||||||
|
return CircumventionProviderFactory.createCircumventionProvider();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,11 +24,11 @@ import org.briarproject.bramble.api.rendezvous.KeyMaterialSource;
|
|||||||
import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint;
|
import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint;
|
||||||
import org.briarproject.bramble.api.settings.Settings;
|
import org.briarproject.bramble.api.settings.Settings;
|
||||||
import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
|
import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
|
||||||
import org.briarproject.nullsafety.InterfaceNotNullByDefault;
|
import org.briarproject.nullsafety.InterfaceNotNullByDefault;
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.onionwrapper.CircumventionProvider;
|
import org.briarproject.onionwrapper.CircumventionProvider;
|
||||||
import org.briarproject.onionwrapper.CircumventionProvider.BridgeType;
|
import org.briarproject.onionwrapper.CircumventionProvider.BridgeType;
|
||||||
|
import org.briarproject.onionwrapper.LocationUtils;
|
||||||
import org.briarproject.onionwrapper.TorWrapper;
|
import org.briarproject.onionwrapper.TorWrapper;
|
||||||
import org.briarproject.onionwrapper.TorWrapper.HiddenServiceProperties;
|
import org.briarproject.onionwrapper.TorWrapper.HiddenServiceProperties;
|
||||||
import org.briarproject.onionwrapper.TorWrapper.Observer;
|
import org.briarproject.onionwrapper.TorWrapper.Observer;
|
||||||
@@ -311,6 +311,9 @@ class TorPlugin implements DuplexPlugin, EventListener {
|
|||||||
tor.stop();
|
tor.stop();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
LOG.warning("Interrupted while stopping Tor");
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -625,12 +628,23 @@ class TorPlugin implements DuplexPlugin, EventListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private synchronized State getState(TorState torState) {
|
private synchronized State getState(TorState torState) {
|
||||||
if (torState == TorState.STARTING_STOPPING || !settingsChecked) {
|
// Treat TorState.STARTED as State.STARTING_STOPPING because it's
|
||||||
|
// only seen during startup, before TorWrapper#enableNetwork() is
|
||||||
|
// called for the first time. TorState.NOT_STARTED and
|
||||||
|
// TorState.STOPPED are mapped to State.STARTING_STOPPING because
|
||||||
|
// that's the State before we've started and after we've stopped.
|
||||||
|
if (torState == TorState.NOT_STARTED ||
|
||||||
|
torState == TorState.STARTING ||
|
||||||
|
torState == TorState.STARTED ||
|
||||||
|
torState == TorState.STOPPING ||
|
||||||
|
torState == TorState.STOPPED ||
|
||||||
|
!settingsChecked) {
|
||||||
return STARTING_STOPPING;
|
return STARTING_STOPPING;
|
||||||
}
|
}
|
||||||
if (reasonsDisabled != 0) return DISABLED;
|
if (reasonsDisabled != 0) return DISABLED;
|
||||||
if (torState == TorState.CONNECTING) return ENABLING;
|
if (torState == TorState.CONNECTING) return ENABLING;
|
||||||
if (torState == TorState.CONNECTED) return ACTIVE;
|
if (torState == TorState.CONNECTED) return ACTIVE;
|
||||||
|
// The plugin is enabled in settings but the device is offline
|
||||||
return INACTIVE;
|
return INACTIVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,10 +17,10 @@ import org.briarproject.bramble.api.plugin.TransportId;
|
|||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
|
||||||
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.onionwrapper.CircumventionProvider;
|
import org.briarproject.onionwrapper.CircumventionProvider;
|
||||||
|
import org.briarproject.onionwrapper.LocationUtils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package org.briarproject.bramble.record;
|
package org.briarproject.bramble.record;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.FormatException;
|
import org.briarproject.bramble.api.FormatException;
|
||||||
import org.briarproject.bramble.api.Predicate;
|
|
||||||
import org.briarproject.bramble.api.record.Record;
|
import org.briarproject.bramble.api.record.Record;
|
||||||
import org.briarproject.bramble.api.record.RecordReader;
|
import org.briarproject.bramble.api.record.RecordReader;
|
||||||
import org.briarproject.bramble.util.ByteUtils;
|
import org.briarproject.bramble.util.ByteUtils;
|
||||||
@@ -45,7 +44,7 @@ class RecordReaderImpl implements RecordReader {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public Record readRecord(Predicate<Record> accept, Predicate<Record> ignore)
|
public Record readRecord(RecordPredicate accept, RecordPredicate ignore)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
while (true) {
|
while (true) {
|
||||||
if (eof()) return null;
|
if (eof()) return null;
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
package org.briarproject.bramble.sync;
|
package org.briarproject.bramble.sync;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.FormatException;
|
import org.briarproject.bramble.api.FormatException;
|
||||||
import org.briarproject.bramble.api.Predicate;
|
|
||||||
import org.briarproject.bramble.api.UniqueId;
|
import org.briarproject.bramble.api.UniqueId;
|
||||||
import org.briarproject.bramble.api.record.Record;
|
import org.briarproject.bramble.api.record.Record;
|
||||||
import org.briarproject.bramble.api.record.RecordReader;
|
import org.briarproject.bramble.api.record.RecordReader;
|
||||||
|
import org.briarproject.bramble.api.record.RecordReader.RecordPredicate;
|
||||||
import org.briarproject.bramble.api.sync.Ack;
|
import org.briarproject.bramble.api.sync.Ack;
|
||||||
import org.briarproject.bramble.api.sync.Message;
|
import org.briarproject.bramble.api.sync.Message;
|
||||||
import org.briarproject.bramble.api.sync.MessageFactory;
|
import org.briarproject.bramble.api.sync.MessageFactory;
|
||||||
@@ -41,12 +41,12 @@ import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION;
|
|||||||
class SyncRecordReaderImpl implements SyncRecordReader {
|
class SyncRecordReaderImpl implements SyncRecordReader {
|
||||||
|
|
||||||
// Accept records with current protocol version, known record type
|
// Accept records with current protocol version, known record type
|
||||||
private static final Predicate<Record> ACCEPT = r ->
|
private static final RecordPredicate ACCEPT = r ->
|
||||||
r.getProtocolVersion() == PROTOCOL_VERSION &&
|
r.getProtocolVersion() == PROTOCOL_VERSION &&
|
||||||
isKnownRecordType(r.getRecordType());
|
isKnownRecordType(r.getRecordType());
|
||||||
|
|
||||||
// Ignore records with current protocol version, unknown record type
|
// Ignore records with current protocol version, unknown record type
|
||||||
private static final Predicate<Record> IGNORE = r ->
|
private static final RecordPredicate IGNORE = r ->
|
||||||
r.getProtocolVersion() == PROTOCOL_VERSION &&
|
r.getProtocolVersion() == PROTOCOL_VERSION &&
|
||||||
!isKnownRecordType(r.getRecordType());
|
!isKnownRecordType(r.getRecordType());
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import org.briarproject.bramble.api.sync.GroupId;
|
|||||||
import org.briarproject.bramble.api.sync.InvalidMessageException;
|
import org.briarproject.bramble.api.sync.InvalidMessageException;
|
||||||
import org.briarproject.bramble.api.sync.Message;
|
import org.briarproject.bramble.api.sync.Message;
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
|
import org.briarproject.bramble.api.sync.MessageStatus;
|
||||||
import org.briarproject.bramble.api.sync.validation.IncomingMessageHook;
|
import org.briarproject.bramble.api.sync.validation.IncomingMessageHook;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.api.versioning.ClientMajorVersion;
|
import org.briarproject.bramble.api.versioning.ClientMajorVersion;
|
||||||
@@ -36,17 +37,20 @@ import java.util.Collections;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
|
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
|
||||||
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
|
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
|
||||||
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
|
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
|
||||||
@@ -58,6 +62,9 @@ import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_
|
|||||||
class ClientVersioningManagerImpl implements ClientVersioningManager,
|
class ClientVersioningManagerImpl implements ClientVersioningManager,
|
||||||
Service, OpenDatabaseHook, ContactHook, IncomingMessageHook {
|
Service, OpenDatabaseHook, ContactHook, IncomingMessageHook {
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
getLogger(ClientVersioningManagerImpl.class.getName());
|
||||||
|
|
||||||
private final DatabaseComponent db;
|
private final DatabaseComponent db;
|
||||||
private final ClientHelper clientHelper;
|
private final ClientHelper clientHelper;
|
||||||
private final ContactGroupFactory contactGroupFactory;
|
private final ContactGroupFactory contactGroupFactory;
|
||||||
@@ -128,12 +135,68 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDatabaseOpened(Transaction txn) throws DbException {
|
public void onDatabaseOpened(Transaction txn) throws DbException {
|
||||||
|
LOG.info("onDatabaseOpened " + localGroup.getId());
|
||||||
|
for (Contact c : db.getContacts(txn)) {
|
||||||
|
try {
|
||||||
|
// FIXME: DO NOT MERGE, this logs the contact name and alias
|
||||||
|
LOG.info(String.format(Locale.US,
|
||||||
|
"find latest updates for %d: %s (%s)",
|
||||||
|
c.getId().getInt(),
|
||||||
|
c.getAuthor().getName(),
|
||||||
|
c.getAlias()));
|
||||||
|
LatestUpdates latestUpdates = findLatestUpdates(txn, c.getId());
|
||||||
|
if (latestUpdates == null) {
|
||||||
|
LOG.info("none found");
|
||||||
|
} else {
|
||||||
|
if (latestUpdates.local != null) {
|
||||||
|
MessageStatus status = db.getMessageStatus(txn,
|
||||||
|
c.getId(), latestUpdates.local.messageId);
|
||||||
|
LOG.info(String.format(Locale.US,
|
||||||
|
"local: %s; sent: %b; seen: %b%n",
|
||||||
|
latestUpdates.local.messageId,
|
||||||
|
status.isSent(),
|
||||||
|
status.isSeen()));
|
||||||
|
Update update =
|
||||||
|
loadUpdate(txn, latestUpdates.local.messageId);
|
||||||
|
printUpdate(update);
|
||||||
|
}
|
||||||
|
if (latestUpdates.remote != null) {
|
||||||
|
MessageStatus status = db.getMessageStatus(txn,
|
||||||
|
c.getId(), latestUpdates.remote.messageId);
|
||||||
|
LOG.info(String.format(Locale.US,
|
||||||
|
"remote: %s; sent: %b; seen: %b%n",
|
||||||
|
latestUpdates.remote.messageId,
|
||||||
|
status.isSent(),
|
||||||
|
status.isSeen()));
|
||||||
|
Update update =
|
||||||
|
loadUpdate(txn, latestUpdates.remote.messageId);
|
||||||
|
printUpdate(update);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (FormatException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (db.containsGroup(txn, localGroup.getId())) return;
|
if (db.containsGroup(txn, localGroup.getId())) return;
|
||||||
db.addGroup(txn, localGroup);
|
db.addGroup(txn, localGroup);
|
||||||
// Set things up for any pre-existing contacts
|
// Set things up for any pre-existing contacts
|
||||||
for (Contact c : db.getContacts(txn)) addingContact(txn, c);
|
for (Contact c : db.getContacts(txn)) addingContact(txn, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void printUpdate(Update update) {
|
||||||
|
LOG.info(String.format(Locale.US, "update version: %d%n",
|
||||||
|
update.updateVersion));
|
||||||
|
for (ClientState state : update.states) {
|
||||||
|
LOG.info(String.format(Locale.US,
|
||||||
|
"id: %s, major: %d, minor: %d, active: %b, %n",
|
||||||
|
state.clientVersion.getClientId().getString(),
|
||||||
|
state.clientVersion.getClientMajorVersion()
|
||||||
|
.getMajorVersion(),
|
||||||
|
state.clientVersion.getMinorVersion(),
|
||||||
|
state.active));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startService() throws ServiceException {
|
public void startService() throws ServiceException {
|
||||||
List<ClientVersion> versions = new ArrayList<>(clients);
|
List<ClientVersion> versions = new ArrayList<>(clients);
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
package org.briarproject.onionwrapper;
|
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import dagger.Module;
|
|
||||||
import dagger.Provides;
|
|
||||||
|
|
||||||
@Module
|
|
||||||
public class CircumventionModule {
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
CircumventionProvider provideCircumventionProvider(
|
|
||||||
CircumventionProviderImpl provider) {
|
|
||||||
return provider;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,316 @@
|
|||||||
|
package org.briarproject.bramble.contact;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.FormatException;
|
||||||
|
import org.briarproject.bramble.api.contact.ContactManager;
|
||||||
|
import org.briarproject.bramble.api.contact.HandshakeManager.HandshakeResult;
|
||||||
|
import org.briarproject.bramble.api.contact.PendingContact;
|
||||||
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||||
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
|
import org.briarproject.bramble.api.db.TransactionManager;
|
||||||
|
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||||
|
import org.briarproject.bramble.api.record.Record;
|
||||||
|
import org.briarproject.bramble.api.record.RecordReader;
|
||||||
|
import org.briarproject.bramble.api.record.RecordReader.RecordPredicate;
|
||||||
|
import org.briarproject.bramble.api.record.RecordReaderFactory;
|
||||||
|
import org.briarproject.bramble.api.record.RecordWriter;
|
||||||
|
import org.briarproject.bramble.api.record.RecordWriterFactory;
|
||||||
|
import org.briarproject.bramble.api.transport.StreamWriter;
|
||||||
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
|
import org.briarproject.bramble.test.DbExpectations;
|
||||||
|
import org.briarproject.bramble.test.PredicateMatcher;
|
||||||
|
import org.jmock.Expectations;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.contact.HandshakeConstants.PROOF_BYTES;
|
||||||
|
import static org.briarproject.bramble.contact.HandshakeConstants.PROTOCOL_MAJOR_VERSION;
|
||||||
|
import static org.briarproject.bramble.contact.HandshakeConstants.PROTOCOL_MINOR_VERSION;
|
||||||
|
import static org.briarproject.bramble.contact.HandshakeRecordTypes.RECORD_TYPE_EPHEMERAL_PUBLIC_KEY;
|
||||||
|
import static org.briarproject.bramble.contact.HandshakeRecordTypes.RECORD_TYPE_MINOR_VERSION;
|
||||||
|
import static org.briarproject.bramble.contact.HandshakeRecordTypes.RECORD_TYPE_PROOF_OF_OWNERSHIP;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getAgreementPrivateKey;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getAgreementPublicKey;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getPendingContact;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class HandshakeManagerImplTest extends BrambleMockTestCase {
|
||||||
|
|
||||||
|
private final TransactionManager db =
|
||||||
|
context.mock(TransactionManager.class);
|
||||||
|
private final IdentityManager identityManager =
|
||||||
|
context.mock(IdentityManager.class);
|
||||||
|
private final ContactManager contactManager =
|
||||||
|
context.mock(ContactManager.class);
|
||||||
|
private final TransportCrypto transportCrypto =
|
||||||
|
context.mock(TransportCrypto.class);
|
||||||
|
private final HandshakeCrypto handshakeCrypto =
|
||||||
|
context.mock(HandshakeCrypto.class);
|
||||||
|
private final RecordReaderFactory recordReaderFactory =
|
||||||
|
context.mock(RecordReaderFactory.class);
|
||||||
|
private final RecordWriterFactory recordWriterFactory =
|
||||||
|
context.mock(RecordWriterFactory.class);
|
||||||
|
private final RecordReader recordReader = context.mock(RecordReader.class);
|
||||||
|
private final RecordWriter recordWriter = context.mock(RecordWriter.class);
|
||||||
|
private final StreamWriter streamWriter = context.mock(StreamWriter.class);
|
||||||
|
|
||||||
|
private final PendingContact pendingContact = getPendingContact();
|
||||||
|
private final PublicKey theirStaticPublicKey =
|
||||||
|
pendingContact.getPublicKey();
|
||||||
|
private final PublicKey ourStaticPublicKey = getAgreementPublicKey();
|
||||||
|
private final PrivateKey ourStaticPrivateKey = getAgreementPrivateKey();
|
||||||
|
private final KeyPair ourStaticKeyPair =
|
||||||
|
new KeyPair(ourStaticPublicKey, ourStaticPrivateKey);
|
||||||
|
private final PublicKey theirEphemeralPublicKey = getAgreementPublicKey();
|
||||||
|
private final PublicKey ourEphemeralPublicKey = getAgreementPublicKey();
|
||||||
|
private final PrivateKey ourEphemeralPrivateKey = getAgreementPrivateKey();
|
||||||
|
private final KeyPair ourEphemeralKeyPair =
|
||||||
|
new KeyPair(ourEphemeralPublicKey, ourEphemeralPrivateKey);
|
||||||
|
private final SecretKey masterKey = getSecretKey();
|
||||||
|
private final byte[] ourProof = getRandomBytes(PROOF_BYTES);
|
||||||
|
private final byte[] theirProof = getRandomBytes(PROOF_BYTES);
|
||||||
|
|
||||||
|
private final InputStream in = new ByteArrayInputStream(new byte[0]);
|
||||||
|
private final OutputStream out = new ByteArrayOutputStream(0);
|
||||||
|
|
||||||
|
private final HandshakeManagerImpl handshakeManager =
|
||||||
|
new HandshakeManagerImpl(db, identityManager, contactManager,
|
||||||
|
transportCrypto, handshakeCrypto, recordReaderFactory,
|
||||||
|
recordWriterFactory);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHandshakeAsAliceWithPeerVersion_0_1() throws Exception {
|
||||||
|
testHandshakeWithPeerVersion_0_1(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHandshakeAsBobWithPeerVersion_0_1() throws Exception {
|
||||||
|
testHandshakeWithPeerVersion_0_1(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testHandshakeWithPeerVersion_0_1(boolean alice)
|
||||||
|
throws Exception {
|
||||||
|
expectPrepareForHandshake(alice);
|
||||||
|
expectSendMinorVersion();
|
||||||
|
expectSendKey();
|
||||||
|
// Remote peer sends minor version, so use new key derivation
|
||||||
|
expectReceiveMinorVersion();
|
||||||
|
expectReceiveKey();
|
||||||
|
expectDeriveMasterKey_0_1(alice);
|
||||||
|
expectDeriveProof(alice);
|
||||||
|
expectSendProof();
|
||||||
|
expectReceiveProof();
|
||||||
|
expectSendEof();
|
||||||
|
expectReceiveEof();
|
||||||
|
expectVerifyOwnership(alice, true);
|
||||||
|
|
||||||
|
HandshakeResult result = handshakeManager.handshake(
|
||||||
|
pendingContact.getId(), in, streamWriter);
|
||||||
|
|
||||||
|
assertArrayEquals(masterKey.getBytes(),
|
||||||
|
result.getMasterKey().getBytes());
|
||||||
|
assertEquals(alice, result.isAlice());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHandshakeAsAliceWithPeerVersion_0_0() throws Exception {
|
||||||
|
testHandshakeWithPeerVersion_0_0(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHandshakeAsBobWithPeerVersion_0_0() throws Exception {
|
||||||
|
testHandshakeWithPeerVersion_0_0(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testHandshakeWithPeerVersion_0_0(boolean alice)
|
||||||
|
throws Exception {
|
||||||
|
expectPrepareForHandshake(alice);
|
||||||
|
expectSendMinorVersion();
|
||||||
|
expectSendKey();
|
||||||
|
// Remote peer does not send minor version, so use old key derivation
|
||||||
|
expectReceiveKey();
|
||||||
|
expectDeriveMasterKey_0_0(alice);
|
||||||
|
expectDeriveProof(alice);
|
||||||
|
expectSendProof();
|
||||||
|
expectReceiveProof();
|
||||||
|
expectSendEof();
|
||||||
|
expectReceiveEof();
|
||||||
|
expectVerifyOwnership(alice, true);
|
||||||
|
|
||||||
|
HandshakeResult result = handshakeManager.handshake(
|
||||||
|
pendingContact.getId(), in, streamWriter);
|
||||||
|
|
||||||
|
assertArrayEquals(masterKey.getBytes(),
|
||||||
|
result.getMasterKey().getBytes());
|
||||||
|
assertEquals(alice, result.isAlice());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testProofOfOwnershipNotVerifiedAsAlice() throws Exception {
|
||||||
|
testProofOfOwnershipNotVerified(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testProofOfOwnershipNotVerifiedAsBob() throws Exception {
|
||||||
|
testProofOfOwnershipNotVerified(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testProofOfOwnershipNotVerified(boolean alice)
|
||||||
|
throws Exception {
|
||||||
|
expectPrepareForHandshake(alice);
|
||||||
|
expectSendMinorVersion();
|
||||||
|
expectSendKey();
|
||||||
|
expectReceiveMinorVersion();
|
||||||
|
expectReceiveKey();
|
||||||
|
expectDeriveMasterKey_0_1(alice);
|
||||||
|
expectDeriveProof(alice);
|
||||||
|
expectSendProof();
|
||||||
|
expectReceiveProof();
|
||||||
|
expectSendEof();
|
||||||
|
expectReceiveEof();
|
||||||
|
expectVerifyOwnership(alice, false);
|
||||||
|
|
||||||
|
handshakeManager.handshake(pendingContact.getId(), in, streamWriter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expectPrepareForHandshake(boolean alice) throws Exception {
|
||||||
|
Transaction txn = new Transaction(null, true);
|
||||||
|
|
||||||
|
context.checking(new DbExpectations() {{
|
||||||
|
oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
|
||||||
|
oneOf(contactManager).getPendingContact(txn,
|
||||||
|
pendingContact.getId());
|
||||||
|
will(returnValue(pendingContact));
|
||||||
|
oneOf(identityManager).getHandshakeKeys(txn);
|
||||||
|
will(returnValue(ourStaticKeyPair));
|
||||||
|
oneOf(transportCrypto).isAlice(theirStaticPublicKey,
|
||||||
|
ourStaticKeyPair);
|
||||||
|
will(returnValue(alice));
|
||||||
|
oneOf(recordReaderFactory).createRecordReader(in);
|
||||||
|
will(returnValue(recordReader));
|
||||||
|
oneOf(streamWriter).getOutputStream();
|
||||||
|
will(returnValue(out));
|
||||||
|
oneOf(recordWriterFactory).createRecordWriter(out);
|
||||||
|
will(returnValue(recordWriter));
|
||||||
|
oneOf(handshakeCrypto).generateEphemeralKeyPair();
|
||||||
|
will(returnValue(ourEphemeralKeyPair));
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expectSendMinorVersion() throws Exception {
|
||||||
|
expectWriteRecord(new Record(PROTOCOL_MAJOR_VERSION,
|
||||||
|
RECORD_TYPE_MINOR_VERSION,
|
||||||
|
new byte[] {PROTOCOL_MINOR_VERSION}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expectReceiveMinorVersion() throws Exception {
|
||||||
|
expectReadRecord(new Record(PROTOCOL_MAJOR_VERSION,
|
||||||
|
RECORD_TYPE_MINOR_VERSION,
|
||||||
|
new byte[] {PROTOCOL_MINOR_VERSION}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expectSendKey() throws Exception {
|
||||||
|
expectWriteRecord(new Record(PROTOCOL_MAJOR_VERSION,
|
||||||
|
RECORD_TYPE_EPHEMERAL_PUBLIC_KEY,
|
||||||
|
ourEphemeralPublicKey.getEncoded()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expectReceiveKey() throws Exception {
|
||||||
|
expectReadRecord(new Record(PROTOCOL_MAJOR_VERSION,
|
||||||
|
RECORD_TYPE_EPHEMERAL_PUBLIC_KEY,
|
||||||
|
theirEphemeralPublicKey.getEncoded()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expectDeriveMasterKey_0_1(boolean alice) throws Exception {
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(handshakeCrypto).deriveMasterKey_0_1(theirStaticPublicKey,
|
||||||
|
theirEphemeralPublicKey, ourStaticKeyPair,
|
||||||
|
ourEphemeralKeyPair, alice);
|
||||||
|
will(returnValue(masterKey));
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expectDeriveMasterKey_0_0(boolean alice) throws Exception {
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(handshakeCrypto).deriveMasterKey_0_0(theirStaticPublicKey,
|
||||||
|
theirEphemeralPublicKey, ourStaticKeyPair,
|
||||||
|
ourEphemeralKeyPair, alice);
|
||||||
|
will(returnValue(masterKey));
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expectDeriveProof(boolean alice) {
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(handshakeCrypto).proveOwnership(masterKey, alice);
|
||||||
|
will(returnValue(ourProof));
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expectSendProof() throws Exception {
|
||||||
|
expectWriteRecord(new Record(PROTOCOL_MAJOR_VERSION,
|
||||||
|
RECORD_TYPE_PROOF_OF_OWNERSHIP, ourProof));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expectReceiveProof() throws Exception {
|
||||||
|
expectReadRecord(new Record(PROTOCOL_MAJOR_VERSION,
|
||||||
|
RECORD_TYPE_PROOF_OF_OWNERSHIP, theirProof));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expectSendEof() throws Exception {
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(streamWriter).sendEndOfStream();
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expectReceiveEof() throws Exception {
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(recordReader).readRecord(with(any(RecordPredicate.class)),
|
||||||
|
with(any(RecordPredicate.class)));
|
||||||
|
will(returnValue(null));
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expectVerifyOwnership(boolean alice, boolean verified) {
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(handshakeCrypto).verifyOwnership(masterKey, !alice,
|
||||||
|
theirProof);
|
||||||
|
will(returnValue(verified));
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expectWriteRecord(Record record) throws Exception {
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(recordWriter).writeRecord(with(new PredicateMatcher<>(
|
||||||
|
Record.class, r -> recordEquals(record, r))));
|
||||||
|
oneOf(recordWriter).flush();
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean recordEquals(Record expected, Record actual) {
|
||||||
|
return expected.getProtocolVersion() == actual.getProtocolVersion() &&
|
||||||
|
expected.getRecordType() == actual.getRecordType() &&
|
||||||
|
Arrays.equals(expected.getPayload(), actual.getPayload());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expectReadRecord(Record record) throws Exception {
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
// Test that the `accept` predicate passed to the reader would
|
||||||
|
// accept the expected record
|
||||||
|
oneOf(recordReader).readRecord(with(new PredicateMatcher<>(
|
||||||
|
RecordPredicate.class, rp -> rp.test(record))),
|
||||||
|
with(any(RecordPredicate.class)));
|
||||||
|
will(returnValue(record));
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ import org.jmock.Expectations;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
import static java.lang.System.arraycopy;
|
import static java.lang.System.arraycopy;
|
||||||
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.BASE32_LINK_BYTES;
|
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.BASE32_LINK_BYTES;
|
||||||
@@ -174,7 +175,7 @@ public class PendingContactFactoryImplTest extends BrambleMockTestCase {
|
|||||||
rawLink[0] = (byte) formatVersion;
|
rawLink[0] = (byte) formatVersion;
|
||||||
byte[] publicKeyBytes = publicKey.getEncoded();
|
byte[] publicKeyBytes = publicKey.getEncoded();
|
||||||
arraycopy(publicKeyBytes, 0, rawLink, 1, publicKeyBytes.length);
|
arraycopy(publicKeyBytes, 0, rawLink, 1, publicKeyBytes.length);
|
||||||
String base32 = Base32.encode(rawLink).toLowerCase();
|
String base32 = Base32.encode(rawLink).toLowerCase(Locale.US);
|
||||||
assertEquals(BASE32_LINK_BYTES, base32.length());
|
assertEquals(BASE32_LINK_BYTES, base32.length());
|
||||||
return base32;
|
return base32;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,22 @@ public class KeyAgreementTest extends BrambleTestCase {
|
|||||||
assertArrayEquals(aShared.getBytes(), bShared.getBytes());
|
assertArrayEquals(aShared.getBytes(), bShared.getBytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDerivesStaticEphemeralSharedSecretBadly() throws Exception {
|
||||||
|
String label = getRandomString(123);
|
||||||
|
KeyPair aStatic = crypto.generateAgreementKeyPair();
|
||||||
|
KeyPair aEphemeral = crypto.generateAgreementKeyPair();
|
||||||
|
KeyPair bStatic = crypto.generateAgreementKeyPair();
|
||||||
|
KeyPair bEphemeral = crypto.generateAgreementKeyPair();
|
||||||
|
SecretKey aShared = crypto.deriveSharedSecretBadly(label,
|
||||||
|
bStatic.getPublic(), bEphemeral.getPublic(), aStatic,
|
||||||
|
aEphemeral, true, inputs);
|
||||||
|
SecretKey bShared = crypto.deriveSharedSecretBadly(label,
|
||||||
|
aStatic.getPublic(), aEphemeral.getPublic(), bStatic,
|
||||||
|
bEphemeral, false, inputs);
|
||||||
|
assertArrayEquals(aShared.getBytes(), bShared.getBytes());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDerivesStaticEphemeralSharedSecret() throws Exception {
|
public void testDerivesStaticEphemeralSharedSecret() throws Exception {
|
||||||
String label = getRandomString(123);
|
String label = getRandomString(123);
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
|
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
public class OnionEncodingTest extends BrambleTestCase {
|
||||||
|
|
||||||
|
private static final Pattern ONION_V3 = Pattern.compile("[a-z2-7]{56}");
|
||||||
|
|
||||||
|
private final CryptoComponent crypto =
|
||||||
|
new CryptoComponentImpl(new TestSecureRandomProvider(), null);
|
||||||
|
private final SecureRandom secureRandom = new SecureRandom();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHostnameIsValid() {
|
||||||
|
byte[] publicKey = new byte[32];
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
secureRandom.nextBytes(publicKey);
|
||||||
|
String onion = crypto.encodeOnion(publicKey);
|
||||||
|
assertTrue(onion, ONION_V3.matcher(onion).matches());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.briarproject.bramble.keyagreement;
|
package org.briarproject.bramble.keyagreement;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.Predicate;
|
|
||||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
|
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
|
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
|
||||||
@@ -8,11 +7,13 @@ import org.briarproject.bramble.api.plugin.TransportId;
|
|||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||||
import org.briarproject.bramble.api.record.Record;
|
import org.briarproject.bramble.api.record.Record;
|
||||||
import org.briarproject.bramble.api.record.RecordReader;
|
import org.briarproject.bramble.api.record.RecordReader;
|
||||||
|
import org.briarproject.bramble.api.record.RecordReader.RecordPredicate;
|
||||||
import org.briarproject.bramble.api.record.RecordReaderFactory;
|
import org.briarproject.bramble.api.record.RecordReaderFactory;
|
||||||
import org.briarproject.bramble.api.record.RecordWriter;
|
import org.briarproject.bramble.api.record.RecordWriter;
|
||||||
import org.briarproject.bramble.api.record.RecordWriterFactory;
|
import org.briarproject.bramble.api.record.RecordWriterFactory;
|
||||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
import org.briarproject.bramble.test.CaptureArgumentAction;
|
import org.briarproject.bramble.test.CaptureArgumentAction;
|
||||||
|
import org.briarproject.bramble.test.PredicateMatcher;
|
||||||
import org.jmock.Expectations;
|
import org.jmock.Expectations;
|
||||||
import org.jmock.imposters.ByteBuddyClassImposteriser;
|
import org.jmock.imposters.ByteBuddyClassImposteriser;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -21,8 +22,6 @@ import java.io.InputStream;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION;
|
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION;
|
||||||
import static org.briarproject.bramble.api.keyagreement.RecordTypes.ABORT;
|
import static org.briarproject.bramble.api.keyagreement.RecordTypes.ABORT;
|
||||||
import static org.briarproject.bramble.api.keyagreement.RecordTypes.CONFIRM;
|
import static org.briarproject.bramble.api.keyagreement.RecordTypes.CONFIRM;
|
||||||
@@ -119,7 +118,7 @@ public class KeyAgreementTransportTest extends BrambleMockTestCase {
|
|||||||
public void testReceiveKeyThrowsExceptionIfAtEndOfStream()
|
public void testReceiveKeyThrowsExceptionIfAtEndOfStream()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
setup();
|
setup();
|
||||||
expectReadRecord(null);
|
expectReadEof();
|
||||||
|
|
||||||
kat.receiveKey();
|
kat.receiveKey();
|
||||||
}
|
}
|
||||||
@@ -148,7 +147,7 @@ public class KeyAgreementTransportTest extends BrambleMockTestCase {
|
|||||||
public void testReceiveConfirmThrowsExceptionIfAtEndOfStream()
|
public void testReceiveConfirmThrowsExceptionIfAtEndOfStream()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
setup();
|
setup();
|
||||||
expectReadRecord(null);
|
expectReadEof();
|
||||||
|
|
||||||
kat.receiveConfirm();
|
kat.receiveConfirm();
|
||||||
}
|
}
|
||||||
@@ -209,12 +208,22 @@ public class KeyAgreementTransportTest extends BrambleMockTestCase {
|
|||||||
assertArrayEquals(expectedPayload, actual.getPayload());
|
assertArrayEquals(expectedPayload, actual.getPayload());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void expectReadRecord(@Nullable Record record) throws Exception {
|
private void expectReadRecord(Record record) throws Exception {
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
//noinspection unchecked
|
// Test that the `accept` predicate passed to the reader would
|
||||||
oneOf(recordReader).readRecord(with(any(Predicate.class)),
|
// accept the expected record
|
||||||
with(any(Predicate.class)));
|
oneOf(recordReader).readRecord(with(new PredicateMatcher<>(
|
||||||
|
RecordPredicate.class, rp -> rp.test(record))),
|
||||||
|
with(any(RecordPredicate.class)));
|
||||||
will(returnValue(record));
|
will(returnValue(record));
|
||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void expectReadEof() throws Exception {
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(recordReader).readRecord(with(any(RecordPredicate.class)),
|
||||||
|
with(any(RecordPredicate.class)));
|
||||||
|
will(returnValue(null));
|
||||||
|
}});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package org.briarproject.bramble.record;
|
package org.briarproject.bramble.record;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.FormatException;
|
import org.briarproject.bramble.api.FormatException;
|
||||||
import org.briarproject.bramble.api.Predicate;
|
|
||||||
import org.briarproject.bramble.api.record.Record;
|
import org.briarproject.bramble.api.record.Record;
|
||||||
import org.briarproject.bramble.api.record.RecordReader;
|
import org.briarproject.bramble.api.record.RecordReader;
|
||||||
|
import org.briarproject.bramble.api.record.RecordReader.RecordPredicate;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.util.ByteUtils;
|
import org.briarproject.bramble.util.ByteUtils;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -128,12 +128,12 @@ public class RecordReaderImplTest extends BrambleTestCase {
|
|||||||
RecordReader reader = new RecordReaderImpl(in);
|
RecordReader reader = new RecordReaderImpl(in);
|
||||||
|
|
||||||
// Accept records with version 0, type 0 or 1
|
// Accept records with version 0, type 0 or 1
|
||||||
Predicate<Record> accept = r -> {
|
RecordPredicate accept = r -> {
|
||||||
byte version = r.getProtocolVersion(), type = r.getRecordType();
|
byte version = r.getProtocolVersion(), type = r.getRecordType();
|
||||||
return version == 0 && (type == 0 || type == 1);
|
return version == 0 && (type == 0 || type == 1);
|
||||||
};
|
};
|
||||||
// Ignore records with version 0, any other type
|
// Ignore records with version 0, any other type
|
||||||
Predicate<Record> ignore = r -> {
|
RecordPredicate ignore = r -> {
|
||||||
byte version = r.getProtocolVersion(), type = r.getRecordType();
|
byte version = r.getProtocolVersion(), type = r.getRecordType();
|
||||||
return version == 0 && !(type == 0 || type == 1);
|
return version == 0 && !(type == 0 || type == 1);
|
||||||
};
|
};
|
||||||
@@ -183,12 +183,12 @@ public class RecordReaderImplTest extends BrambleTestCase {
|
|||||||
RecordReader reader = new RecordReaderImpl(in);
|
RecordReader reader = new RecordReaderImpl(in);
|
||||||
|
|
||||||
// Accept records with version 0, type 0 or 1
|
// Accept records with version 0, type 0 or 1
|
||||||
Predicate<Record> accept = r -> {
|
RecordPredicate accept = r -> {
|
||||||
byte version = r.getProtocolVersion(), type = r.getRecordType();
|
byte version = r.getProtocolVersion(), type = r.getRecordType();
|
||||||
return version == 0 && (type == 0 || type == 1);
|
return version == 0 && (type == 0 || type == 1);
|
||||||
};
|
};
|
||||||
// Ignore records with version 0, any other type
|
// Ignore records with version 0, any other type
|
||||||
Predicate<Record> ignore = r -> {
|
RecordPredicate ignore = r -> {
|
||||||
byte version = r.getProtocolVersion(), type = r.getRecordType();
|
byte version = r.getProtocolVersion(), type = r.getRecordType();
|
||||||
return version == 0 && !(type == 0 || type == 1);
|
return version == 0 && !(type == 0 || type == 1);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
package org.briarproject.bramble.sync;
|
package org.briarproject.bramble.sync;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.FormatException;
|
import org.briarproject.bramble.api.FormatException;
|
||||||
import org.briarproject.bramble.api.Predicate;
|
|
||||||
import org.briarproject.bramble.api.UniqueId;
|
import org.briarproject.bramble.api.UniqueId;
|
||||||
import org.briarproject.bramble.api.record.Record;
|
import org.briarproject.bramble.api.record.Record;
|
||||||
import org.briarproject.bramble.api.record.RecordReader;
|
import org.briarproject.bramble.api.record.RecordReader;
|
||||||
|
import org.briarproject.bramble.api.record.RecordReader.RecordPredicate;
|
||||||
import org.briarproject.bramble.api.sync.Ack;
|
import org.briarproject.bramble.api.sync.Ack;
|
||||||
import org.briarproject.bramble.api.sync.GroupId;
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
import org.briarproject.bramble.api.sync.Message;
|
import org.briarproject.bramble.api.sync.Message;
|
||||||
@@ -24,8 +24,6 @@ import org.junit.Test;
|
|||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.record.Record.MAX_RECORD_PAYLOAD_BYTES;
|
import static org.briarproject.bramble.api.record.Record.MAX_RECORD_PAYLOAD_BYTES;
|
||||||
import static org.briarproject.bramble.api.sync.RecordTypes.ACK;
|
import static org.briarproject.bramble.api.sync.RecordTypes.ACK;
|
||||||
import static org.briarproject.bramble.api.sync.RecordTypes.MESSAGE;
|
import static org.briarproject.bramble.api.sync.RecordTypes.MESSAGE;
|
||||||
@@ -186,7 +184,7 @@ public class SyncRecordReaderImplTest extends BrambleMockTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testEofReturnsTrueWhenAtEndOfStream() throws Exception {
|
public void testEofReturnsTrueWhenAtEndOfStream() throws Exception {
|
||||||
expectReadRecord(createAck());
|
expectReadRecord(createAck());
|
||||||
expectReadRecord(null);
|
expectReadEof();
|
||||||
|
|
||||||
SyncRecordReader reader =
|
SyncRecordReader reader =
|
||||||
new SyncRecordReaderImpl(messageFactory, recordReader);
|
new SyncRecordReaderImpl(messageFactory, recordReader);
|
||||||
@@ -212,15 +210,25 @@ public class SyncRecordReaderImplTest extends BrambleMockTestCase {
|
|||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void expectReadRecord(@Nullable Record record) throws Exception {
|
private void expectReadRecord(Record record) throws Exception {
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
//noinspection unchecked
|
// Test that the `accept` predicate passed to the reader would
|
||||||
oneOf(recordReader).readRecord(with(any(Predicate.class)),
|
// accept the expected record
|
||||||
with(any(Predicate.class)));
|
oneOf(recordReader).readRecord(with(new PredicateMatcher<>(
|
||||||
|
RecordPredicate.class, rp -> rp.test(record))),
|
||||||
|
with(any(RecordPredicate.class)));
|
||||||
will(returnValue(record));
|
will(returnValue(record));
|
||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void expectReadEof() throws Exception {
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(recordReader).readRecord(with(any(RecordPredicate.class)),
|
||||||
|
with(any(RecordPredicate.class)));
|
||||||
|
will(returnValue(null));
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
|
||||||
private Record createMessage(int payloadLength) {
|
private Record createMessage(int payloadLength) {
|
||||||
return new Record(PROTOCOL_VERSION, MESSAGE, new byte[payloadLength]);
|
return new Record(PROTOCOL_VERSION, MESSAGE, new byte[payloadLength]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -234,12 +234,12 @@ public abstract class BrambleIntegrationTest<C extends BrambleIntegrationTestCom
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void awaitPendingMessageDelivery(int num)
|
protected void awaitPendingMessageDelivery(int num)
|
||||||
throws TimeoutException {
|
throws TimeoutException, InterruptedException {
|
||||||
awaitPendingMessageDelivery(num, TIMEOUT);
|
awaitPendingMessageDelivery(num, TIMEOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void awaitPendingMessageDelivery(int num, long timeout)
|
protected void awaitPendingMessageDelivery(int num, long timeout)
|
||||||
throws TimeoutException {
|
throws TimeoutException, InterruptedException {
|
||||||
deliveryWaiter.await(timeout, num);
|
deliveryWaiter.await(timeout, num);
|
||||||
assertEquals("Messages delivered", num, deliveryCounter.getAndSet(0));
|
assertEquals("Messages delivered", num, deliveryCounter.getAndSet(0));
|
||||||
|
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ dependencyVerification {
|
|||||||
'com.fasterxml.jackson.core:jackson-databind:2.13.4:jackson-databind-2.13.4.jar:c9faff420d9e2c7e1e4711dbeebec2506a32c9942027211c5c293d8d87807eb6',
|
'com.fasterxml.jackson.core:jackson-databind:2.13.4:jackson-databind-2.13.4.jar:c9faff420d9e2c7e1e4711dbeebec2506a32c9942027211c5c293d8d87807eb6',
|
||||||
'com.google.code.findbugs:annotations:3.0.1:annotations-3.0.1.jar:6b47ff0a6de0ce17cbedc3abb0828ca5bce3009d53ea47b3723ff023c4742f79',
|
'com.google.code.findbugs:annotations:3.0.1:annotations-3.0.1.jar:6b47ff0a6de0ce17cbedc3abb0828ca5bce3009d53ea47b3723ff023c4742f79',
|
||||||
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
|
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
|
||||||
'com.google.dagger:dagger-compiler:2.43.2:dagger-compiler-2.43.2.jar:298c020ee6ed2f4cc651ebbfdb7f8de329b07c44b618d65be117846a850e2a03',
|
'com.google.dagger:dagger-compiler:2.45:dagger-compiler-2.45.jar:5617dfb994537dba5b41f3744a6dd13ec3cd99789c065e0d5c6fa9f21cf7ca25',
|
||||||
'com.google.dagger:dagger-producers:2.43.2:dagger-producers-2.43.2.jar:e7f5d9ffc85d48a49c8e22e02833d418f7ccad5d7512f529964db5127ab915ff',
|
'com.google.dagger:dagger-producers:2.45:dagger-producers-2.45.jar:a05abb4c3ccf6bb0f056bdcb5ef973898ecf172952ab5948a824aeea6c86ecaa',
|
||||||
'com.google.dagger:dagger-spi:2.43.2:dagger-spi-2.43.2.jar:3bae8d9dadeaaa5927da6f094389a560c12c05fec3d2711d2fa79292c7a7d7ad',
|
'com.google.dagger:dagger-spi:2.45:dagger-spi-2.45.jar:7cd6f0b09d88e64a9c97bc80e544ab8ac8fdee9301754413585a74cf64222b27',
|
||||||
'com.google.dagger:dagger:2.43.2:dagger-2.43.2.jar:c89681f7cbbf8c527bf4ac2748515d617fdb54a1d425c08d914fdc28192b5fe4',
|
'com.google.dagger:dagger:2.45:dagger-2.45.jar:f011cae7d2c0fb7ea17c34e05bc10e768b1081a5892ad019cf1fdb0e125c49c1',
|
||||||
'com.google.devtools.ksp:symbol-processing-api:1.7.0-1.0.6:symbol-processing-api-1.7.0-1.0.6.jar:adc29417be5ca9ff42118105fea4e36d9ef44987abfc41432309371a60198941',
|
'com.google.devtools.ksp:symbol-processing-api:1.7.0-1.0.6:symbol-processing-api-1.7.0-1.0.6.jar:adc29417be5ca9ff42118105fea4e36d9ef44987abfc41432309371a60198941',
|
||||||
'com.google.errorprone:error_prone_annotations:2.7.1:error_prone_annotations-2.7.1.jar:cd5257c08a246cf8628817ae71cb822be192ef91f6881ca4a3fcff4f1de1cff3',
|
'com.google.errorprone:error_prone_annotations:2.7.1:error_prone_annotations-2.7.1.jar:cd5257c08a246cf8628817ae71cb822be192ef91f6881ca4a3fcff4f1de1cff3',
|
||||||
'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30',
|
'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30',
|
||||||
@@ -19,42 +19,44 @@ dependencyVerification {
|
|||||||
'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:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava:listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar:b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99',
|
||||||
'com.google.j2objc:j2objc-annotations:1.3:j2objc-annotations-1.3.jar:21af30c92267bd6122c0e0b4d20cccb6641a37eaf956c6540ec471d584e64a7b',
|
'com.google.j2objc:j2objc-annotations:1.3:j2objc-annotations-1.3.jar:21af30c92267bd6122c0e0b4d20cccb6641a37eaf956c6540ec471d584e64a7b',
|
||||||
'com.h2database:h2:1.4.192:h2-1.4.192.jar:225b22e9857235c46c93861410b60b8c81c10dc8985f4faf188985ba5445126c',
|
'com.h2database:h2:1.4.192:h2-1.4.192.jar:225b22e9857235c46c93861410b60b8c81c10dc8985f4faf188985ba5445126c',
|
||||||
'com.squareup.okhttp3:mockwebserver:4.9.3:mockwebserver-4.9.3.jar:9c8c581c29f22f877a35d11380462f75bb24bf1886204fe835ee695594a2784e',
|
'com.squareup.okhttp3:mockwebserver:4.10.0:mockwebserver-4.10.0.jar:af29da234e63159d6e0dea43bf8288eea97d71cdf1651a5ee2d6c0d0d4adbf8f',
|
||||||
'com.squareup.okhttp3:okhttp:3.12.13:okhttp-3.12.13.jar:508234e024ef7e270ab1a6d5b356f5b98e786511239ca986d684fd1e2cf7bc82',
|
'com.squareup.okhttp3:okhttp:4.10.0:okhttp-4.10.0.jar:7580f14fa1691206e37081ad3f92063b1603b328da0bb316f2fef02e0562e7ec',
|
||||||
'com.squareup.okhttp3:okhttp:4.9.3:okhttp-4.9.3.jar:93ecd6cba19d87dccfe566ec848d91aae799e3cf16c00709358ea69bd9227219',
|
'com.squareup.okio:okio-jvm:3.0.0:okio-jvm-3.0.0.jar:be64a0cc1f28ea9cd5c970dd7e7557af72c808d738c495b397bf897c9921e907',
|
||||||
'com.squareup.okio:okio:1.15.0:okio-1.15.0.jar:693fa319a7e8843300602b204023b7674f106ebcb577f2dd5807212b66118bd2',
|
|
||||||
'com.squareup.okio:okio:2.8.0:okio-jvm-2.8.0.jar:4496b06e73982fcdd8a5393f46e5df2ce2fa4465df5895454cac68a32f09bbc8',
|
|
||||||
'com.squareup:javapoet:1.13.0:javapoet-1.13.0.jar:4c7517e848a71b36d069d12bb3bf46a70fd4cda3105d822b0ed2e19c00b69291',
|
'com.squareup:javapoet:1.13.0:javapoet-1.13.0.jar:4c7517e848a71b36d069d12bb3bf46a70fd4cda3105d822b0ed2e19c00b69291',
|
||||||
|
'com.squareup:kotlinpoet:1.11.0:kotlinpoet-1.11.0.jar:2887ada1ca03dd83baa2758640d87e840d1907564db0ef88d2289c868a980492',
|
||||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||||
'junit:junit:4.13.2:junit-4.13.2.jar:8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3',
|
'junit:junit:4.13.2:junit-4.13.2.jar:8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3',
|
||||||
'net.bytebuddy:byte-buddy:1.9.12:byte-buddy-1.9.12.jar:3688c3d434bebc3edc5516296a2ed0f47b65e451071b4afecad84f902f0efc11',
|
'net.bytebuddy:byte-buddy:1.9.12:byte-buddy-1.9.12.jar:3688c3d434bebc3edc5516296a2ed0f47b65e451071b4afecad84f902f0efc11',
|
||||||
'net.i2p.crypto:eddsa:0.2.0:eddsa-0.2.0.jar:a7cb1b85c16e2f0730b9204106929a1d9aaae1df728adc7041a8b8b605692140',
|
'net.i2p.crypto:eddsa:0.2.0:eddsa-0.2.0.jar:a7cb1b85c16e2f0730b9204106929a1d9aaae1df728adc7041a8b8b605692140',
|
||||||
'net.jcip:jcip-annotations:1.0:jcip-annotations-1.0.jar:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0',
|
'net.jcip:jcip-annotations:1.0:jcip-annotations-1.0.jar:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0',
|
||||||
'net.jodah:concurrentunit:0.4.2:concurrentunit-0.4.2.jar:5583078e1acf91734939e985bc9e7ee947b0e93a8eef679da6bb07bbeb47ced3',
|
'net.jodah:concurrentunit:0.4.6:concurrentunit-0.4.6.jar:760e6d4ab7801484de09da621b61141f3b2c2432949da9eb13f076e5d9a5e0a5',
|
||||||
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
|
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
|
||||||
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
|
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
|
||||||
'org.bitlet:weupnp:0.1.4:weupnp-0.1.4.jar:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
|
'org.bitlet:weupnp:0.1.4:weupnp-0.1.4.jar:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
|
||||||
'org.bouncycastle:bcprov-jdk15to18:1.71:bcprov-jdk15to18-1.71.jar:143aaa4a40edd5fc2a18db7900059f6c16f4d931b94b94b20f7e2238e6662886',
|
'org.bouncycastle:bcprov-jdk15to18:1.71:bcprov-jdk15to18-1.71.jar:143aaa4a40edd5fc2a18db7900059f6c16f4d931b94b94b20f7e2238e6662886',
|
||||||
'org.briarproject:jtorctl:0.5:jtorctl-0.5.jar:43f8c7d390169772b9a2c82ab806c8414c136a2a8636c555e22754bb7260793b',
|
'org.briarproject:jtorctl:0.5:jtorctl-0.5.jar:43f8c7d390169772b9a2c82ab806c8414c136a2a8636c555e22754bb7260793b',
|
||||||
'org.briarproject:null-safety:0.1:null-safety-0.1.jar:161760de5e838cb982bafa973df820675d4397098e9a91637a36a306d43ba011',
|
'org.briarproject:null-safety:0.1:null-safety-0.1.jar:161760de5e838cb982bafa973df820675d4397098e9a91637a36a306d43ba011',
|
||||||
'org.briarproject:onionwrapper-core:0.0.1:onionwrapper-core-0.0.1.jar:a1937506b00ee6620e909a500e5d004be81f94a6f7d7c898e1a9e841a8ae8a2a',
|
'org.briarproject:onionwrapper-core:0.0.4:onionwrapper-core-0.0.4.jar:28a01a62e96aa763989a8afc325abd3bee54f8021269f91aa48b247a6e717870',
|
||||||
'org.briarproject:socks-socket:0.1:socks-socket-0.1.jar:e5898822d10f5390363c5dddb945891648c92cf93ba50709e07f0d173ec0eb4b',
|
'org.briarproject:socks-socket:0.1:socks-socket-0.1.jar:e5898822d10f5390363c5dddb945891648c92cf93ba50709e07f0d173ec0eb4b',
|
||||||
'org.checkerframework:checker-compat-qual:2.5.5:checker-compat-qual-2.5.5.jar:11d134b245e9cacc474514d2d66b5b8618f8039a1465cdc55bbc0b34e0008b7a',
|
'org.checkerframework:checker-compat-qual:2.5.5:checker-compat-qual-2.5.5.jar:11d134b245e9cacc474514d2d66b5b8618f8039a1465cdc55bbc0b34e0008b7a',
|
||||||
'org.checkerframework:checker-qual:3.12.0:checker-qual-3.12.0.jar:ff10785ac2a357ec5de9c293cb982a2cbb605c0309ea4cc1cb9b9bc6dbe7f3cb',
|
'org.checkerframework:checker-qual:3.12.0:checker-qual-3.12.0.jar:ff10785ac2a357ec5de9c293cb982a2cbb605c0309ea4cc1cb9b9bc6dbe7f3cb',
|
||||||
'org.codehaus.mojo.signature:java16:1.1:java16-1.1.signature:53799223a2c98dba2d0add810bed76315460df285c69e4f397ae6098f87dd619',
|
'org.codehaus.mojo.signature:java16:1.1:java16-1.1.signature:53799223a2c98dba2d0add810bed76315460df285c69e4f397ae6098f87dd619',
|
||||||
'org.codehaus.mojo:animal-sniffer-ant-tasks:1.20:animal-sniffer-ant-tasks-1.20.jar:bb7d2498144118311d968bb08ff6fae3fc535fb1cb9cca8b8e9ea65b189422ac',
|
'org.codehaus.mojo:animal-sniffer-ant-tasks:1.22:animal-sniffer-ant-tasks-1.22.jar:3f6afeb3e09301d2d7179ed1db21e3ad8846c1e38415ad832a395138ae3f4218',
|
||||||
'org.codehaus.mojo:animal-sniffer:1.20:animal-sniffer-1.20.jar:80c422523c38db91260c6d78e5ee4b012862ab61cc55020c9e243dd7b5c62249',
|
'org.codehaus.mojo:animal-sniffer:1.22:animal-sniffer-1.22.jar:f18c11a25bdd8b520b9c6a28cbb6f33007c812ab0051b6be3f0778e660aa501c',
|
||||||
'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9',
|
'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9',
|
||||||
'org.hamcrest:hamcrest-library:2.1:hamcrest-library-2.1.jar:b7e2b6895b3b679f0e47b6380fda391b225e9b78505db9d8bdde8d3cc8d52a21',
|
'org.hamcrest:hamcrest-library:2.1:hamcrest-library-2.1.jar:b7e2b6895b3b679f0e47b6380fda391b225e9b78505db9d8bdde8d3cc8d52a21',
|
||||||
'org.hamcrest:hamcrest:2.1:hamcrest-2.1.jar:ba93b2e3a562322ba432f0a1b53addcc55cb188253319a020ed77f824e692050',
|
'org.hamcrest:hamcrest:2.1:hamcrest-2.1.jar:ba93b2e3a562322ba432f0a1b53addcc55cb188253319a020ed77f824e692050',
|
||||||
'org.hsqldb:hsqldb:2.3.5:hsqldb-2.3.5.jar:6676a6977ac98997a80f827ddbd3fe8ca1e0853dad1492512135fd1a222ccfad',
|
'org.hsqldb:hsqldb:2.3.5:hsqldb-2.3.5.jar:6676a6977ac98997a80f827ddbd3fe8ca1e0853dad1492512135fd1a222ccfad',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib-common:1.4.10:kotlin-stdlib-common-1.4.10.jar:4681f2d436a68c7523595d84ed5758e1382f9da0f67c91e6a848690d711274fe',
|
'org.jetbrains.kotlin:kotlin-reflect:1.6.10:kotlin-reflect-1.6.10.jar:3277ac102ae17aad10a55abec75ff5696c8d109790396434b496e75087854203',
|
||||||
|
'org.jetbrains.kotlin:kotlin-stdlib-common:1.6.20:kotlin-stdlib-common-1.6.20.jar:8da40a2520d30dcb1012176fe93d24e82d08a3e346c37e0343b0fb6f64f6be01',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib-common:1.7.0:kotlin-stdlib-common-1.7.0.jar:59c6ff64fe9a6604afce03e8aaa75f83586c6030ac71fb0b34ee7cdefed3618f',
|
'org.jetbrains.kotlin:kotlin-stdlib-common:1.7.0:kotlin-stdlib-common-1.7.0.jar:59c6ff64fe9a6604afce03e8aaa75f83586c6030ac71fb0b34ee7cdefed3618f',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.10:kotlin-stdlib-jdk7-1.4.10.jar:f9566380c08722c780ce33ceee23e98ddf765ca98fabd3e2fabae7975c8d232b',
|
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.5.31:kotlin-stdlib-jdk7-1.5.31.jar:a25bf47353ce899d843cbddee516d621a73473e7fba97f8d0301e7b4aed7c15f',
|
||||||
|
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.10:kotlin-stdlib-jdk7-1.6.10.jar:2aedcdc6b69b33bdf5cc235bcea88e7cf6601146bb6bcdffdb312bbacd7be261',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.0:kotlin-stdlib-jdk7-1.7.0.jar:07e91be9b2ca20672d2bdb7e181b766e73453a2da13492b5ddaee8fa47aea239',
|
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.0:kotlin-stdlib-jdk7-1.7.0.jar:07e91be9b2ca20672d2bdb7e181b766e73453a2da13492b5ddaee8fa47aea239',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.10:kotlin-stdlib-jdk8-1.4.10.jar:39b7a9442d7a3865e0f4a732c56c1d5da0e11ffb3bb82a461d32deb0c0ca7673',
|
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.31:kotlin-stdlib-jdk8-1.5.31.jar:b548f7767aacf029d2417e47440742bd6d3ebede19b60386e23554ce5c4c5fdc',
|
||||||
|
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.10:kotlin-stdlib-jdk8-1.6.10.jar:1456d82d039ea30d8485b032901f52bbf07e7cdbe8bb1f8708ad32a8574c41ce',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.0:kotlin-stdlib-jdk8-1.7.0.jar:cf058e11db1dfc9944680c8c61b95ac689aaaa8a3eb30bced028100f038f030b',
|
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.0:kotlin-stdlib-jdk8-1.7.0.jar:cf058e11db1dfc9944680c8c61b95ac689aaaa8a3eb30bced028100f038f030b',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib:1.4.10:kotlin-stdlib-1.4.10.jar:01ecb09782c042b931c1839acf21a188340b295d05400afd6e3415d4475b8daa',
|
'org.jetbrains.kotlin:kotlin-stdlib:1.6.20:kotlin-stdlib-1.6.20.jar:eeb51c2b67b26233fd81d0bc4f8044ec849718890905763ceffd84a31e2cb799',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib:1.7.0:kotlin-stdlib-1.7.0.jar:aa88e9625577957f3249a46cb6e166ee09b369e600f7a11d148d16b0a6d87f05',
|
'org.jetbrains.kotlin:kotlin-stdlib:1.7.0:kotlin-stdlib-1.7.0.jar:aa88e9625577957f3249a46cb6e166ee09b369e600f7a11d148d16b0a6d87f05',
|
||||||
'org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.5.0:kotlinx-metadata-jvm-0.5.0.jar:ca063a96639b08b9eaa0de4d65e899480740a6efbe28ab9a8681a2ced03055a4',
|
'org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.5.0:kotlinx-metadata-jvm-0.5.0.jar:ca063a96639b08b9eaa0de4d65e899480740a6efbe28ab9a8681a2ced03055a4',
|
||||||
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
|
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
|
||||||
@@ -65,7 +67,7 @@ dependencyVerification {
|
|||||||
'org.jmock:jmock:2.12.0:jmock-2.12.0.jar:266d07314c0cd343c46ff8a55601272de8cf406807caf55e6f313295f83d10be',
|
'org.jmock:jmock:2.12.0:jmock-2.12.0.jar:266d07314c0cd343c46ff8a55601272de8cf406807caf55e6f313295f83d10be',
|
||||||
'org.objenesis:objenesis:3.0.1:objenesis-3.0.1.jar:7a8ff780b9ff48415d7c705f60030b0acaa616e7f823c98eede3b63508d4e984',
|
'org.objenesis:objenesis:3.0.1:objenesis-3.0.1.jar:7a8ff780b9ff48415d7c705f60030b0acaa616e7f823c98eede3b63508d4e984',
|
||||||
'org.ow2.asm:asm:7.1:asm-7.1.jar:4ab2fa2b6d2cc9ccb1eaa05ea329c407b47b13ed2915f62f8c4b8cc96258d4de',
|
'org.ow2.asm:asm:7.1:asm-7.1.jar:4ab2fa2b6d2cc9ccb1eaa05ea329c407b47b13ed2915f62f8c4b8cc96258d4de',
|
||||||
'org.ow2.asm:asm:9.1:asm-9.1.jar:cda4de455fab48ff0bcb7c48b4639447d4de859a7afc30a094a986f0936beba2',
|
'org.ow2.asm:asm:9.3:asm-9.3.jar:1263369b59e29c943918de11d6d6152e2ec6085ce63e5710516f8c67d368e4bc',
|
||||||
'org.whispersystems:curve25519-java:0.5.0:curve25519-java-0.5.0.jar:0aadd43cf01d11e9b58f867b3c4f25c3194e8b0623d1953d32dfbfbee009e38d',
|
'org.whispersystems:curve25519-java:0.5.0:curve25519-java-0.5.0.jar:0aadd43cf01d11e9b58f867b3c4f25c3194e8b0623d1953d32dfbfbee009e38d',
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ dependencies {
|
|||||||
implementation project(':bramble-core')
|
implementation project(':bramble-core')
|
||||||
|
|
||||||
implementation fileTree(dir: 'libs', include: '*.jar')
|
implementation fileTree(dir: 'libs', include: '*.jar')
|
||||||
def jna_version = '4.5.2'
|
def jna_version = '5.13.0'
|
||||||
implementation "net.java.dev.jna:jna:$jna_version"
|
implementation "net.java.dev.jna:jna:$jna_version"
|
||||||
implementation "net.java.dev.jna:jna-platform:$jna_version"
|
implementation "net.java.dev.jna:jna-platform:$jna_version"
|
||||||
implementation "org.briarproject:onionwrapper-java:$onionwrapper_version"
|
implementation "org.briarproject:onionwrapper-java:$onionwrapper_version"
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package org.briarproject.bramble;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.network.JavaNetworkModule;
|
||||||
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
public interface BrambleJavaEagerSingletons {
|
||||||
|
|
||||||
|
void inject(JavaNetworkModule.EagerSingletons init);
|
||||||
|
|
||||||
|
class Helper {
|
||||||
|
|
||||||
|
public static void injectEagerSingletons(BrambleJavaEagerSingletons c) {
|
||||||
|
c.inject(new JavaNetworkModule.EagerSingletons());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,9 +3,9 @@ package org.briarproject.bramble;
|
|||||||
import org.briarproject.bramble.io.DnsModule;
|
import org.briarproject.bramble.io.DnsModule;
|
||||||
import org.briarproject.bramble.mailbox.ModularMailboxModule;
|
import org.briarproject.bramble.mailbox.ModularMailboxModule;
|
||||||
import org.briarproject.bramble.network.JavaNetworkModule;
|
import org.briarproject.bramble.network.JavaNetworkModule;
|
||||||
|
import org.briarproject.bramble.plugin.tor.CircumventionModule;
|
||||||
import org.briarproject.bramble.socks.SocksModule;
|
import org.briarproject.bramble.socks.SocksModule;
|
||||||
import org.briarproject.bramble.system.JavaSystemModule;
|
import org.briarproject.bramble.system.JavaSystemModule;
|
||||||
import org.briarproject.onionwrapper.CircumventionModule;
|
|
||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
|
|
||||||
|
|||||||
@@ -1,33 +1,51 @@
|
|||||||
package org.briarproject.bramble.network;
|
package org.briarproject.bramble.network;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
|
import org.briarproject.bramble.api.lifecycle.Service;
|
||||||
import org.briarproject.bramble.api.network.NetworkManager;
|
import org.briarproject.bramble.api.network.NetworkManager;
|
||||||
import org.briarproject.bramble.api.network.NetworkStatus;
|
import org.briarproject.bramble.api.network.NetworkStatus;
|
||||||
import org.briarproject.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.bramble.api.network.event.NetworkStatusEvent;
|
||||||
import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.bramble.api.system.TaskScheduler;
|
||||||
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import java.net.Inet4Address;
|
import java.net.Inet4Address;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.NetworkInterface;
|
import java.net.NetworkInterface;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static java.util.Collections.list;
|
import static java.util.Collections.list;
|
||||||
|
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||||
|
import static java.util.logging.Level.INFO;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static java.util.logging.Logger.getLogger;
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
import static org.briarproject.bramble.util.NetworkUtils.getNetworkInterfaces;
|
import static org.briarproject.bramble.util.NetworkUtils.getNetworkInterfaces;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@NotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
class JavaNetworkManager implements NetworkManager, Service {
|
||||||
class JavaNetworkManager implements NetworkManager {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
getLogger(JavaNetworkManager.class.getName());
|
getLogger(JavaNetworkManager.class.getName());
|
||||||
|
|
||||||
|
private final TaskScheduler scheduler;
|
||||||
|
private final Executor ioExecutor;
|
||||||
|
private final EventBus eventBus;
|
||||||
|
private final AtomicReference<NetworkStatus> lastStatus =
|
||||||
|
new AtomicReference<>();
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
JavaNetworkManager() {
|
JavaNetworkManager(TaskScheduler scheduler,
|
||||||
|
@IoExecutor Executor ioExecutor,
|
||||||
|
EventBus eventBus) {
|
||||||
|
this.scheduler = scheduler;
|
||||||
|
this.ioExecutor = ioExecutor;
|
||||||
|
this.eventBus = eventBus;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -48,7 +66,29 @@ class JavaNetworkManager implements NetworkManager {
|
|||||||
} catch (SocketException e) {
|
} catch (SocketException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
}
|
}
|
||||||
|
if (LOG.isLoggable(INFO)) {
|
||||||
|
LOG.info("Connected: " + connected
|
||||||
|
+ ", has IPv4 address: " + hasIpv4
|
||||||
|
+ ", has IPv6 unicast address: " + hasIpv6Unicast);
|
||||||
|
}
|
||||||
return new NetworkStatus(connected, false, !hasIpv4 && hasIpv6Unicast);
|
return new NetworkStatus(connected, false, !hasIpv4 && hasIpv6Unicast);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void broadcastNetworkStatusIfChanged() {
|
||||||
|
NetworkStatus status = getNetworkStatus();
|
||||||
|
NetworkStatus old = lastStatus.getAndSet(status);
|
||||||
|
if (!status.equals(old)) {
|
||||||
|
eventBus.broadcast(new NetworkStatusEvent(status));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startService() {
|
||||||
|
scheduler.scheduleWithFixedDelay(this::broadcastNetworkStatusIfChanged,
|
||||||
|
ioExecutor, 0, 10, SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stopService() {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package org.briarproject.bramble.network;
|
package org.briarproject.bramble.network;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.api.network.NetworkManager;
|
import org.briarproject.bramble.api.network.NetworkManager;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
@@ -10,9 +12,16 @@ import dagger.Provides;
|
|||||||
@Module
|
@Module
|
||||||
public class JavaNetworkModule {
|
public class JavaNetworkModule {
|
||||||
|
|
||||||
|
public static class EagerSingletons {
|
||||||
|
@Inject
|
||||||
|
NetworkManager networkManager;
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
NetworkManager provideNetworkManager(JavaNetworkManager networkManager) {
|
NetworkManager provideNetworkManager(LifecycleManager lifecycleManager,
|
||||||
|
JavaNetworkManager networkManager) {
|
||||||
|
lifecycleManager.registerService(networkManager);
|
||||||
return networkManager;
|
return networkManager;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package org.briarproject.bramble.plugin.tor;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.battery.BatteryManager;
|
||||||
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
|
import org.briarproject.bramble.api.event.EventExecutor;
|
||||||
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
|
import org.briarproject.bramble.api.network.NetworkManager;
|
||||||
|
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.TorControlPort;
|
||||||
|
import org.briarproject.bramble.api.plugin.TorDirectory;
|
||||||
|
import org.briarproject.bramble.api.plugin.TorSocksPort;
|
||||||
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
|
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
||||||
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.onionwrapper.CircumventionProvider;
|
||||||
|
import org.briarproject.onionwrapper.LocationUtils;
|
||||||
|
import org.briarproject.onionwrapper.MacTorWrapper;
|
||||||
|
import org.briarproject.onionwrapper.TorWrapper;
|
||||||
|
import org.briarproject.onionwrapper.UnixTorWrapper;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.net.SocketFactory;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.INFO;
|
||||||
|
import static org.briarproject.bramble.util.OsUtils.isMac;
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
public class MacTorPluginFactory extends TorPluginFactory {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
MacTorPluginFactory(@IoExecutor Executor ioExecutor,
|
||||||
|
@EventExecutor Executor eventExecutor,
|
||||||
|
@WakefulIoExecutor Executor wakefulIoExecutor,
|
||||||
|
NetworkManager networkManager,
|
||||||
|
LocationUtils locationUtils,
|
||||||
|
EventBus eventBus,
|
||||||
|
SocketFactory torSocketFactory,
|
||||||
|
BackoffFactory backoffFactory,
|
||||||
|
CircumventionProvider circumventionProvider,
|
||||||
|
BatteryManager batteryManager,
|
||||||
|
Clock clock,
|
||||||
|
CryptoComponent crypto,
|
||||||
|
@TorDirectory File torDirectory,
|
||||||
|
@TorSocksPort int torSocksPort,
|
||||||
|
@TorControlPort int torControlPort) {
|
||||||
|
super(ioExecutor, eventExecutor, wakefulIoExecutor, networkManager,
|
||||||
|
locationUtils, eventBus, torSocketFactory, backoffFactory,
|
||||||
|
circumventionProvider, batteryManager, clock, crypto,
|
||||||
|
torDirectory, torSocksPort, torControlPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
String getArchitectureForTorBinary() {
|
||||||
|
if (!isMac()) return null;
|
||||||
|
String arch = System.getProperty("os.arch");
|
||||||
|
if (LOG.isLoggable(INFO)) {
|
||||||
|
LOG.info("System's os.arch is " + arch);
|
||||||
|
}
|
||||||
|
if (arch.equals("x86_64")) return "x86_64";
|
||||||
|
else if (arch.equals("aarch64")) return "aarch64";
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
TorPlugin createPluginInstance(Backoff backoff,
|
||||||
|
TorRendezvousCrypto torRendezvousCrypto, PluginCallback callback,
|
||||||
|
String architecture) {
|
||||||
|
TorWrapper tor = new MacTorWrapper(ioExecutor, eventExecutor,
|
||||||
|
architecture, torDirectory, torSocksPort, torControlPort);
|
||||||
|
return new TorPlugin(ioExecutor, wakefulIoExecutor, networkManager,
|
||||||
|
locationUtils, torSocketFactory, circumventionProvider,
|
||||||
|
batteryManager, backoff, torRendezvousCrypto, tor, callback,
|
||||||
|
MAX_LATENCY, MAX_IDLE_TIME, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,10 +13,10 @@ import org.briarproject.bramble.api.plugin.TorControlPort;
|
|||||||
import org.briarproject.bramble.api.plugin.TorDirectory;
|
import org.briarproject.bramble.api.plugin.TorDirectory;
|
||||||
import org.briarproject.bramble.api.plugin.TorSocksPort;
|
import org.briarproject.bramble.api.plugin.TorSocksPort;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
|
||||||
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.onionwrapper.CircumventionProvider;
|
import org.briarproject.onionwrapper.CircumventionProvider;
|
||||||
|
import org.briarproject.onionwrapper.LocationUtils;
|
||||||
import org.briarproject.onionwrapper.TorWrapper;
|
import org.briarproject.onionwrapper.TorWrapper;
|
||||||
import org.briarproject.onionwrapper.UnixTorWrapper;
|
import org.briarproject.onionwrapper.UnixTorWrapper;
|
||||||
|
|
||||||
|
|||||||
@@ -13,10 +13,10 @@ import org.briarproject.bramble.api.plugin.TorControlPort;
|
|||||||
import org.briarproject.bramble.api.plugin.TorDirectory;
|
import org.briarproject.bramble.api.plugin.TorDirectory;
|
||||||
import org.briarproject.bramble.api.plugin.TorSocksPort;
|
import org.briarproject.bramble.api.plugin.TorSocksPort;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
|
||||||
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.onionwrapper.CircumventionProvider;
|
import org.briarproject.onionwrapper.CircumventionProvider;
|
||||||
|
import org.briarproject.onionwrapper.LocationUtils;
|
||||||
import org.briarproject.onionwrapper.TorWrapper;
|
import org.briarproject.onionwrapper.TorWrapper;
|
||||||
import org.briarproject.onionwrapper.WindowsTorWrapper;
|
import org.briarproject.onionwrapper.WindowsTorWrapper;
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ public class DesktopSecureRandomModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
SecureRandomProvider provideSecureRandomProvider() {
|
SecureRandomProvider provideSecureRandomProvider() {
|
||||||
if (isLinux() || isMac()) return new UnixSecureRandomProvider();
|
if (isLinux()) return new UnixSecureRandomProvider();
|
||||||
return () -> null; // Use system default
|
return () -> null; // Use system default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
package org.briarproject.bramble.system;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
class JavaLocationUtils implements LocationUtils {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
Logger.getLogger(JavaLocationUtils.class.getName());
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
JavaLocationUtils() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCurrentCountry() {
|
|
||||||
LOG.info("Using user-defined locale");
|
|
||||||
return Locale.getDefault().getCountry();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
package org.briarproject.bramble.system;
|
package org.briarproject.bramble.system;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
|
||||||
import org.briarproject.bramble.api.system.ResourceProvider;
|
import org.briarproject.bramble.api.system.ResourceProvider;
|
||||||
|
import org.briarproject.onionwrapper.JavaLocationUtilsFactory;
|
||||||
|
import org.briarproject.onionwrapper.LocationUtils;
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
@@ -13,8 +14,8 @@ public class JavaSystemModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
LocationUtils provideLocationUtils(JavaLocationUtils locationUtils) {
|
LocationUtils provideLocationUtils() {
|
||||||
return locationUtils;
|
return JavaLocationUtilsFactory.createJavaLocationUtils();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ dependencyVerification {
|
|||||||
'cglib:cglib:3.2.8:cglib-3.2.8.jar:3f64de999ecc5595dc84ca8ff0879d8a34c8623f9ef3c517a53ed59023fcb9db',
|
'cglib:cglib:3.2.8:cglib-3.2.8.jar:3f64de999ecc5595dc84ca8ff0879d8a34c8623f9ef3c517a53ed59023fcb9db',
|
||||||
'com.google.code.findbugs:annotations:3.0.1:annotations-3.0.1.jar:6b47ff0a6de0ce17cbedc3abb0828ca5bce3009d53ea47b3723ff023c4742f79',
|
'com.google.code.findbugs:annotations:3.0.1:annotations-3.0.1.jar:6b47ff0a6de0ce17cbedc3abb0828ca5bce3009d53ea47b3723ff023c4742f79',
|
||||||
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
|
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
|
||||||
'com.google.dagger:dagger-compiler:2.43.2:dagger-compiler-2.43.2.jar:298c020ee6ed2f4cc651ebbfdb7f8de329b07c44b618d65be117846a850e2a03',
|
'com.google.dagger:dagger-compiler:2.45:dagger-compiler-2.45.jar:5617dfb994537dba5b41f3744a6dd13ec3cd99789c065e0d5c6fa9f21cf7ca25',
|
||||||
'com.google.dagger:dagger-producers:2.43.2:dagger-producers-2.43.2.jar:e7f5d9ffc85d48a49c8e22e02833d418f7ccad5d7512f529964db5127ab915ff',
|
'com.google.dagger:dagger-producers:2.45:dagger-producers-2.45.jar:a05abb4c3ccf6bb0f056bdcb5ef973898ecf172952ab5948a824aeea6c86ecaa',
|
||||||
'com.google.dagger:dagger-spi:2.43.2:dagger-spi-2.43.2.jar:3bae8d9dadeaaa5927da6f094389a560c12c05fec3d2711d2fa79292c7a7d7ad',
|
'com.google.dagger:dagger-spi:2.45:dagger-spi-2.45.jar:7cd6f0b09d88e64a9c97bc80e544ab8ac8fdee9301754413585a74cf64222b27',
|
||||||
'com.google.dagger:dagger:2.43.2:dagger-2.43.2.jar:c89681f7cbbf8c527bf4ac2748515d617fdb54a1d425c08d914fdc28192b5fe4',
|
'com.google.dagger:dagger:2.45:dagger-2.45.jar:f011cae7d2c0fb7ea17c34e05bc10e768b1081a5892ad019cf1fdb0e125c49c1',
|
||||||
'com.google.devtools.ksp:symbol-processing-api:1.7.0-1.0.6:symbol-processing-api-1.7.0-1.0.6.jar:adc29417be5ca9ff42118105fea4e36d9ef44987abfc41432309371a60198941',
|
'com.google.devtools.ksp:symbol-processing-api:1.7.0-1.0.6:symbol-processing-api-1.7.0-1.0.6.jar:adc29417be5ca9ff42118105fea4e36d9ef44987abfc41432309371a60198941',
|
||||||
'com.google.errorprone:error_prone_annotations:2.7.1:error_prone_annotations-2.7.1.jar:cd5257c08a246cf8628817ae71cb822be192ef91f6881ca4a3fcff4f1de1cff3',
|
'com.google.errorprone:error_prone_annotations:2.7.1:error_prone_annotations-2.7.1.jar:cd5257c08a246cf8628817ae71cb822be192ef91f6881ca4a3fcff4f1de1cff3',
|
||||||
'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30',
|
'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30',
|
||||||
@@ -16,23 +16,25 @@ dependencyVerification {
|
|||||||
'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:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava:listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar:b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99',
|
||||||
'com.google.j2objc:j2objc-annotations:1.3:j2objc-annotations-1.3.jar:21af30c92267bd6122c0e0b4d20cccb6641a37eaf956c6540ec471d584e64a7b',
|
'com.google.j2objc:j2objc-annotations:1.3:j2objc-annotations-1.3.jar:21af30c92267bd6122c0e0b4d20cccb6641a37eaf956c6540ec471d584e64a7b',
|
||||||
'com.squareup:javapoet:1.13.0:javapoet-1.13.0.jar:4c7517e848a71b36d069d12bb3bf46a70fd4cda3105d822b0ed2e19c00b69291',
|
'com.squareup:javapoet:1.13.0:javapoet-1.13.0.jar:4c7517e848a71b36d069d12bb3bf46a70fd4cda3105d822b0ed2e19c00b69291',
|
||||||
|
'com.squareup:kotlinpoet:1.11.0:kotlinpoet-1.11.0.jar:2887ada1ca03dd83baa2758640d87e840d1907564db0ef88d2289c868a980492',
|
||||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||||
'junit:junit:4.13.2:junit-4.13.2.jar:8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3',
|
'junit:junit:4.13.2:junit-4.13.2.jar:8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3',
|
||||||
'net.bytebuddy:byte-buddy:1.9.12:byte-buddy-1.9.12.jar:3688c3d434bebc3edc5516296a2ed0f47b65e451071b4afecad84f902f0efc11',
|
'net.bytebuddy:byte-buddy:1.9.12:byte-buddy-1.9.12.jar:3688c3d434bebc3edc5516296a2ed0f47b65e451071b4afecad84f902f0efc11',
|
||||||
'net.java.dev.jna:jna-platform:4.5.2:jna-platform-4.5.2.jar:f1d00c167d8921c6e23c626ef9f1c3ae0be473c95c68ffa012bc7ae55a87e2d6',
|
'net.java.dev.jna:jna-platform:5.13.0:jna-platform-5.13.0.jar:474d7b88f6e97009b6ec1d98c3024dd95c23187c65dabfbc35331bcac3d173dd',
|
||||||
'net.java.dev.jna:jna:4.5.2:jna-4.5.2.jar:0c8eb7acf67261656d79005191debaba3b6bf5dd60a43735a245429381dbecff',
|
'net.java.dev.jna:jna:5.13.0:jna-5.13.0.jar:66d4f819a062a51a1d5627bffc23fac55d1677f0e0a1feba144aabdd670a64bb',
|
||||||
'net.jcip:jcip-annotations:1.0:jcip-annotations-1.0.jar:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0',
|
'net.jcip:jcip-annotations:1.0:jcip-annotations-1.0.jar:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0',
|
||||||
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
|
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
|
||||||
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
|
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
|
||||||
'org.briarproject:jtorctl:0.5:jtorctl-0.5.jar:43f8c7d390169772b9a2c82ab806c8414c136a2a8636c555e22754bb7260793b',
|
'org.briarproject:jtorctl:0.5:jtorctl-0.5.jar:43f8c7d390169772b9a2c82ab806c8414c136a2a8636c555e22754bb7260793b',
|
||||||
'org.briarproject:null-safety:0.1:null-safety-0.1.jar:161760de5e838cb982bafa973df820675d4397098e9a91637a36a306d43ba011',
|
'org.briarproject:null-safety:0.1:null-safety-0.1.jar:161760de5e838cb982bafa973df820675d4397098e9a91637a36a306d43ba011',
|
||||||
'org.briarproject:onionwrapper-core:0.0.1:onionwrapper-core-0.0.1.jar:a1937506b00ee6620e909a500e5d004be81f94a6f7d7c898e1a9e841a8ae8a2a',
|
'org.briarproject:onionwrapper-core:0.0.4:onionwrapper-core-0.0.4.jar:28a01a62e96aa763989a8afc325abd3bee54f8021269f91aa48b247a6e717870',
|
||||||
'org.briarproject:onionwrapper-java:0.0.1:onionwrapper-java-0.0.1.jar:102ccea934d02b13702fd28e890e27e342db8b669a4c84bb54a3783cb8926552',
|
'org.briarproject:onionwrapper-java:0.0.4:onionwrapper-java-0.0.4.jar:7806ef878074498653b557e26eb70e6007df3450d6a910a2e9a322f7eb4df442',
|
||||||
'org.checkerframework:checker-compat-qual:2.5.5:checker-compat-qual-2.5.5.jar:11d134b245e9cacc474514d2d66b5b8618f8039a1465cdc55bbc0b34e0008b7a',
|
'org.checkerframework:checker-compat-qual:2.5.5:checker-compat-qual-2.5.5.jar:11d134b245e9cacc474514d2d66b5b8618f8039a1465cdc55bbc0b34e0008b7a',
|
||||||
'org.checkerframework:checker-qual:3.12.0:checker-qual-3.12.0.jar:ff10785ac2a357ec5de9c293cb982a2cbb605c0309ea4cc1cb9b9bc6dbe7f3cb',
|
'org.checkerframework:checker-qual:3.12.0:checker-qual-3.12.0.jar:ff10785ac2a357ec5de9c293cb982a2cbb605c0309ea4cc1cb9b9bc6dbe7f3cb',
|
||||||
'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9',
|
'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9',
|
||||||
'org.hamcrest:hamcrest-library:2.1:hamcrest-library-2.1.jar:b7e2b6895b3b679f0e47b6380fda391b225e9b78505db9d8bdde8d3cc8d52a21',
|
'org.hamcrest:hamcrest-library:2.1:hamcrest-library-2.1.jar:b7e2b6895b3b679f0e47b6380fda391b225e9b78505db9d8bdde8d3cc8d52a21',
|
||||||
'org.hamcrest:hamcrest:2.1:hamcrest-2.1.jar:ba93b2e3a562322ba432f0a1b53addcc55cb188253319a020ed77f824e692050',
|
'org.hamcrest:hamcrest:2.1:hamcrest-2.1.jar:ba93b2e3a562322ba432f0a1b53addcc55cb188253319a020ed77f824e692050',
|
||||||
|
'org.jetbrains.kotlin:kotlin-reflect:1.6.10:kotlin-reflect-1.6.10.jar:3277ac102ae17aad10a55abec75ff5696c8d109790396434b496e75087854203',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib-common:1.7.0:kotlin-stdlib-common-1.7.0.jar:59c6ff64fe9a6604afce03e8aaa75f83586c6030ac71fb0b34ee7cdefed3618f',
|
'org.jetbrains.kotlin:kotlin-stdlib-common:1.7.0:kotlin-stdlib-common-1.7.0.jar:59c6ff64fe9a6604afce03e8aaa75f83586c6030ac71fb0b34ee7cdefed3618f',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.0:kotlin-stdlib-jdk7-1.7.0.jar:07e91be9b2ca20672d2bdb7e181b766e73453a2da13492b5ddaee8fa47aea239',
|
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.0:kotlin-stdlib-jdk7-1.7.0.jar:07e91be9b2ca20672d2bdb7e181b766e73453a2da13492b5ddaee8fa47aea239',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.0:kotlin-stdlib-jdk8-1.7.0.jar:cf058e11db1dfc9944680c8c61b95ac689aaaa8a3eb30bced028100f038f030b',
|
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.0:kotlin-stdlib-jdk8-1.7.0.jar:cf058e11db1dfc9944680c8c61b95ac689aaaa8a3eb30bced028100f038f030b',
|
||||||
|
|||||||
@@ -24,10 +24,10 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 16
|
minSdkVersion 21
|
||||||
targetSdkVersion 31
|
targetSdkVersion 33
|
||||||
versionCode 10501
|
versionCode 10504
|
||||||
versionName "1.5.1"
|
versionName "1.5.4"
|
||||||
applicationId "org.briarproject.briar.android"
|
applicationId "org.briarproject.briar.android"
|
||||||
buildConfigField "String", "TorVersion", "\"$tor_version\""
|
buildConfigField "String", "TorVersion", "\"$tor_version\""
|
||||||
|
|
||||||
@@ -62,7 +62,6 @@ android {
|
|||||||
productFlavors {
|
productFlavors {
|
||||||
screenshot {
|
screenshot {
|
||||||
dimension "version"
|
dimension "version"
|
||||||
minSdkVersion 21
|
|
||||||
applicationIdSuffix ".screenshot" // = org.briarproject.briar.android.screenshot.debug
|
applicationIdSuffix ".screenshot" // = org.briarproject.briar.android.screenshot.debug
|
||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt', 'proguard-test.txt'
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt', 'proguard-test.txt'
|
||||||
}
|
}
|
||||||
@@ -90,6 +89,7 @@ android {
|
|||||||
|
|
||||||
lintOptions {
|
lintOptions {
|
||||||
warning 'MissingTranslation'
|
warning 'MissingTranslation'
|
||||||
|
warning 'MissingDefaultResource'
|
||||||
warning 'ImpliedQuantity'
|
warning 'ImpliedQuantity'
|
||||||
warning 'ExtraTranslation'
|
warning 'ExtraTranslation'
|
||||||
// FIXME
|
// FIXME
|
||||||
@@ -108,24 +108,30 @@ dependencies {
|
|||||||
implementation project(':bramble-android')
|
implementation project(':bramble-android')
|
||||||
implementation project(':briar-core')
|
implementation project(':briar-core')
|
||||||
|
|
||||||
implementation 'androidx.fragment:fragment:1.3.4'
|
implementation 'androidx.fragment:fragment:1.5.5'
|
||||||
implementation 'androidx.preference:preference:1.1.1'
|
implementation 'androidx.preference:preference:1.2.0'
|
||||||
implementation 'androidx.exifinterface:exifinterface:1.3.3'
|
implementation 'androidx.exifinterface:exifinterface:1.3.6'
|
||||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
|
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
implementation 'androidx.lifecycle:lifecycle-livedata:2.5.1'
|
||||||
implementation 'com.google.android.material:material:1.3.0'
|
|
||||||
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||||
|
// check https://github.com/material-components/material-components-android/issues/3191
|
||||||
|
// before upgrading material library
|
||||||
|
implementation 'com.google.android.material:material:1.7.0'
|
||||||
implementation 'androidx.recyclerview:recyclerview-selection:1.1.0'
|
implementation 'androidx.recyclerview:recyclerview-selection:1.1.0'
|
||||||
|
|
||||||
|
// force kotlin standard lib to latest version to prevent jetifier issues
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||||
implementation "com.squareup.okhttp3:okhttp:$okhttp_version"
|
implementation "com.squareup.okhttp3:okhttp:$okhttp_version"
|
||||||
implementation "org.jsoup:jsoup:$jsoup_version"
|
implementation "org.jsoup:jsoup:$jsoup_version"
|
||||||
implementation 'info.guardianproject.panic:panic:1.0'
|
implementation 'info.guardianproject.panic:panic:1.0'
|
||||||
implementation 'de.hdodenhof:circleimageview:3.1.0'
|
implementation 'de.hdodenhof:circleimageview:3.1.0'
|
||||||
implementation 'com.google.zxing:core:3.3.3' // newer version need minSdk 24
|
implementation 'com.google.zxing:core:3.3.3' // newer version need minSdk 24
|
||||||
implementation 'uk.co.samuelwall:material-tap-target-prompt:3.3.0'
|
implementation 'uk.co.samuelwall:material-tap-target-prompt:3.3.2'
|
||||||
implementation 'com.vanniktech:emoji-google:0.7.0' // newer versions need minSdk 21
|
implementation 'com.vanniktech:emoji-google:0.9.0' // newer versions are more work to adapt
|
||||||
implementation 'com.github.kobakei:MaterialFabSpeedDial:1.2.1'
|
implementation 'com.github.kobakei:MaterialFabSpeedDial:1.2.1'
|
||||||
implementation 'com.github.chrisbanes:PhotoView:2.3.0'
|
implementation 'com.github.chrisbanes:PhotoView:2.3.0'
|
||||||
def glideVersion = '4.12.0'
|
def glideVersion = '4.14.2'
|
||||||
implementation("com.github.bumptech.glide:glide:$glideVersion") {
|
implementation("com.github.bumptech.glide:glide:$glideVersion") {
|
||||||
exclude group: 'com.android.support'
|
exclude group: 'com.android.support'
|
||||||
exclude module: 'disklrucache' // when there's no disk cache, we can't accidentally use it
|
exclude module: 'disklrucache' // when there's no disk cache, we can't accidentally use it
|
||||||
@@ -142,12 +148,12 @@ dependencies {
|
|||||||
|
|
||||||
def espressoVersion = '3.3.0'
|
def espressoVersion = '3.3.0'
|
||||||
testImplementation 'androidx.test:runner:1.4.0'
|
testImplementation 'androidx.test:runner:1.4.0'
|
||||||
testImplementation 'androidx.test.ext:junit:1.1.3'
|
testImplementation 'androidx.test.ext:junit:1.1.5'
|
||||||
testImplementation 'androidx.fragment:fragment-testing:1.4.0'
|
testImplementation 'androidx.fragment:fragment-testing:1.4.0'
|
||||||
testImplementation "androidx.arch.core:core-testing:2.1.0"
|
testImplementation "androidx.arch.core:core-testing:2.1.0"
|
||||||
testImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
|
testImplementation "androidx.test.espresso:espresso-core:3.5.0"
|
||||||
testImplementation 'org.robolectric:robolectric:4.4'
|
testImplementation 'org.robolectric:robolectric:4.8.2' // newer versions have SecureRandom issue
|
||||||
testImplementation 'org.mockito:mockito-core:3.9.0'
|
testImplementation 'org.mockito:mockito-core:5.1.1'
|
||||||
testImplementation "junit:junit:$junit_version"
|
testImplementation "junit:junit:$junit_version"
|
||||||
testImplementation "org.jmock:jmock:$jmock_version"
|
testImplementation "org.jmock:jmock:$jmock_version"
|
||||||
testImplementation "org.jmock:jmock-junit4:$jmock_version"
|
testImplementation "org.jmock:jmock-junit4:$jmock_version"
|
||||||
|
|||||||
@@ -19,6 +19,10 @@
|
|||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
|
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
|
||||||
|
<uses-permission
|
||||||
|
android:name="android.permission.NEARBY_WIFI_DEVICES"
|
||||||
|
android:usesPermissionFlags="neverForLocation"
|
||||||
|
tools:targetApi="31" />
|
||||||
<uses-permission android:name="android.permission.CAMERA" />
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
@@ -30,8 +34,10 @@
|
|||||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||||
android:maxSdkVersion="18"
|
android:maxSdkVersion="18"
|
||||||
tools:ignore="ScopedStorage" />
|
tools:ignore="ScopedStorage" />
|
||||||
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
|
|
||||||
<uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION" />
|
<uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION"
|
||||||
|
android:maxSdkVersion="32" />
|
||||||
<uses-permission-sdk-23 android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
<uses-permission-sdk-23 android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||||
<uses-permission-sdk-23 android:name="android.permission.USE_BIOMETRIC" />
|
<uses-permission-sdk-23 android:name="android.permission.USE_BIOMETRIC" />
|
||||||
<uses-permission-sdk-23 android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission-sdk-23 android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
@@ -99,11 +105,6 @@
|
|||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:label="@string/app_name" />
|
android:label="@string/app_name" />
|
||||||
|
|
||||||
<activity
|
|
||||||
android:name="org.briarproject.briar.android.splash.ExpiredOldAndroidActivity"
|
|
||||||
android:exported="false"
|
|
||||||
android:label="@string/app_name" />
|
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name="org.briarproject.briar.android.login.StartupActivity"
|
android:name="org.briarproject.briar.android.login.StartupActivity"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import org.briarproject.bramble.api.plugin.PluginManager;
|
|||||||
import org.briarproject.bramble.api.settings.SettingsManager;
|
import org.briarproject.bramble.api.settings.SettingsManager;
|
||||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
|
||||||
import org.briarproject.bramble.mailbox.ModularMailboxModule;
|
import org.briarproject.bramble.mailbox.ModularMailboxModule;
|
||||||
import org.briarproject.bramble.plugin.file.RemovableDriveModule;
|
import org.briarproject.bramble.plugin.file.RemovableDriveModule;
|
||||||
import org.briarproject.bramble.system.ClockModule;
|
import org.briarproject.bramble.system.ClockModule;
|
||||||
@@ -84,6 +83,7 @@ import org.briarproject.briar.api.privategroup.invitation.GroupInvitationFactory
|
|||||||
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager;
|
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager;
|
||||||
import org.briarproject.briar.api.test.TestDataCreator;
|
import org.briarproject.briar.api.test.TestDataCreator;
|
||||||
import org.briarproject.onionwrapper.CircumventionProvider;
|
import org.briarproject.onionwrapper.CircumventionProvider;
|
||||||
|
import org.briarproject.onionwrapper.LocationUtils;
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
|||||||
@@ -294,10 +294,8 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
b.setOngoing(true);
|
b.setOngoing(true);
|
||||||
Intent i = new Intent(appContext, SplashScreenActivity.class);
|
Intent i = new Intent(appContext, SplashScreenActivity.class);
|
||||||
b.setContentIntent(getActivity(appContext, 0, i, getImmutableFlags(0)));
|
b.setContentIntent(getActivity(appContext, 0, i, getImmutableFlags(0)));
|
||||||
if (SDK_INT >= 21) {
|
b.setCategory(CATEGORY_SERVICE);
|
||||||
b.setCategory(CATEGORY_SERVICE);
|
b.setVisibility(VISIBILITY_SECRET);
|
||||||
b.setVisibility(VISIBILITY_SECRET);
|
|
||||||
}
|
|
||||||
b.setPriority(PRIORITY_MIN);
|
b.setPriority(PRIORITY_MIN);
|
||||||
return b.build();
|
return b.build();
|
||||||
}
|
}
|
||||||
@@ -773,8 +771,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
i.setAction(ACTION_STOP_HOTSPOT);
|
i.setAction(ACTION_STOP_HOTSPOT);
|
||||||
PendingIntent actionIntent =
|
PendingIntent actionIntent =
|
||||||
getActivity(appContext, 0, i, getImmutableFlags(0));
|
getActivity(appContext, 0, i, getImmutableFlags(0));
|
||||||
int icon = SDK_INT >= 21 ? R.drawable.ic_portable_wifi_off : 0;
|
b.addAction(R.drawable.ic_portable_wifi_off, actionTitle, actionIntent);
|
||||||
b.addAction(icon, actionTitle, actionIntent);
|
|
||||||
notificationManager.notify(HOTSPOT_NOTIFICATION_ID, b.build());
|
notificationManager.notify(HOTSPOT_NOTIFICATION_ID, b.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -212,7 +212,7 @@ public class AppModule {
|
|||||||
public Collection<SimplexPluginFactory> getSimplexFactories() {
|
public Collection<SimplexPluginFactory> getSimplexFactories() {
|
||||||
List<SimplexPluginFactory> simplex = new ArrayList<>();
|
List<SimplexPluginFactory> simplex = new ArrayList<>();
|
||||||
simplex.add(mailbox);
|
simplex.add(mailbox);
|
||||||
if (SDK_INT >= 19) simplex.add(drive);
|
simplex.add(drive);
|
||||||
return simplex;
|
return simplex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,17 +7,26 @@ import android.content.IntentFilter;
|
|||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.lifecycle.Service;
|
import org.briarproject.bramble.api.lifecycle.Service;
|
||||||
import org.briarproject.bramble.api.lifecycle.ServiceException;
|
|
||||||
import org.briarproject.briar.api.android.DozeWatchdog;
|
import org.briarproject.briar.api.android.DozeWatchdog;
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
|
||||||
import static android.content.Context.POWER_SERVICE;
|
import static android.content.Context.POWER_SERVICE;
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
import static android.os.PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED;
|
import static android.os.PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED;
|
||||||
|
import static android.os.PowerManager.ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED;
|
||||||
|
import static android.os.PowerManager.ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED;
|
||||||
|
import static java.util.logging.Level.WARNING;
|
||||||
|
import static java.util.logging.Logger.getLogger;
|
||||||
|
|
||||||
class DozeWatchdogImpl implements DozeWatchdog, Service {
|
class DozeWatchdogImpl implements DozeWatchdog, Service {
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
getLogger(DozeWatchdogImpl.class.getName());
|
||||||
|
|
||||||
private final Context appContext;
|
private final Context appContext;
|
||||||
private final AtomicBoolean dozed = new AtomicBoolean(false);
|
private final AtomicBoolean dozed = new AtomicBoolean(false);
|
||||||
private final BroadcastReceiver receiver = new DozeBroadcastReceiver();
|
private final BroadcastReceiver receiver = new DozeBroadcastReceiver();
|
||||||
@@ -32,14 +41,18 @@ class DozeWatchdogImpl implements DozeWatchdog, Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startService() throws ServiceException {
|
public void startService() {
|
||||||
if (SDK_INT < 23) return;
|
if (SDK_INT < 23) return;
|
||||||
IntentFilter filter = new IntentFilter(ACTION_DEVICE_IDLE_MODE_CHANGED);
|
IntentFilter filter = new IntentFilter(ACTION_DEVICE_IDLE_MODE_CHANGED);
|
||||||
|
if (SDK_INT >= 33) {
|
||||||
|
filter.addAction(ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED);
|
||||||
|
filter.addAction(ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED);
|
||||||
|
}
|
||||||
appContext.registerReceiver(receiver, filter);
|
appContext.registerReceiver(receiver, filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stopService() throws ServiceException {
|
public void stopService() {
|
||||||
if (SDK_INT < 23) return;
|
if (SDK_INT < 23) return;
|
||||||
appContext.unregisterReceiver(receiver);
|
appContext.unregisterReceiver(receiver);
|
||||||
}
|
}
|
||||||
@@ -49,9 +62,33 @@ class DozeWatchdogImpl implements DozeWatchdog, Service {
|
|||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
if (SDK_INT < 23) return;
|
if (SDK_INT < 23) return;
|
||||||
|
String action = intent.getAction();
|
||||||
PowerManager pm =
|
PowerManager pm =
|
||||||
(PowerManager) appContext.getSystemService(POWER_SERVICE);
|
(PowerManager) appContext.getSystemService(POWER_SERVICE);
|
||||||
if (pm.isDeviceIdleMode()) dozed.set(true);
|
if (ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) {
|
||||||
|
if (pm.isDeviceIdleMode()) dozed.set(true);
|
||||||
|
} else if (SDK_INT >= 33) {
|
||||||
|
onReceive33(action, pm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(33)
|
||||||
|
private void onReceive33(String action, PowerManager pm) {
|
||||||
|
if (ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED.equals(action)) {
|
||||||
|
if (pm.isLowPowerStandbyEnabled()) {
|
||||||
|
if (LOG.isLoggable(WARNING)) {
|
||||||
|
LOG.warning("System is in low power standby mode");
|
||||||
|
}
|
||||||
|
dozed.set(true);
|
||||||
|
}
|
||||||
|
} else if (ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED.equals(action)) {
|
||||||
|
if (pm.isDeviceLightIdleMode()) {
|
||||||
|
if (LOG.isLoggable(WARNING)) {
|
||||||
|
LOG.warning("System is in light idle mode");
|
||||||
|
}
|
||||||
|
dozed.set(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,16 +56,8 @@ public class Localizer {
|
|||||||
// Get Locale from BCP-47 tag
|
// Get Locale from BCP-47 tag
|
||||||
@Nullable
|
@Nullable
|
||||||
public static Locale getLocaleFromTag(String tag) {
|
public static Locale getLocaleFromTag(String tag) {
|
||||||
if (tag.equals("default"))
|
if (tag.equals("default")) return null;
|
||||||
return null;
|
return Locale.forLanguageTag(tag);
|
||||||
if (SDK_INT >= 21) {
|
|
||||||
return Locale.forLanguageTag(tag);
|
|
||||||
}
|
|
||||||
if (tag.contains("-")) {
|
|
||||||
String[] langArray = tag.split("-");
|
|
||||||
return new Locale(langArray[0], langArray[1]);
|
|
||||||
} else
|
|
||||||
return new Locale(tag);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -94,12 +86,8 @@ public class Localizer {
|
|||||||
if (locale.equals(currentLocale))
|
if (locale.equals(currentLocale))
|
||||||
return context;
|
return context;
|
||||||
Locale.setDefault(locale);
|
Locale.setDefault(locale);
|
||||||
if (SDK_INT >= 17) {
|
conf.setLocale(locale);
|
||||||
conf.setLocale(locale);
|
context = context.createConfigurationContext(conf);
|
||||||
context = context.createConfigurationContext(conf);
|
|
||||||
} else
|
|
||||||
conf.locale = locale;
|
|
||||||
//noinspection deprecation
|
|
||||||
res.updateConfiguration(conf, res.getDisplayMetrics());
|
res.updateConfiguration(conf, res.getDisplayMetrics());
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ package org.briarproject.briar.android;
|
|||||||
|
|
||||||
import org.briarproject.briar.BuildConfig;
|
import org.briarproject.briar.BuildConfig;
|
||||||
|
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
|
||||||
import static java.util.concurrent.TimeUnit.DAYS;
|
import static java.util.concurrent.TimeUnit.DAYS;
|
||||||
|
import static org.briarproject.briar.BuildConfig.BuildTimestamp;
|
||||||
|
|
||||||
public interface TestingConstants {
|
public interface TestingConstants {
|
||||||
|
|
||||||
@@ -20,15 +20,9 @@ public interface TestingConstants {
|
|||||||
*/
|
*/
|
||||||
boolean PREVENT_SCREENSHOTS = !IS_DEBUG_BUILD;
|
boolean PREVENT_SCREENSHOTS = !IS_DEBUG_BUILD;
|
||||||
|
|
||||||
boolean IS_OLD_ANDROID = SDK_INT <= 19;
|
|
||||||
long OLD_ANDROID_WARN_DATE = 1659225600_000L; // 2022-07-31
|
|
||||||
long OLD_ANDROID_EXPIRY_DATE = 1675123200_000L; // 2023-01-31
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Debug builds expire after 90 days. Release builds running on Android 4
|
* Debug builds expire after 90 days.
|
||||||
* expire at a set date, otherwise they expire after 292 million years.
|
|
||||||
*/
|
*/
|
||||||
long EXPIRY_DATE = IS_DEBUG_BUILD ?
|
long EXPIRY_DATE = IS_DEBUG_BUILD ?
|
||||||
BuildConfig.BuildTimestamp + DAYS.toMillis(90)
|
BuildTimestamp + DAYS.toMillis(90) : Long.MAX_VALUE;
|
||||||
: (IS_OLD_ANDROID ? OLD_ANDROID_EXPIRY_DATE : Long.MAX_VALUE);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.briar.android.account;
|
package org.briarproject.briar.android.account;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.ActivityNotFoundException;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
@@ -8,6 +9,7 @@ import android.view.View;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.account.PowerView.OnCheckedChangedListener;
|
import org.briarproject.briar.android.account.PowerView.OnCheckedChangedListener;
|
||||||
@@ -18,6 +20,7 @@ import androidx.annotation.Nullable;
|
|||||||
|
|
||||||
import static android.view.View.INVISIBLE;
|
import static android.view.View.INVISIBLE;
|
||||||
import static android.view.View.VISIBLE;
|
import static android.view.View.VISIBLE;
|
||||||
|
import static android.widget.Toast.LENGTH_LONG;
|
||||||
import static org.briarproject.android.dontkillmelib.DozeUtils.getDozeWhitelistingIntent;
|
import static org.briarproject.android.dontkillmelib.DozeUtils.getDozeWhitelistingIntent;
|
||||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_DOZE_WHITELISTING;
|
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_DOZE_WHITELISTING;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.showOnboardingDialog;
|
import static org.briarproject.briar.android.util.UiUtils.showOnboardingDialog;
|
||||||
@@ -113,7 +116,12 @@ public class DozeFragment extends SetupFragment
|
|||||||
private void askForDozeWhitelisting() {
|
private void askForDozeWhitelisting() {
|
||||||
if (getContext() == null) return;
|
if (getContext() == null) return;
|
||||||
Intent i = getDozeWhitelistingIntent(getContext());
|
Intent i = getDozeWhitelistingIntent(getContext());
|
||||||
startActivityForResult(i, REQUEST_DOZE_WHITELISTING);
|
try {
|
||||||
|
startActivityForResult(i, REQUEST_DOZE_WHITELISTING);
|
||||||
|
} catch (ActivityNotFoundException e) {
|
||||||
|
Toast.makeText(requireContext(),
|
||||||
|
R.string.error_start_activity, LENGTH_LONG).show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import androidx.annotation.UiThread;
|
|||||||
|
|
||||||
import static org.briarproject.android.dontkillmelib.HuaweiUtils.getHuaweiProtectedAppsIntent;
|
import static org.briarproject.android.dontkillmelib.HuaweiUtils.getHuaweiProtectedAppsIntent;
|
||||||
import static org.briarproject.android.dontkillmelib.HuaweiUtils.protectedAppsNeedsToBeShown;
|
import static org.briarproject.android.dontkillmelib.HuaweiUtils.protectedAppsNeedsToBeShown;
|
||||||
|
import static org.briarproject.briar.android.util.UiUtils.tryToStartActivity;
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -49,7 +50,7 @@ class HuaweiProtectedAppsView extends PowerView {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onButtonClick() {
|
protected void onButtonClick() {
|
||||||
getContext().startActivity(getHuaweiProtectedAppsIntent());
|
tryToStartActivity(getContext(), getHuaweiProtectedAppsIntent());
|
||||||
setChecked(true);
|
setChecked(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,10 +19,17 @@ import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
|||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission;
|
||||||
|
|
||||||
|
import static android.Manifest.permission.POST_NOTIFICATIONS;
|
||||||
import static android.content.Context.INPUT_METHOD_SERVICE;
|
import static android.content.Context.INPUT_METHOD_SERVICE;
|
||||||
|
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||||
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
import static android.view.View.INVISIBLE;
|
import static android.view.View.INVISIBLE;
|
||||||
import static android.view.View.VISIBLE;
|
import static android.view.View.VISIBLE;
|
||||||
import static android.view.inputmethod.EditorInfo.IME_ACTION_DONE;
|
import static android.view.inputmethod.EditorInfo.IME_ACTION_DONE;
|
||||||
|
import static androidx.core.content.ContextCompat.checkSelfPermission;
|
||||||
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_WEAK;
|
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_WEAK;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.setError;
|
import static org.briarproject.briar.android.util.UiUtils.setError;
|
||||||
|
|
||||||
@@ -38,6 +45,10 @@ public class SetPasswordFragment extends SetupFragment {
|
|||||||
private StrengthMeter strengthMeter;
|
private StrengthMeter strengthMeter;
|
||||||
private Button nextButton;
|
private Button nextButton;
|
||||||
|
|
||||||
|
private final ActivityResultLauncher<String> requestPermissionLauncher =
|
||||||
|
registerForActivityResult(new RequestPermission(), isGranted ->
|
||||||
|
setPassword());
|
||||||
|
|
||||||
public static SetPasswordFragment newInstance() {
|
public static SetPasswordFragment newInstance() {
|
||||||
return new SetPasswordFragment();
|
return new SetPasswordFragment();
|
||||||
}
|
}
|
||||||
@@ -121,6 +132,18 @@ public class SetPasswordFragment extends SetupFragment {
|
|||||||
IBinder token = passwordEntry.getWindowToken();
|
IBinder token = passwordEntry.getWindowToken();
|
||||||
Object o = requireContext().getSystemService(INPUT_METHOD_SERVICE);
|
Object o = requireContext().getSystemService(INPUT_METHOD_SERVICE);
|
||||||
((InputMethodManager) o).hideSoftInputFromWindow(token, 0);
|
((InputMethodManager) o).hideSoftInputFromWindow(token, 0);
|
||||||
|
if (SDK_INT >= 33 &&
|
||||||
|
checkSelfPermission(requireContext(), POST_NOTIFICATIONS) !=
|
||||||
|
PERMISSION_GRANTED) {
|
||||||
|
// this calls setPassword() when it returns
|
||||||
|
requestPermissionLauncher.launch(POST_NOTIFICATIONS);
|
||||||
|
} else {
|
||||||
|
setPassword();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setPassword() {
|
||||||
viewModel.setPassword(passwordEntry.getText().toString());
|
viewModel.setPassword(passwordEntry.getText().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ public class SetupActivity extends BaseActivity
|
|||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ViewModelProvider.Factory viewModelFactory;
|
ViewModelProvider.Factory viewModelFactory;
|
||||||
SetupViewModel viewModel;
|
private SetupViewModel viewModel;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void injectActivity(ActivityComponent component) {
|
public void injectActivity(ActivityComponent component) {
|
||||||
@@ -71,16 +71,16 @@ public class SetupActivity extends BaseActivity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void showPasswordFragment() {
|
private void showPasswordFragment() {
|
||||||
showNextFragment(SetPasswordFragment.newInstance());
|
showNextFragment(SetPasswordFragment.newInstance());
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(23)
|
@TargetApi(23)
|
||||||
void showDozeFragment() {
|
private void showDozeFragment() {
|
||||||
showNextFragment(DozeFragment.newInstance());
|
showNextFragment(DozeFragment.newInstance());
|
||||||
}
|
}
|
||||||
|
|
||||||
void showApp() {
|
private void showApp() {
|
||||||
Intent i = new Intent(this, ENTRY_ACTIVITY);
|
Intent i = new Intent(this, ENTRY_ACTIVITY);
|
||||||
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME |
|
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME |
|
||||||
FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_CLEAR_TOP);
|
FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.briar.android.account;
|
package org.briarproject.briar.android.account;
|
||||||
|
|
||||||
import android.app.KeyguardManager;
|
import android.app.KeyguardManager;
|
||||||
|
import android.content.ActivityNotFoundException;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.hardware.biometrics.BiometricPrompt;
|
import android.hardware.biometrics.BiometricPrompt;
|
||||||
import android.hardware.biometrics.BiometricPrompt.AuthenticationCallback;
|
import android.hardware.biometrics.BiometricPrompt.AuthenticationCallback;
|
||||||
@@ -28,11 +29,11 @@ import static android.hardware.biometrics.BiometricPrompt.BIOMETRIC_ERROR_CANCEL
|
|||||||
import static android.hardware.biometrics.BiometricPrompt.BIOMETRIC_ERROR_USER_CANCELED;
|
import static android.hardware.biometrics.BiometricPrompt.BIOMETRIC_ERROR_USER_CANCELED;
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
import static android.view.View.INVISIBLE;
|
import static android.view.View.INVISIBLE;
|
||||||
|
import static android.widget.Toast.LENGTH_LONG;
|
||||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_KEYGUARD_UNLOCK;
|
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_KEYGUARD_UNLOCK;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.hasKeyguardLock;
|
import static org.briarproject.briar.android.util.UiUtils.hasKeyguardLock;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.hasUsableFingerprint;
|
import static org.briarproject.briar.android.util.UiUtils.hasUsableFingerprint;
|
||||||
|
|
||||||
@RequiresApi(21)
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
public class UnlockActivity extends BaseActivity {
|
public class UnlockActivity extends BaseActivity {
|
||||||
@@ -192,7 +193,12 @@ public class UnlockActivity extends BaseActivity {
|
|||||||
unlock();
|
unlock();
|
||||||
} else {
|
} else {
|
||||||
keyguardShown = true;
|
keyguardShown = true;
|
||||||
startActivityForResult(intent, REQUEST_KEYGUARD_UNLOCK);
|
try {
|
||||||
|
startActivityForResult(intent, REQUEST_KEYGUARD_UNLOCK);
|
||||||
|
} catch (ActivityNotFoundException e) {
|
||||||
|
Toast.makeText(this, R.string.error_start_activity, LENGTH_LONG)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
overridePendingTransition(0, 0);
|
overridePendingTransition(0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.briar.android.account;
|
package org.briarproject.briar.android.account;
|
||||||
|
|
||||||
|
import android.content.ActivityNotFoundException;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
@@ -60,12 +61,12 @@ class XiaomiLockAppsView extends PowerView {
|
|||||||
getContext().startActivity(getXiaomiLockAppsIntent());
|
getContext().startActivity(getXiaomiLockAppsIntent());
|
||||||
setChecked(true);
|
setChecked(true);
|
||||||
return;
|
return;
|
||||||
} catch (SecurityException e) {
|
} catch (SecurityException | ActivityNotFoundException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
|
Toast.makeText(getContext(),
|
||||||
|
R.string.dnkm_xiaomi_lock_apps_error_toast,
|
||||||
|
LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
Toast.makeText(getContext(),
|
|
||||||
R.string.dnkm_xiaomi_lock_apps_error_toast,
|
|
||||||
LENGTH_LONG).show();
|
|
||||||
// Let the user continue with setup
|
// Let the user continue with setup
|
||||||
setChecked(true);
|
setChecked(true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,7 +81,6 @@ import org.briarproject.briar.android.sharing.ShareBlogFragment;
|
|||||||
import org.briarproject.briar.android.sharing.ShareForumActivity;
|
import org.briarproject.briar.android.sharing.ShareForumActivity;
|
||||||
import org.briarproject.briar.android.sharing.ShareForumFragment;
|
import org.briarproject.briar.android.sharing.ShareForumFragment;
|
||||||
import org.briarproject.briar.android.sharing.SharingModule;
|
import org.briarproject.briar.android.sharing.SharingModule;
|
||||||
import org.briarproject.briar.android.splash.ExpiredOldAndroidActivity;
|
|
||||||
import org.briarproject.briar.android.splash.SplashScreenActivity;
|
import org.briarproject.briar.android.splash.SplashScreenActivity;
|
||||||
import org.briarproject.briar.android.test.TestDataActivity;
|
import org.briarproject.briar.android.test.TestDataActivity;
|
||||||
|
|
||||||
@@ -184,8 +183,6 @@ public interface ActivityComponent {
|
|||||||
|
|
||||||
void inject(RemovableDriveActivity activity);
|
void inject(RemovableDriveActivity activity);
|
||||||
|
|
||||||
void inject(ExpiredOldAndroidActivity activity);
|
|
||||||
|
|
||||||
// Fragments
|
// Fragments
|
||||||
|
|
||||||
void inject(SetupFragment fragment);
|
void inject(SetupFragment fragment);
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package org.briarproject.briar.android.activity;
|
package org.briarproject.briar.android.activity;
|
||||||
|
|
||||||
|
import android.content.ActivityNotFoundException;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.transition.Transition;
|
import android.transition.Transition;
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
import android.widget.CheckBox;
|
import android.widget.CheckBox;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManager;
|
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManager;
|
||||||
import org.briarproject.bramble.api.system.Wakeful;
|
import org.briarproject.bramble.api.system.Wakeful;
|
||||||
@@ -24,7 +26,6 @@ import java.util.logging.Logger;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
import androidx.appcompat.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
@@ -35,9 +36,12 @@ import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
|
|||||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||||
import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
|
import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
|
import static android.widget.Toast.LENGTH_LONG;
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
|
import static java.util.logging.Level.WARNING;
|
||||||
import static java.util.logging.Logger.getLogger;
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static org.briarproject.android.dontkillmelib.DozeUtils.getDozeWhitelistingIntent;
|
import static org.briarproject.android.dontkillmelib.DozeUtils.getDozeWhitelistingIntent;
|
||||||
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_DOZE_WHITELISTING;
|
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_DOZE_WHITELISTING;
|
||||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PASSWORD;
|
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PASSWORD;
|
||||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_UNLOCK;
|
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_UNLOCK;
|
||||||
@@ -105,7 +109,7 @@ public abstract class BriarActivity extends BaseActivity {
|
|||||||
LOG.info("Not signed in, launching StartupActivity");
|
LOG.info("Not signed in, launching StartupActivity");
|
||||||
Intent i = new Intent(this, StartupActivity.class);
|
Intent i = new Intent(this, StartupActivity.class);
|
||||||
startActivityForResult(i, REQUEST_PASSWORD);
|
startActivityForResult(i, REQUEST_PASSWORD);
|
||||||
} else if (SDK_INT >= 21 && lockManager.isLocked() && !isFinishing()) {
|
} else if (lockManager.isLocked() && !isFinishing()) {
|
||||||
// Also check that the activity isn't finishing already.
|
// Also check that the activity isn't finishing already.
|
||||||
// This is possible if we finished in onActivityResult().
|
// This is possible if we finished in onActivityResult().
|
||||||
// Launching another UnlockActivity would cause a loop.
|
// Launching another UnlockActivity would cause a loop.
|
||||||
@@ -135,8 +139,7 @@ public abstract class BriarActivity extends BaseActivity {
|
|||||||
* @param exitTransition used to move views out when starting a <b>new</b> activity.
|
* @param exitTransition used to move views out when starting a <b>new</b> activity.
|
||||||
* @param returnTransition used when window is closing, because the activity is finishing.
|
* @param returnTransition used when window is closing, because the activity is finishing.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(api = 21)
|
protected void setSceneTransitionAnimation(
|
||||||
public void setSceneTransitionAnimation(
|
|
||||||
@Nullable Transition enterTransition,
|
@Nullable Transition enterTransition,
|
||||||
@Nullable Transition exitTransition,
|
@Nullable Transition exitTransition,
|
||||||
@Nullable Transition returnTransition) {
|
@Nullable Transition returnTransition) {
|
||||||
@@ -181,7 +184,13 @@ public abstract class BriarActivity extends BaseActivity {
|
|||||||
b.setPositiveButton(R.string.fix,
|
b.setPositiveButton(R.string.fix,
|
||||||
(dialog, which) -> {
|
(dialog, which) -> {
|
||||||
Intent i = getDozeWhitelistingIntent(BriarActivity.this);
|
Intent i = getDozeWhitelistingIntent(BriarActivity.this);
|
||||||
startActivityForResult(i, REQUEST_DOZE_WHITELISTING);
|
try {
|
||||||
|
startActivityForResult(i, REQUEST_DOZE_WHITELISTING);
|
||||||
|
} catch (ActivityNotFoundException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
Toast.makeText(this, R.string.error_start_activity,
|
||||||
|
LENGTH_LONG).show();
|
||||||
|
}
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
});
|
});
|
||||||
b.setNegativeButton(R.string.cancel,
|
b.setNegativeButton(R.string.cancel,
|
||||||
@@ -232,8 +241,7 @@ public abstract class BriarActivity extends BaseActivity {
|
|||||||
|
|
||||||
@Wakeful
|
@Wakeful
|
||||||
private void finishAndExit() {
|
private void finishAndExit() {
|
||||||
if (SDK_INT >= 21) finishAndRemoveTask();
|
finishAndRemoveTask();
|
||||||
else supportFinishAfterTransition();
|
|
||||||
LOG.info("Exiting");
|
LOG.info("Exiting");
|
||||||
BriarApplication app = (BriarApplication) getApplication();
|
BriarApplication app = (BriarApplication) getApplication();
|
||||||
if (!app.isInstrumentationTest()) System.exit(0);
|
if (!app.isInstrumentationTest()) System.exit(0);
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import androidx.annotation.UiThread;
|
|||||||
import androidx.core.view.ViewCompat;
|
import androidx.core.view.ViewCompat;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
|
||||||
import static android.view.View.GONE;
|
import static android.view.View.GONE;
|
||||||
import static android.view.View.VISIBLE;
|
import static android.view.View.VISIBLE;
|
||||||
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
|
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
|
||||||
@@ -134,12 +133,6 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
|
|||||||
} else {
|
} else {
|
||||||
reblogger.setVisibility(GONE);
|
reblogger.setVisibility(GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply Android 4 padding fix after setting up author/reblogger views
|
|
||||||
if (SDK_INT < 21) {
|
|
||||||
reblogger.setPadding(padding, padding, padding, padding);
|
|
||||||
author.setPadding(padding, padding, padding, padding);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onBindComment(BlogCommentItem item, boolean authorClickable) {
|
private void onBindComment(BlogCommentItem item, boolean authorClickable) {
|
||||||
|
|||||||
@@ -27,11 +27,9 @@ import javax.annotation.Nullable;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import androidx.activity.result.ActivityResultLauncher;
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
|
||||||
import static android.view.View.GONE;
|
import static android.view.View.GONE;
|
||||||
import static android.view.View.VISIBLE;
|
import static android.view.View.VISIBLE;
|
||||||
import static android.view.inputmethod.EditorInfo.IME_ACTION_DONE;
|
import static android.view.inputmethod.EditorInfo.IME_ACTION_DONE;
|
||||||
@@ -52,7 +50,6 @@ public class RssFeedImportFragment extends BaseFragment {
|
|||||||
private Button importButton;
|
private Button importButton;
|
||||||
private ProgressBar progressBar;
|
private ProgressBar progressBar;
|
||||||
|
|
||||||
@RequiresApi(19)
|
|
||||||
private final ActivityResultLauncher<String[]> docLauncher =
|
private final ActivityResultLauncher<String[]> docLauncher =
|
||||||
registerForActivityResult(new OpenDocumentAdvanced(),
|
registerForActivityResult(new OpenDocumentAdvanced(),
|
||||||
this::onFileChosen);
|
this::onFileChosen);
|
||||||
@@ -74,7 +71,7 @@ public class RssFeedImportFragment extends BaseFragment {
|
|||||||
@Nullable ViewGroup container,
|
@Nullable ViewGroup container,
|
||||||
@Nullable Bundle savedInstanceState) {
|
@Nullable Bundle savedInstanceState) {
|
||||||
requireActivity().setTitle(getString(R.string.blogs_rss_feeds_import));
|
requireActivity().setTitle(getString(R.string.blogs_rss_feeds_import));
|
||||||
if (SDK_INT >= 19) setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
View v = inflater.inflate(R.layout.fragment_rss_feed_import,
|
View v = inflater.inflate(R.layout.fragment_rss_feed_import,
|
||||||
container, false);
|
container, false);
|
||||||
|
|
||||||
@@ -117,15 +114,13 @@ public class RssFeedImportFragment extends BaseFragment {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
if (SDK_INT >= 19) {
|
inflater.inflate(R.menu.rss_feed_import_actions, menu);
|
||||||
inflater.inflate(R.menu.rss_feed_import_actions, menu);
|
|
||||||
}
|
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
if (item.getItemId() == R.id.action_import_file && SDK_INT >= 19) {
|
if (item.getItemId() == R.id.action_import_file) {
|
||||||
launchActivityToOpenFile(requireContext(), docLauncher,
|
launchActivityToOpenFile(requireContext(), docLauncher,
|
||||||
contentLauncher, "*/*");
|
contentLauncher, "*/*");
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -55,7 +55,6 @@ import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener
|
|||||||
import org.briarproject.briar.android.introduction.IntroductionActivity;
|
import org.briarproject.briar.android.introduction.IntroductionActivity;
|
||||||
import org.briarproject.briar.android.privategroup.conversation.GroupActivity;
|
import org.briarproject.briar.android.privategroup.conversation.GroupActivity;
|
||||||
import org.briarproject.briar.android.removabledrive.RemovableDriveActivity;
|
import org.briarproject.briar.android.removabledrive.RemovableDriveActivity;
|
||||||
import org.briarproject.briar.android.util.ActivityLaunchers.GetImageAdvanced;
|
|
||||||
import org.briarproject.briar.android.util.ActivityLaunchers.GetMultipleImagesAdvanced;
|
import org.briarproject.briar.android.util.ActivityLaunchers.GetMultipleImagesAdvanced;
|
||||||
import org.briarproject.briar.android.util.ActivityLaunchers.OpenMultipleImageDocumentsAdvanced;
|
import org.briarproject.briar.android.util.ActivityLaunchers.OpenMultipleImageDocumentsAdvanced;
|
||||||
import org.briarproject.briar.android.util.BriarSnackbarBuilder;
|
import org.briarproject.briar.android.util.BriarSnackbarBuilder;
|
||||||
@@ -122,13 +121,11 @@ import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
|
|||||||
import de.hdodenhof.circleimageview.CircleImageView;
|
import de.hdodenhof.circleimageview.CircleImageView;
|
||||||
import uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt;
|
import uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt;
|
||||||
|
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
|
||||||
import static android.view.Gravity.RIGHT;
|
import static android.view.Gravity.RIGHT;
|
||||||
import static android.widget.Toast.LENGTH_SHORT;
|
import static android.widget.Toast.LENGTH_SHORT;
|
||||||
import static androidx.core.app.ActivityOptionsCompat.makeSceneTransitionAnimation;
|
import static androidx.core.app.ActivityOptionsCompat.makeSceneTransitionAnimation;
|
||||||
import static androidx.lifecycle.Lifecycle.State.STARTED;
|
import static androidx.lifecycle.Lifecycle.State.STARTED;
|
||||||
import static androidx.recyclerview.widget.SortedList.INVALID_POSITION;
|
import static androidx.recyclerview.widget.SortedList.INVALID_POSITION;
|
||||||
import static java.util.Collections.singletonList;
|
|
||||||
import static java.util.Collections.sort;
|
import static java.util.Collections.sort;
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
@@ -200,18 +197,13 @@ public class ConversationActivity extends BriarActivity
|
|||||||
requireNonNull(name);
|
requireNonNull(name);
|
||||||
loadMessages();
|
loadMessages();
|
||||||
};
|
};
|
||||||
@Nullable
|
|
||||||
private final ActivityResultLauncher<String[]> docLauncher = SDK_INT >= 19 ?
|
private final ActivityResultLauncher<String[]> docLauncher =
|
||||||
registerForActivityResult(new OpenMultipleImageDocumentsAdvanced(),
|
registerForActivityResult(new OpenMultipleImageDocumentsAdvanced(),
|
||||||
this::onImagesChosen) :
|
this::onImagesChosen);
|
||||||
null;
|
|
||||||
private final ActivityResultLauncher<String> contentLauncher =
|
private final ActivityResultLauncher<String> contentLauncher =
|
||||||
SDK_INT >= 18 ?
|
registerForActivityResult(new GetMultipleImagesAdvanced(),
|
||||||
registerForActivityResult(new GetMultipleImagesAdvanced(),
|
this::onImagesChosen);
|
||||||
this::onImagesChosen) :
|
|
||||||
registerForActivityResult(new GetImageAdvanced(), uri -> {
|
|
||||||
if (uri != null) onImagesChosen(singletonList(uri));
|
|
||||||
});
|
|
||||||
|
|
||||||
private AttachmentRetriever attachmentRetriever;
|
private AttachmentRetriever attachmentRetriever;
|
||||||
private ConversationViewModel viewModel;
|
private ConversationViewModel viewModel;
|
||||||
@@ -242,13 +234,11 @@ public class ConversationActivity extends BriarActivity
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(@Nullable Bundle state) {
|
public void onCreate(@Nullable Bundle state) {
|
||||||
if (SDK_INT >= 21) {
|
// Spurious lint warning - using END causes a crash
|
||||||
// Spurious lint warning - using END causes a crash
|
@SuppressLint("RtlHardcoded")
|
||||||
@SuppressLint("RtlHardcoded")
|
Transition slide = new Slide(RIGHT);
|
||||||
Transition slide = new Slide(RIGHT);
|
slide.setDuration(TRANSITION_DURATION_MS);
|
||||||
slide.setDuration(TRANSITION_DURATION_MS);
|
setSceneTransitionAnimation(slide, null, slide);
|
||||||
setSceneTransitionAnimation(slide, null, slide);
|
|
||||||
}
|
|
||||||
super.onCreate(state);
|
super.onCreate(state);
|
||||||
|
|
||||||
Intent i = getIntent();
|
Intent i = getIntent();
|
||||||
@@ -389,10 +379,6 @@ public class ConversationActivity extends BriarActivity
|
|||||||
this::showIntroductionOnboarding);
|
this::showIntroductionOnboarding);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// Transfer Data feature only supported on API 19+
|
|
||||||
if (SDK_INT >= 19) {
|
|
||||||
menu.findItem(R.id.action_transfer_data).setVisible(true);
|
|
||||||
}
|
|
||||||
// enable alias and bluetooth action once available
|
// enable alias and bluetooth action once available
|
||||||
observeOnce(viewModel.getContactItem(), this, contact -> {
|
observeOnce(viewModel.getContactItem(), this, contact -> {
|
||||||
menu.findItem(R.id.action_set_alias).setEnabled(true);
|
menu.findItem(R.id.action_set_alias).setEnabled(true);
|
||||||
@@ -434,11 +420,9 @@ public class ConversationActivity extends BriarActivity
|
|||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
return true;
|
return true;
|
||||||
} else if (itemId == R.id.action_transfer_data) {
|
} else if (itemId == R.id.action_transfer_data) {
|
||||||
if (SDK_INT >= 19) {
|
Intent intent = new Intent(this, RemovableDriveActivity.class);
|
||||||
Intent intent = new Intent(this, RemovableDriveActivity.class);
|
intent.putExtra(CONTACT_ID, contactId.getInt());
|
||||||
intent.putExtra(CONTACT_ID, contactId.getInt());
|
startActivity(intent);
|
||||||
startActivity(intent);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
} else if (itemId == R.id.action_delete_all_messages) {
|
} else if (itemId == R.id.action_delete_all_messages) {
|
||||||
askToDeleteAllMessages();
|
askToDeleteAllMessages();
|
||||||
@@ -955,14 +939,10 @@ public class ConversationActivity extends BriarActivity
|
|||||||
|
|
||||||
private void showImageOnboarding(Boolean show) {
|
private void showImageOnboarding(Boolean show) {
|
||||||
if (!show) return;
|
if (!show) return;
|
||||||
if (SDK_INT >= 21) {
|
// show onboarding only after the enter transition has ended
|
||||||
// show onboarding only after the enter transition has ended
|
// otherwise the tap target animation won't play
|
||||||
// otherwise the tap target animation won't play
|
textInputView.postDelayed(this::showImageOnboarding,
|
||||||
textInputView.postDelayed(this::showImageOnboarding,
|
TRANSITION_DURATION_MS + ONBOARDING_DELAY_MS);
|
||||||
TRANSITION_DURATION_MS + ONBOARDING_DELAY_MS);
|
|
||||||
} else {
|
|
||||||
showImageOnboarding();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showImageOnboarding() {
|
private void showImageOnboarding() {
|
||||||
@@ -973,14 +953,10 @@ public class ConversationActivity extends BriarActivity
|
|||||||
|
|
||||||
private void showIntroductionOnboarding(@Nullable Boolean show) {
|
private void showIntroductionOnboarding(@Nullable Boolean show) {
|
||||||
if (show == null || !show) return;
|
if (show == null || !show) return;
|
||||||
if (SDK_INT >= 21) {
|
// show onboarding only after the enter transition has ended
|
||||||
// show onboarding only after the enter transition has ended
|
// otherwise the tap target animation won't play
|
||||||
// otherwise the tap target animation won't play
|
textInputView.postDelayed(this::showIntroductionOnboarding,
|
||||||
textInputView.postDelayed(this::showIntroductionOnboarding,
|
TRANSITION_DURATION_MS + ONBOARDING_DELAY_MS);
|
||||||
TRANSITION_DURATION_MS + ONBOARDING_DELAY_MS);
|
|
||||||
} else {
|
|
||||||
showIntroductionOnboarding();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showIntroductionOnboarding() {
|
private void showIntroductionOnboarding() {
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import org.briarproject.briar.android.util.BriarSnackbarBuilder;
|
|||||||
import org.briarproject.briar.android.view.PullDownLayout;
|
import org.briarproject.briar.android.view.PullDownLayout;
|
||||||
import org.briarproject.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -41,13 +42,11 @@ import androidx.viewpager2.adapter.FragmentStateAdapter;
|
|||||||
import androidx.viewpager2.widget.ViewPager2;
|
import androidx.viewpager2.widget.ViewPager2;
|
||||||
|
|
||||||
import static android.graphics.Color.TRANSPARENT;
|
import static android.graphics.Color.TRANSPARENT;
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
|
||||||
import static android.view.View.GONE;
|
import static android.view.View.GONE;
|
||||||
import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
|
import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
|
||||||
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
|
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
|
||||||
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
|
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
|
||||||
import static android.view.View.VISIBLE;
|
import static android.view.View.VISIBLE;
|
||||||
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
|
|
||||||
import static com.google.android.material.snackbar.Snackbar.LENGTH_LONG;
|
import static com.google.android.material.snackbar.Snackbar.LENGTH_LONG;
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.formatDateAbsolute;
|
import static org.briarproject.briar.android.util.UiUtils.formatDateAbsolute;
|
||||||
@@ -77,11 +76,9 @@ public class ImageActivity extends BriarActivity
|
|||||||
private List<AttachmentItem> attachments;
|
private List<AttachmentItem> attachments;
|
||||||
private MessageId conversationMessageId;
|
private MessageId conversationMessageId;
|
||||||
|
|
||||||
@Nullable
|
private final ActivityResultLauncher<String> launcher =
|
||||||
private final ActivityResultLauncher<String> launcher = SDK_INT >= 19 ?
|
|
||||||
registerForActivityResult(new CreateDocumentAdvanced(),
|
registerForActivityResult(new CreateDocumentAdvanced(),
|
||||||
this::onImageUriSelected) :
|
this::onImageUriSelected);
|
||||||
null;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void injectActivity(ActivityComponent component) {
|
public void injectActivity(ActivityComponent component) {
|
||||||
@@ -97,10 +94,8 @@ public class ImageActivity extends BriarActivity
|
|||||||
// Transitions
|
// Transitions
|
||||||
if (state == null) supportPostponeEnterTransition();
|
if (state == null) supportPostponeEnterTransition();
|
||||||
Window window = getWindow();
|
Window window = getWindow();
|
||||||
if (SDK_INT >= 21) {
|
Transition transition = new Fade();
|
||||||
Transition transition = new Fade();
|
setSceneTransitionAnimation(transition, null, transition);
|
||||||
setSceneTransitionAnimation(transition, null, transition);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Intent Extras
|
// Intent Extras
|
||||||
Intent i = getIntent();
|
Intent i = getIntent();
|
||||||
@@ -124,12 +119,7 @@ public class ImageActivity extends BriarActivity
|
|||||||
layout.getViewTreeObserver().addOnGlobalLayoutListener(this);
|
layout.getViewTreeObserver().addOnGlobalLayoutListener(this);
|
||||||
|
|
||||||
// Status Bar
|
// Status Bar
|
||||||
if (SDK_INT >= 21) {
|
window.setStatusBarColor(TRANSPARENT);
|
||||||
window.setStatusBarColor(TRANSPARENT);
|
|
||||||
} else if (SDK_INT >= 19) {
|
|
||||||
// we can't make the status bar transparent, but translucent
|
|
||||||
window.addFlags(FLAG_TRANSLUCENT_STATUS);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Toolbar
|
// Toolbar
|
||||||
appBarLayout = findViewById(R.id.appBarLayout);
|
appBarLayout = findViewById(R.id.appBarLayout);
|
||||||
@@ -257,16 +247,12 @@ public class ImageActivity extends BriarActivity
|
|||||||
|
|
||||||
private void showSaveImageDialog() {
|
private void showSaveImageDialog() {
|
||||||
OnClickListener okListener = (dialog, which) -> {
|
OnClickListener okListener = (dialog, which) -> {
|
||||||
if (SDK_INT >= 19) {
|
String name = viewModel.getFileName() + "." +
|
||||||
String name = viewModel.getFileName() + "." +
|
getVisibleAttachment().getExtension();
|
||||||
getVisibleAttachment().getExtension();
|
try {
|
||||||
try {
|
launcher.launch(name);
|
||||||
requireNonNull(launcher).launch(name);
|
} catch (ActivityNotFoundException e) {
|
||||||
} catch (ActivityNotFoundException e) {
|
viewModel.onSaveImageError();
|
||||||
viewModel.onSaveImageError();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
viewModel.saveImage(getVisibleAttachment());
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Builder builder = new Builder(this, R.style.BriarDialogTheme);
|
Builder builder = new Builder(this, R.style.BriarDialogTheme);
|
||||||
@@ -295,7 +281,7 @@ public class ImageActivity extends BriarActivity
|
|||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
AttachmentItem getVisibleAttachment() {
|
private AttachmentItem getVisibleAttachment() {
|
||||||
return attachments.get(viewPager.getCurrentItem());
|
return attachments.get(viewPager.getCurrentItem());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,6 +293,7 @@ public class ImageActivity extends BriarActivity
|
|||||||
super(ImageActivity.this);
|
super(ImageActivity.this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public Fragment createFragment(int position) {
|
public Fragment createFragment(int position) {
|
||||||
Fragment f = ImageFragment.newInstance(
|
Fragment f = ImageFragment.newInstance(
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ import androidx.fragment.app.Fragment;
|
|||||||
import androidx.lifecycle.LifecycleOwner;
|
import androidx.lifecycle.LifecycleOwner;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
|
||||||
import static android.widget.ImageView.ScaleType.FIT_START;
|
import static android.widget.ImageView.ScaleType.FIT_START;
|
||||||
import static com.bumptech.glide.load.engine.DiskCacheStrategy.NONE;
|
import static com.bumptech.glide.load.engine.DiskCacheStrategy.NONE;
|
||||||
import static org.briarproject.briar.android.attachment.AttachmentItem.State.AVAILABLE;
|
import static org.briarproject.briar.android.attachment.AttachmentItem.State.AVAILABLE;
|
||||||
@@ -150,7 +149,7 @@ public class ImageFragment extends Fragment
|
|||||||
public boolean onResourceReady(Drawable resource, Object model,
|
public boolean onResourceReady(Drawable resource, Object model,
|
||||||
Target<Drawable> target, DataSource dataSource,
|
Target<Drawable> target, DataSource dataSource,
|
||||||
boolean isFirstResource) {
|
boolean isFirstResource) {
|
||||||
if (SDK_INT >= 21 && !(resource instanceof Animatable)) {
|
if (!(resource instanceof Animatable)) {
|
||||||
// set transition name only when not animatable,
|
// set transition name only when not animatable,
|
||||||
// because the animation won't start otherwise
|
// because the animation won't start otherwise
|
||||||
photoView.setTransitionName(
|
photoView.setTransitionName(
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import androidx.annotation.DrawableRes;
|
|||||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
|
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
|
||||||
import androidx.recyclerview.widget.StaggeredGridLayoutManager.LayoutParams;
|
import androidx.recyclerview.widget.StaggeredGridLayoutManager.LayoutParams;
|
||||||
|
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
|
||||||
import static android.widget.ImageView.ScaleType.CENTER_CROP;
|
import static android.widget.ImageView.ScaleType.CENTER_CROP;
|
||||||
import static android.widget.ImageView.ScaleType.FIT_CENTER;
|
import static android.widget.ImageView.ScaleType.FIT_CENTER;
|
||||||
import static com.bumptech.glide.load.engine.DiskCacheStrategy.NONE;
|
import static com.bumptech.glide.load.engine.DiskCacheStrategy.NONE;
|
||||||
@@ -58,10 +57,8 @@ class ImageViewHolder extends ViewHolder {
|
|||||||
loadImage(attachment, r);
|
loadImage(attachment, r);
|
||||||
imageView.setScaleType(CENTER_CROP);
|
imageView.setScaleType(CENTER_CROP);
|
||||||
}
|
}
|
||||||
if (SDK_INT >= 21) {
|
imageView.setTransitionName(
|
||||||
imageView.setTransitionName(
|
attachment.getTransitionName(conversationItemId));
|
||||||
attachment.getTransitionName(conversationItemId));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setImageViewDimensions(AttachmentItem a, boolean single,
|
private void setImageViewDimensions(AttachmentItem a, boolean single,
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
package org.briarproject.briar.android.forum;
|
package org.briarproject.briar.android.forum;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
|
||||||
import org.briarproject.briar.api.identity.AuthorInfo;
|
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
|
||||||
import org.briarproject.briar.android.threaded.ThreadItem;
|
import org.briarproject.briar.android.threaded.ThreadItem;
|
||||||
import org.briarproject.briar.api.forum.ForumPostHeader;
|
import org.briarproject.briar.api.forum.ForumPostHeader;
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import org.briarproject.briar.api.android.ScreenFilterMonitor;
|
|||||||
import org.briarproject.briar.api.android.ScreenFilterMonitor.AppDetails;
|
import org.briarproject.briar.api.android.ScreenFilterMonitor.AppDetails;
|
||||||
import org.briarproject.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -32,6 +33,7 @@ import androidx.fragment.app.DialogFragment;
|
|||||||
import static android.os.Build.VERSION.SDK_INT;
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
import static android.provider.Settings.ACTION_MANAGE_OVERLAY_PERMISSION;
|
import static android.provider.Settings.ACTION_MANAGE_OVERLAY_PERMISSION;
|
||||||
import static android.view.View.GONE;
|
import static android.view.View.GONE;
|
||||||
|
import static org.briarproject.briar.android.util.UiUtils.tryToStartActivity;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
@@ -68,6 +70,7 @@ public class ScreenFilterDialogFragment extends DialogFragment {
|
|||||||
((BaseActivity) requireActivity()).getActivityComponent().inject(this);
|
((BaseActivity) requireActivity()).getActivityComponent().inject(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
||||||
Activity activity = getActivity();
|
Activity activity = getActivity();
|
||||||
@@ -98,7 +101,7 @@ public class ScreenFilterDialogFragment extends DialogFragment {
|
|||||||
builder.setNeutralButton(R.string.screen_filter_review_apps,
|
builder.setNeutralButton(R.string.screen_filter_review_apps,
|
||||||
(dialog, which) -> {
|
(dialog, which) -> {
|
||||||
Intent i = new Intent(ACTION_MANAGE_OVERLAY_PERMISSION);
|
Intent i = new Intent(ACTION_MANAGE_OVERLAY_PERMISSION);
|
||||||
startActivity(i);
|
tryToStartActivity(requireActivity(), i);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
builder.setPositiveButton(R.string.continue_button, (dialog, which) -> {
|
builder.setPositiveButton(R.string.continue_button, (dialog, which) -> {
|
||||||
|
|||||||
@@ -1,21 +1,27 @@
|
|||||||
package org.briarproject.briar.android.hotspot;
|
package org.briarproject.briar.android.hotspot;
|
||||||
|
|
||||||
|
import android.content.ActivityNotFoundException;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.net.wifi.WifiManager;
|
import android.net.wifi.WifiManager;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
|
|
||||||
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts;
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.core.util.Consumer;
|
import androidx.core.util.Consumer;
|
||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
|
||||||
import static android.content.Context.WIFI_SERVICE;
|
import static android.content.Context.WIFI_SERVICE;
|
||||||
|
import static android.widget.Toast.LENGTH_LONG;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract base class for the ConditionManagers that ensure that the conditions
|
* Abstract base class for the ConditionManagers that ensure that the conditions
|
||||||
* to open a hotspot are fulfilled. There are different extensions of this for
|
* to open a hotspot are fulfilled. There are different extensions of this for
|
||||||
* API levels lower than 29 and 29+.
|
* API levels lower than 29, 29+ and 33+.
|
||||||
*/
|
*/
|
||||||
abstract class AbstractConditionManager {
|
abstract class AbstractConditionManager {
|
||||||
|
|
||||||
@@ -28,6 +34,7 @@ abstract class AbstractConditionManager {
|
|||||||
final Consumer<Boolean> permissionUpdateCallback;
|
final Consumer<Boolean> permissionUpdateCallback;
|
||||||
protected FragmentActivity ctx;
|
protected FragmentActivity ctx;
|
||||||
WifiManager wifiManager;
|
WifiManager wifiManager;
|
||||||
|
private ActivityResultLauncher<Intent> wifiRequest;
|
||||||
|
|
||||||
AbstractConditionManager(Consumer<Boolean> permissionUpdateCallback) {
|
AbstractConditionManager(Consumer<Boolean> permissionUpdateCallback) {
|
||||||
this.permissionUpdateCallback = permissionUpdateCallback;
|
this.permissionUpdateCallback = permissionUpdateCallback;
|
||||||
@@ -38,8 +45,12 @@ abstract class AbstractConditionManager {
|
|||||||
*/
|
*/
|
||||||
void init(FragmentActivity ctx) {
|
void init(FragmentActivity ctx) {
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
this.wifiManager = (WifiManager) ctx.getApplicationContext()
|
wifiManager = (WifiManager) ctx.getApplicationContext()
|
||||||
.getSystemService(WIFI_SERVICE);
|
.getSystemService(WIFI_SERVICE);
|
||||||
|
wifiRequest = ctx.registerForActivityResult(
|
||||||
|
new ActivityResultContracts.StartActivityForResult(),
|
||||||
|
result -> permissionUpdateCallback
|
||||||
|
.accept(wifiManager.isWifiEnabled()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -57,6 +68,8 @@ abstract class AbstractConditionManager {
|
|||||||
*/
|
*/
|
||||||
abstract boolean checkAndRequestConditions();
|
abstract boolean checkAndRequestConditions();
|
||||||
|
|
||||||
|
abstract String getWifiSettingsAction();
|
||||||
|
|
||||||
void showRationale(Context ctx, @StringRes int title,
|
void showRationale(Context ctx, @StringRes int title,
|
||||||
@StringRes int body, Runnable onContinueClicked,
|
@StringRes int body, Runnable onContinueClicked,
|
||||||
Runnable onDismiss) {
|
Runnable onDismiss) {
|
||||||
@@ -69,4 +82,13 @@ abstract class AbstractConditionManager {
|
|||||||
builder.show();
|
builder.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void requestEnableWiFi() {
|
||||||
|
try {
|
||||||
|
wifiRequest.launch(new Intent(getWifiSettingsAction()));
|
||||||
|
} catch (ActivityNotFoundException e) {
|
||||||
|
Toast.makeText(ctx, R.string.error_start_activity, LENGTH_LONG)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
package org.briarproject.briar.android.hotspot;
|
package org.briarproject.briar.android.hotspot;
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
|
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import androidx.activity.result.ActivityResultCaller;
|
|
||||||
import androidx.activity.result.ActivityResultLauncher;
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult;
|
|
||||||
import androidx.core.util.Consumer;
|
import androidx.core.util.Consumer;
|
||||||
|
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
@@ -22,20 +19,14 @@ import static java.util.logging.Logger.getLogger;
|
|||||||
* As soon as {@link #checkAndRequestConditions()} returns true,
|
* As soon as {@link #checkAndRequestConditions()} returns true,
|
||||||
* all conditions are fulfilled.
|
* all conditions are fulfilled.
|
||||||
*/
|
*/
|
||||||
|
@NotNullByDefault
|
||||||
class ConditionManager extends AbstractConditionManager {
|
class ConditionManager extends AbstractConditionManager {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
getLogger(ConditionManager.class.getName());
|
getLogger(ConditionManager.class.getName());
|
||||||
|
|
||||||
private final ActivityResultLauncher<Intent> wifiRequest;
|
ConditionManager(Consumer<Boolean> permissionUpdateCallback) {
|
||||||
|
super( permissionUpdateCallback);
|
||||||
ConditionManager(ActivityResultCaller arc,
|
|
||||||
Consumer<Boolean> permissionUpdateCallback) {
|
|
||||||
super(permissionUpdateCallback);
|
|
||||||
wifiRequest = arc.registerForActivityResult(
|
|
||||||
new StartActivityForResult(),
|
|
||||||
result -> permissionUpdateCallback
|
|
||||||
.accept(wifiManager.isWifiEnabled()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -76,8 +67,9 @@ class ConditionManager extends AbstractConditionManager {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void requestEnableWiFi() {
|
@Override
|
||||||
wifiRequest.launch(new Intent(Settings.ACTION_WIFI_SETTINGS));
|
String getWifiSettingsAction() {
|
||||||
|
return Settings.ACTION_WIFI_SETTINGS;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
package org.briarproject.briar.android.hotspot;
|
package org.briarproject.briar.android.hotspot;
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
|
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.util.Permission;
|
import org.briarproject.briar.android.util.Permission;
|
||||||
import org.briarproject.briar.android.util.PermissionUtils;
|
import org.briarproject.briar.android.util.PermissionUtils;
|
||||||
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import androidx.activity.result.ActivityResultCaller;
|
import androidx.activity.result.ActivityResultCaller;
|
||||||
import androidx.activity.result.ActivityResultLauncher;
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission;
|
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission;
|
||||||
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult;
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
import androidx.annotation.RequiresApi;
|
||||||
import androidx.core.util.Consumer;
|
import androidx.core.util.Consumer;
|
||||||
@@ -28,12 +27,13 @@ import static org.briarproject.briar.android.util.PermissionUtils.showLocationDi
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This class ensures that the conditions to open a hotspot are fulfilled on
|
* This class ensures that the conditions to open a hotspot are fulfilled on
|
||||||
* API levels >= 29.
|
* API levels >= 29 and < 33.
|
||||||
* <p>
|
* <p>
|
||||||
* As soon as {@link #checkAndRequestConditions()} returns true,
|
* As soon as {@link #checkAndRequestConditions()} returns true,
|
||||||
* all conditions are fulfilled.
|
* all conditions are fulfilled.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(29)
|
@RequiresApi(29)
|
||||||
|
@NotNullByDefault
|
||||||
class ConditionManager29 extends AbstractConditionManager {
|
class ConditionManager29 extends AbstractConditionManager {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
@@ -42,7 +42,6 @@ class ConditionManager29 extends AbstractConditionManager {
|
|||||||
private Permission locationPermission = Permission.UNKNOWN;
|
private Permission locationPermission = Permission.UNKNOWN;
|
||||||
|
|
||||||
private final ActivityResultLauncher<String> locationRequest;
|
private final ActivityResultLauncher<String> locationRequest;
|
||||||
private final ActivityResultLauncher<Intent> wifiRequest;
|
|
||||||
|
|
||||||
ConditionManager29(ActivityResultCaller arc,
|
ConditionManager29(ActivityResultCaller arc,
|
||||||
Consumer<Boolean> permissionUpdateCallback) {
|
Consumer<Boolean> permissionUpdateCallback) {
|
||||||
@@ -53,11 +52,6 @@ class ConditionManager29 extends AbstractConditionManager {
|
|||||||
onRequestPermissionResult(granted);
|
onRequestPermissionResult(granted);
|
||||||
permissionUpdateCallback.accept(TRUE.equals(granted));
|
permissionUpdateCallback.accept(TRUE.equals(granted));
|
||||||
});
|
});
|
||||||
wifiRequest = arc.registerForActivityResult(
|
|
||||||
new StartActivityForResult(),
|
|
||||||
result -> permissionUpdateCallback
|
|
||||||
.accept(wifiManager.isWifiEnabled())
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -131,6 +125,11 @@ class ConditionManager29 extends AbstractConditionManager {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String getWifiSettingsAction() {
|
||||||
|
return Settings.Panel.ACTION_WIFI;
|
||||||
|
}
|
||||||
|
|
||||||
private void onRequestPermissionResult(@Nullable Boolean granted) {
|
private void onRequestPermissionResult(@Nullable Boolean granted) {
|
||||||
if (granted != null && granted) {
|
if (granted != null && granted) {
|
||||||
locationPermission = Permission.GRANTED;
|
locationPermission = Permission.GRANTED;
|
||||||
@@ -146,8 +145,4 @@ class ConditionManager29 extends AbstractConditionManager {
|
|||||||
locationRequest.launch(ACCESS_FINE_LOCATION);
|
locationRequest.launch(ACCESS_FINE_LOCATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void requestEnableWiFi() {
|
|
||||||
wifiRequest.launch(new Intent(Settings.Panel.ACTION_WIFI));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,134 @@
|
|||||||
|
package org.briarproject.briar.android.hotspot;
|
||||||
|
|
||||||
|
import android.provider.Settings;
|
||||||
|
|
||||||
|
import org.briarproject.briar.R;
|
||||||
|
import org.briarproject.briar.android.util.Permission;
|
||||||
|
import org.briarproject.briar.android.util.PermissionUtils;
|
||||||
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import androidx.activity.result.ActivityResultCaller;
|
||||||
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
import androidx.core.util.Consumer;
|
||||||
|
|
||||||
|
import static android.Manifest.permission.NEARBY_WIFI_DEVICES;
|
||||||
|
import static androidx.core.app.ActivityCompat.shouldShowRequestPermissionRationale;
|
||||||
|
import static java.lang.Boolean.TRUE;
|
||||||
|
import static java.util.logging.Level.INFO;
|
||||||
|
import static java.util.logging.Logger.getLogger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class ensures that the conditions to open a hotspot are fulfilled on
|
||||||
|
* API levels >= 33.
|
||||||
|
* <p>
|
||||||
|
* As soon as {@link #checkAndRequestConditions()} returns true,
|
||||||
|
* all conditions are fulfilled.
|
||||||
|
*/
|
||||||
|
@RequiresApi(33)
|
||||||
|
@NotNullByDefault
|
||||||
|
class ConditionManager33 extends AbstractConditionManager {
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
getLogger(ConditionManager33.class.getName());
|
||||||
|
|
||||||
|
private Permission nearbyWifiPermission = Permission.UNKNOWN;
|
||||||
|
|
||||||
|
private final ActivityResultLauncher<String> nearbyWifiRequest;
|
||||||
|
|
||||||
|
ConditionManager33(ActivityResultCaller arc,
|
||||||
|
Consumer<Boolean> permissionUpdateCallback) {
|
||||||
|
super(permissionUpdateCallback);
|
||||||
|
// permissionUpdateCallback receives false if permissions were denied
|
||||||
|
nearbyWifiRequest = arc.registerForActivityResult(
|
||||||
|
new RequestPermission(), granted -> {
|
||||||
|
onRequestPermissionResult(granted);
|
||||||
|
permissionUpdateCallback.accept(TRUE.equals(granted));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void onStart() {
|
||||||
|
nearbyWifiPermission = Permission.UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean areEssentialPermissionsGranted() {
|
||||||
|
boolean isWifiEnabled = wifiManager.isWifiEnabled();
|
||||||
|
if (LOG.isLoggable(INFO)) {
|
||||||
|
LOG.info(String.format("areEssentialPermissionsGranted(): " +
|
||||||
|
"nearbyWifiPermission? %s, " +
|
||||||
|
"wifiManager.isWifiEnabled()? %b",
|
||||||
|
nearbyWifiPermission, isWifiEnabled));
|
||||||
|
}
|
||||||
|
return nearbyWifiPermission == Permission.GRANTED && isWifiEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean checkAndRequestConditions() {
|
||||||
|
if (areEssentialPermissionsGranted()) return true;
|
||||||
|
|
||||||
|
if (nearbyWifiPermission == Permission.UNKNOWN) {
|
||||||
|
requestPermissions();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the location permission has been permanently denied, ask the
|
||||||
|
// user to change the setting
|
||||||
|
if (nearbyWifiPermission == Permission.PERMANENTLY_DENIED) {
|
||||||
|
PermissionUtils.showDenialDialog(ctx,
|
||||||
|
R.string.permission_nearby_devices_title,
|
||||||
|
R.string.permission_hotspot_nearby_wifi_denied_body,
|
||||||
|
() -> permissionUpdateCallback.accept(false));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should we show the rationale for location permission?
|
||||||
|
if (nearbyWifiPermission == Permission.SHOW_RATIONALE) {
|
||||||
|
showRationale(ctx,
|
||||||
|
R.string.permission_location_title,
|
||||||
|
R.string.permission_hotspot_nearby_wifi_request_body,
|
||||||
|
this::requestPermissions,
|
||||||
|
() -> permissionUpdateCallback.accept(false));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If Wifi is not enabled, we show the rationale for enabling Wifi?
|
||||||
|
if (!wifiManager.isWifiEnabled()) {
|
||||||
|
showRationale(ctx, R.string.wifi_settings_title,
|
||||||
|
R.string.wifi_settings_request_enable_body,
|
||||||
|
this::requestEnableWiFi,
|
||||||
|
() -> permissionUpdateCallback.accept(false));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we shouldn't usually reach this point, but if we do, return false
|
||||||
|
// anyway to force a recheck. Maybe some condition changed in the
|
||||||
|
// meantime.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String getWifiSettingsAction() {
|
||||||
|
return Settings.Panel.ACTION_WIFI;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onRequestPermissionResult(@Nullable Boolean granted) {
|
||||||
|
if (granted != null && granted) {
|
||||||
|
nearbyWifiPermission = Permission.GRANTED;
|
||||||
|
} else if (shouldShowRequestPermissionRationale(ctx,
|
||||||
|
NEARBY_WIFI_DEVICES)) {
|
||||||
|
nearbyWifiPermission = Permission.SHOW_RATIONALE;
|
||||||
|
} else {
|
||||||
|
nearbyWifiPermission = Permission.PERMANENTLY_DENIED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void requestPermissions() {
|
||||||
|
nearbyWifiRequest.launch(NEARBY_WIFI_DEVICES);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2,7 +2,6 @@ package org.briarproject.briar.android.hotspot;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.ResolveInfo;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
@@ -17,8 +16,6 @@ import org.briarproject.briar.android.util.ActivityLaunchers.CreateDocumentAdvan
|
|||||||
import org.briarproject.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import androidx.activity.result.ActivityResultLauncher;
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
@@ -29,14 +26,12 @@ import androidx.lifecycle.ViewModelProvider;
|
|||||||
import static android.content.Intent.ACTION_SEND;
|
import static android.content.Intent.ACTION_SEND;
|
||||||
import static android.content.Intent.EXTRA_STREAM;
|
import static android.content.Intent.EXTRA_STREAM;
|
||||||
import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
|
import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
|
||||||
import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
|
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
|
||||||
import static android.view.View.INVISIBLE;
|
import static android.view.View.INVISIBLE;
|
||||||
import static android.view.View.VISIBLE;
|
import static android.view.View.VISIBLE;
|
||||||
import static androidx.transition.TransitionManager.beginDelayedTransition;
|
import static androidx.transition.TransitionManager.beginDelayedTransition;
|
||||||
import static org.briarproject.briar.android.AppModule.getAndroidComponent;
|
import static org.briarproject.briar.android.AppModule.getAndroidComponent;
|
||||||
import static org.briarproject.briar.android.hotspot.HotspotViewModel.getApkFileName;
|
import static org.briarproject.briar.android.hotspot.HotspotViewModel.getApkFileName;
|
||||||
import static org.briarproject.nullsafety.NullSafety.requireNonNull;
|
import static org.briarproject.briar.android.util.UiUtils.tryToStartActivity;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
@@ -48,11 +43,9 @@ public class FallbackFragment extends BaseFragment {
|
|||||||
ViewModelProvider.Factory viewModelFactory;
|
ViewModelProvider.Factory viewModelFactory;
|
||||||
|
|
||||||
private HotspotViewModel viewModel;
|
private HotspotViewModel viewModel;
|
||||||
@Nullable
|
private final ActivityResultLauncher<String> launcher =
|
||||||
private final ActivityResultLauncher<String> launcher = SDK_INT >= 19 ?
|
|
||||||
registerForActivityResult(new CreateDocumentAdvanced(),
|
registerForActivityResult(new CreateDocumentAdvanced(),
|
||||||
this::onDocumentCreated) :
|
this::onDocumentCreated);
|
||||||
null;
|
|
||||||
private Button fallbackButton;
|
private Button fallbackButton;
|
||||||
private ProgressBar progressBar;
|
private ProgressBar progressBar;
|
||||||
|
|
||||||
@@ -89,12 +82,7 @@ public class FallbackFragment extends BaseFragment {
|
|||||||
beginDelayedTransition((ViewGroup) v);
|
beginDelayedTransition((ViewGroup) v);
|
||||||
fallbackButton.setVisibility(INVISIBLE);
|
fallbackButton.setVisibility(INVISIBLE);
|
||||||
progressBar.setVisibility(VISIBLE);
|
progressBar.setVisibility(VISIBLE);
|
||||||
|
launcher.launch(getApkFileName());
|
||||||
if (SDK_INT >= 19) {
|
|
||||||
requireNonNull(launcher).launch(getApkFileName());
|
|
||||||
} else {
|
|
||||||
viewModel.exportApk();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
viewModel.getSavedApkToUri().observeEvent(this, this::shareUri);
|
viewModel.getSavedApkToUri().observeEvent(this, this::shareUri);
|
||||||
}
|
}
|
||||||
@@ -110,24 +98,12 @@ public class FallbackFragment extends BaseFragment {
|
|||||||
progressBar.setVisibility(INVISIBLE);
|
progressBar.setVisibility(INVISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void shareUri(Uri uri) {
|
private void shareUri(Uri uri) {
|
||||||
Intent i = new Intent(ACTION_SEND);
|
Intent i = new Intent(ACTION_SEND);
|
||||||
i.putExtra(EXTRA_STREAM, uri);
|
i.putExtra(EXTRA_STREAM, uri);
|
||||||
i.setType("*/*"); // gives us all sharing options
|
i.setType("*/*"); // gives us all sharing options
|
||||||
i.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
|
i.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
Context ctx = requireContext();
|
tryToStartActivity(requireActivity(), Intent.createChooser(i, null));
|
||||||
if (SDK_INT <= 19) {
|
|
||||||
// Workaround for Android bug:
|
|
||||||
// ctx.grantUriPermission also needed for Android 4
|
|
||||||
List<ResolveInfo> resInfoList = ctx.getPackageManager()
|
|
||||||
.queryIntentActivities(i, MATCH_DEFAULT_ONLY);
|
|
||||||
for (ResolveInfo resolveInfo : resInfoList) {
|
|
||||||
String packageName = resolveInfo.activityInfo.packageName;
|
|
||||||
ctx.grantUriPermission(packageName, uri,
|
|
||||||
FLAG_GRANT_READ_URI_PERMISSION);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
startActivity(Intent.createChooser(i, null));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,8 +49,10 @@ public class HotspotIntroFragment extends Fragment {
|
|||||||
private TextView progressTextView;
|
private TextView progressTextView;
|
||||||
|
|
||||||
private final AbstractConditionManager conditionManager = SDK_INT < 29 ?
|
private final AbstractConditionManager conditionManager = SDK_INT < 29 ?
|
||||||
new ConditionManager(this, this::onPermissionUpdate) :
|
new ConditionManager(this::onPermissionUpdate) :
|
||||||
new ConditionManager29(this, this::onPermissionUpdate);
|
SDK_INT >= 33 ?
|
||||||
|
new ConditionManager33(this, this::onPermissionUpdate) :
|
||||||
|
new ConditionManager29(this, this::onPermissionUpdate);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Context context) {
|
public void onAttach(Context context) {
|
||||||
@@ -87,7 +89,6 @@ public class HotspotIntroFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onButtonClick(View view) {
|
private void onButtonClick(View view) {
|
||||||
startButton.setEnabled(false);
|
|
||||||
startHotspotIfConditionsFulfilled();
|
startHotspotIfConditionsFulfilled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import org.briarproject.nullsafety.NotNullByDefault;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
@@ -38,9 +37,6 @@ import androidx.annotation.UiThread;
|
|||||||
import androidx.lifecycle.LiveData;
|
import androidx.lifecycle.LiveData;
|
||||||
import androidx.lifecycle.MutableLiveData;
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
|
||||||
import static android.os.Environment.DIRECTORY_DOWNLOADS;
|
|
||||||
import static android.os.Environment.getExternalStoragePublicDirectory;
|
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static java.util.logging.Logger.getLogger;
|
import static java.util.logging.Logger.getLogger;
|
||||||
@@ -168,7 +164,6 @@ class HotspotViewModel extends DbViewModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
void exportApk(Uri uri) {
|
void exportApk(Uri uri) {
|
||||||
if (SDK_INT < 19) throw new IllegalStateException();
|
|
||||||
try {
|
try {
|
||||||
OutputStream out = getApplication().getContentResolver()
|
OutputStream out = getApplication().getContentResolver()
|
||||||
.openOutputStream(uri, "wt");
|
.openOutputStream(uri, "wt");
|
||||||
@@ -178,20 +173,6 @@ class HotspotViewModel extends DbViewModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void exportApk() {
|
|
||||||
if (SDK_INT >= 19) throw new IllegalStateException();
|
|
||||||
File path = getExternalStoragePublicDirectory(DIRECTORY_DOWNLOADS);
|
|
||||||
//noinspection ResultOfMethodCallIgnored
|
|
||||||
path.mkdirs();
|
|
||||||
File file = new File(path, getApkFileName());
|
|
||||||
try {
|
|
||||||
OutputStream out = new FileOutputStream(file);
|
|
||||||
writeApk(out, Uri.fromFile(file));
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
handleException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static String getApkFileName() {
|
static String getApkFileName() {
|
||||||
return "briar" + (DEBUG ? "-debug-" : "-") + VERSION_NAME + ".apk";
|
return "briar" + (DEBUG ? "-debug-" : "-") + VERSION_NAME + ".apk";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,13 +22,19 @@ import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.lifecycle.LifecycleOwner;
|
import androidx.lifecycle.LifecycleOwner;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
|
||||||
|
import static android.Manifest.permission.POST_NOTIFICATIONS;
|
||||||
|
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||||
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
import static android.view.View.INVISIBLE;
|
import static android.view.View.INVISIBLE;
|
||||||
import static android.view.View.VISIBLE;
|
import static android.view.View.VISIBLE;
|
||||||
import static android.view.inputmethod.EditorInfo.IME_ACTION_DONE;
|
import static android.view.inputmethod.EditorInfo.IME_ACTION_DONE;
|
||||||
|
import static androidx.core.content.ContextCompat.checkSelfPermission;
|
||||||
import static org.briarproject.bramble.api.crypto.DecryptionResult.KEY_STRENGTHENER_ERROR;
|
import static org.briarproject.bramble.api.crypto.DecryptionResult.KEY_STRENGTHENER_ERROR;
|
||||||
import static org.briarproject.bramble.api.crypto.DecryptionResult.SUCCESS;
|
import static org.briarproject.bramble.api.crypto.DecryptionResult.SUCCESS;
|
||||||
import static org.briarproject.briar.android.login.LoginUtils.createKeyStrengthenerErrorDialog;
|
import static org.briarproject.briar.android.login.LoginUtils.createKeyStrengthenerErrorDialog;
|
||||||
@@ -52,6 +58,10 @@ public class PasswordFragment extends BaseFragment implements TextWatcher {
|
|||||||
private TextInputLayout input;
|
private TextInputLayout input;
|
||||||
private TextInputEditText password;
|
private TextInputEditText password;
|
||||||
|
|
||||||
|
private final ActivityResultLauncher<String> requestPermissionLauncher =
|
||||||
|
registerForActivityResult(new RequestPermission(), isGranted ->
|
||||||
|
validatePassword());
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void injectFragment(ActivityComponent component) {
|
public void injectFragment(ActivityComponent component) {
|
||||||
component.inject(this);
|
component.inject(this);
|
||||||
@@ -109,6 +119,17 @@ public class PasswordFragment extends BaseFragment implements TextWatcher {
|
|||||||
hideSoftKeyboard(password);
|
hideSoftKeyboard(password);
|
||||||
signInButton.setVisibility(INVISIBLE);
|
signInButton.setVisibility(INVISIBLE);
|
||||||
progress.setVisibility(VISIBLE);
|
progress.setVisibility(VISIBLE);
|
||||||
|
if (SDK_INT >= 33 &&
|
||||||
|
checkSelfPermission(requireContext(), POST_NOTIFICATIONS) !=
|
||||||
|
PERMISSION_GRANTED) {
|
||||||
|
// this calls validatePassword() when it returns
|
||||||
|
requestPermissionLauncher.launch(POST_NOTIFICATIONS);
|
||||||
|
} else {
|
||||||
|
validatePassword();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validatePassword() {
|
||||||
viewModel.validatePassword(password.getText().toString());
|
viewModel.validatePassword(password.getText().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ import android.os.Bundle;
|
|||||||
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
|
||||||
|
|
||||||
public class ExitActivity extends Activity {
|
public class ExitActivity extends Activity {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
@@ -15,8 +13,7 @@ public class ExitActivity extends Activity {
|
|||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle state) {
|
public void onCreate(Bundle state) {
|
||||||
super.onCreate(state);
|
super.onCreate(state);
|
||||||
if (SDK_INT >= 21) finishAndRemoveTask();
|
finishAndRemoveTask();
|
||||||
else finish();
|
|
||||||
LOG.info("Exiting");
|
LOG.info("Exiting");
|
||||||
System.exit(0);
|
System.exit(0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,15 +75,12 @@ import static org.briarproject.bramble.api.plugin.Plugin.State.ENABLING;
|
|||||||
import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING;
|
import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING;
|
||||||
import static org.briarproject.briar.android.BriarService.EXTRA_STARTUP_FAILED;
|
import static org.briarproject.briar.android.BriarService.EXTRA_STARTUP_FAILED;
|
||||||
import static org.briarproject.briar.android.BriarService.EXTRA_START_RESULT;
|
import static org.briarproject.briar.android.BriarService.EXTRA_START_RESULT;
|
||||||
import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE;
|
|
||||||
import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
|
import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
|
||||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PASSWORD;
|
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PASSWORD;
|
||||||
import static org.briarproject.briar.android.navdrawer.IntentRouter.handleExternalIntent;
|
import static org.briarproject.briar.android.navdrawer.IntentRouter.handleExternalIntent;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.formatDateFull;
|
|
||||||
import static org.briarproject.briar.android.util.UiUtils.getDaysUntilExpiry;
|
import static org.briarproject.briar.android.util.UiUtils.getDaysUntilExpiry;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.observeOnce;
|
import static org.briarproject.briar.android.util.UiUtils.observeOnce;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.resolveColorAttribute;
|
import static org.briarproject.briar.android.util.UiUtils.resolveColorAttribute;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.shouldWarnOldAndroidExpiry;
|
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
@@ -140,11 +137,9 @@ public class NavDrawerActivity extends BriarActivity implements
|
|||||||
setContentView(R.layout.activity_nav_drawer);
|
setContentView(R.layout.activity_nav_drawer);
|
||||||
|
|
||||||
BriarApplication app = (BriarApplication) getApplication();
|
BriarApplication app = (BriarApplication) getApplication();
|
||||||
if (!app.isInstrumentationTest()) {
|
if (IS_DEBUG_BUILD && !app.isInstrumentationTest()) {
|
||||||
if (IS_DEBUG_BUILD || shouldWarnOldAndroidExpiry()) {
|
navDrawerViewModel.showExpiryWarning()
|
||||||
navDrawerViewModel.showExpiryWarning()
|
.observe(this, this::showExpiryWarning);
|
||||||
.observe(this, this::showExpiryWarning);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
navDrawerViewModel.shouldAskForDozeWhitelisting().observe(this, ask -> {
|
navDrawerViewModel.shouldAskForDozeWhitelisting().observe(this, ask -> {
|
||||||
if (ask) showDozeDialog(R.string.dnkm_doze_intro);
|
if (ask) showDozeDialog(R.string.dnkm_doze_intro);
|
||||||
@@ -212,7 +207,7 @@ public class NavDrawerActivity extends BriarActivity implements
|
|||||||
public void onStart() {
|
public void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
lockManager.checkIfLockable();
|
lockManager.checkIfLockable();
|
||||||
if (IS_DEBUG_BUILD || shouldWarnOldAndroidExpiry()) {
|
if (IS_DEBUG_BUILD) {
|
||||||
navDrawerViewModel.checkExpiryWarning();
|
navDrawerViewModel.checkExpiryWarning();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -384,23 +379,14 @@ public class NavDrawerActivity extends BriarActivity implements
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String text;
|
|
||||||
if (IS_DEBUG_BUILD) {
|
|
||||||
text = getResources().getQuantityString(
|
|
||||||
R.plurals.expiry_warning, (int) daysUntilExpiry,
|
|
||||||
(int) daysUntilExpiry);
|
|
||||||
} else {
|
|
||||||
text = getResources().getQuantityString(
|
|
||||||
R.plurals.old_android_expiry_warning, (int) daysUntilExpiry,
|
|
||||||
formatDateFull(this, EXPIRY_DATE),
|
|
||||||
(int) daysUntilExpiry);
|
|
||||||
}
|
|
||||||
|
|
||||||
ViewGroup expiryWarning = findViewById(R.id.expiryWarning);
|
ViewGroup expiryWarning = findViewById(R.id.expiryWarning);
|
||||||
if (show) {
|
if (show) {
|
||||||
// show expiry warning text
|
// show expiry warning text
|
||||||
TextView expiryWarningText =
|
TextView expiryWarningText =
|
||||||
expiryWarning.findViewById(R.id.expiryWarningText);
|
expiryWarning.findViewById(R.id.expiryWarningText);
|
||||||
|
String text = getResources().getQuantityString(
|
||||||
|
R.plurals.expiry_warning, (int) daysUntilExpiry,
|
||||||
|
(int) daysUntilExpiry);
|
||||||
expiryWarningText.setText(text);
|
expiryWarningText.setText(text);
|
||||||
// make close button functional
|
// make close button functional
|
||||||
ImageView expiryWarningClose =
|
ImageView expiryWarningClose =
|
||||||
@@ -432,7 +418,7 @@ public class NavDrawerActivity extends BriarActivity implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View getView(int position, View convertView,
|
public View getView(int position, @Nullable View convertView,
|
||||||
ViewGroup parent) {
|
ViewGroup parent) {
|
||||||
View view;
|
View view;
|
||||||
if (convertView != null) {
|
if (convertView != null) {
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import info.guardianproject.panic.Panic;
|
|||||||
import info.guardianproject.panic.PanicResponder;
|
import info.guardianproject.panic.PanicResponder;
|
||||||
import info.guardianproject.trustedintents.TrustedIntents;
|
import info.guardianproject.trustedintents.TrustedIntents;
|
||||||
|
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
|
||||||
import static java.util.logging.Logger.getLogger;
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static org.briarproject.briar.android.panic.PanicPreferencesFragment.KEY_LOCK;
|
import static org.briarproject.briar.android.panic.PanicPreferencesFragment.KEY_LOCK;
|
||||||
import static org.briarproject.briar.android.panic.PanicPreferencesFragment.KEY_PURGE;
|
import static org.briarproject.briar.android.panic.PanicPreferencesFragment.KEY_PURGE;
|
||||||
@@ -73,12 +72,7 @@ public class PanicResponderActivity extends BriarActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finishAndRemoveTask();
|
||||||
if (SDK_INT >= 21) {
|
|
||||||
finishAndRemoveTask();
|
|
||||||
} else {
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import javax.inject.Inject;
|
|||||||
|
|
||||||
import androidx.activity.result.ActivityResultLauncher;
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
@@ -31,7 +30,6 @@ import static org.briarproject.briar.android.AppModule.getAndroidComponent;
|
|||||||
import static org.briarproject.briar.android.util.UiUtils.hideViewOnSmallScreen;
|
import static org.briarproject.briar.android.util.UiUtils.hideViewOnSmallScreen;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.launchActivityToOpenFile;
|
import static org.briarproject.briar.android.util.UiUtils.launchActivityToOpenFile;
|
||||||
|
|
||||||
@RequiresApi(19)
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
public class ReceiveFragment extends Fragment {
|
public class ReceiveFragment extends Fragment {
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import javax.inject.Inject;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
@@ -29,7 +28,6 @@ import static java.util.Objects.requireNonNull;
|
|||||||
import static org.briarproject.briar.android.conversation.ConversationActivity.CONTACT_ID;
|
import static org.briarproject.briar.android.conversation.ConversationActivity.CONTACT_ID;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.showFragment;
|
import static org.briarproject.briar.android.util.UiUtils.showFragment;
|
||||||
|
|
||||||
@RequiresApi(19)
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
public class RemovableDriveActivity extends BriarActivity {
|
public class RemovableDriveActivity extends BriarActivity {
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import javax.inject.Inject;
|
|||||||
|
|
||||||
import androidx.activity.result.ActivityResultLauncher;
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
@@ -38,7 +37,6 @@ import static org.briarproject.bramble.util.LogUtils.logException;
|
|||||||
import static org.briarproject.briar.android.AppModule.getAndroidComponent;
|
import static org.briarproject.briar.android.AppModule.getAndroidComponent;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.hideViewOnSmallScreen;
|
import static org.briarproject.briar.android.util.UiUtils.hideViewOnSmallScreen;
|
||||||
|
|
||||||
@RequiresApi(19)
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
public class SendFragment extends Fragment {
|
public class SendFragment extends Fragment {
|
||||||
|
|||||||
@@ -220,10 +220,10 @@ class BriarReportCollector {
|
|||||||
method.setAccessible(true);
|
method.setAccessible(true);
|
||||||
mobileEnabled = (Boolean) requireNonNull(method.invoke(cm));
|
mobileEnabled = (Boolean) requireNonNull(method.invoke(cm));
|
||||||
} catch (ClassNotFoundException
|
} catch (ClassNotFoundException
|
||||||
| NoSuchMethodException
|
| NoSuchMethodException
|
||||||
| IllegalArgumentException
|
| IllegalArgumentException
|
||||||
| InvocationTargetException
|
| InvocationTargetException
|
||||||
| IllegalAccessException e) {
|
| IllegalAccessException e) {
|
||||||
connectivityInfo
|
connectivityInfo
|
||||||
.add("MobileDataReflectionException", e.toString());
|
.add("MobileDataReflectionException", e.toString());
|
||||||
}
|
}
|
||||||
@@ -300,15 +300,12 @@ class BriarReportCollector {
|
|||||||
scanMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE;
|
scanMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE;
|
||||||
connectivityInfo.add("BluetoothDiscoverable", btDiscoverable);
|
connectivityInfo.add("BluetoothDiscoverable", btDiscoverable);
|
||||||
|
|
||||||
if (SDK_INT >= 21) {
|
// Is Bluetooth LE scanning and advertising supported?
|
||||||
// Is Bluetooth LE scanning and advertising supported?
|
boolean btLeScan = bt.getBluetoothLeScanner() != null;
|
||||||
boolean btLeScan = bt.getBluetoothLeScanner() != null;
|
connectivityInfo.add("BluetoothLeScanningSupported", btLeScan);
|
||||||
connectivityInfo.add("BluetoothLeScanningSupported", btLeScan);
|
boolean btLeAdvertise = bt.getBluetoothLeAdvertiser() != null;
|
||||||
boolean btLeAdvertise =
|
connectivityInfo.add("BluetoothLeAdvertisingSupported",
|
||||||
bt.getBluetoothLeAdvertiser() != null;
|
btLeAdvertise);
|
||||||
connectivityInfo.add("BluetoothLeAdvertisingSupported",
|
|
||||||
btLeAdvertise);
|
|
||||||
}
|
|
||||||
|
|
||||||
Pair<String, String> p = getBluetoothAddressAndMethod(ctx, bt);
|
Pair<String, String> p = getBluetoothAddressAndMethod(ctx, bt);
|
||||||
String address = p.getFirst();
|
String address = p.getFirst();
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.briarproject.briar.android.reporting;
|
package org.briarproject.briar.android.reporting;
|
||||||
|
|
||||||
import android.content.ActivityNotFoundException;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@@ -33,13 +32,11 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||||||
import static android.view.View.GONE;
|
import static android.view.View.GONE;
|
||||||
import static android.view.View.INVISIBLE;
|
import static android.view.View.INVISIBLE;
|
||||||
import static android.view.View.VISIBLE;
|
import static android.view.View.VISIBLE;
|
||||||
import static android.widget.Toast.LENGTH_LONG;
|
|
||||||
import static android.widget.Toast.LENGTH_SHORT;
|
import static android.widget.Toast.LENGTH_SHORT;
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static java.util.logging.Logger.getLogger;
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
|
||||||
import static org.briarproject.briar.android.util.UiUtils.onSingleLinkClick;
|
import static org.briarproject.briar.android.util.UiUtils.onSingleLinkClick;
|
||||||
|
import static org.briarproject.briar.android.util.UiUtils.tryToStartActivity;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
@@ -180,13 +177,7 @@ public class ReportFormFragment extends BaseFragment {
|
|||||||
private void triggerPrivacyPolicy() {
|
private void triggerPrivacyPolicy() {
|
||||||
Intent i = new Intent(Intent.ACTION_VIEW);
|
Intent i = new Intent(Intent.ACTION_VIEW);
|
||||||
i.setData(Uri.parse("https://briarproject.org/privacy-policy/\\"));
|
i.setData(Uri.parse("https://briarproject.org/privacy-policy/\\"));
|
||||||
try {
|
tryToStartActivity(requireActivity(), i);
|
||||||
startActivity(i);
|
|
||||||
} catch (ActivityNotFoundException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
Toast.makeText(requireContext(),
|
|
||||||
R.string.error_start_activity, LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.briarproject.briar.android.settings;
|
package org.briarproject.briar.android.settings;
|
||||||
|
|
||||||
import android.content.ActivityNotFoundException;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@@ -8,7 +7,6 @@ import android.view.LayoutInflater;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import org.briarproject.briar.BuildConfig;
|
import org.briarproject.briar.BuildConfig;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
@@ -21,10 +19,9 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
import static android.widget.Toast.LENGTH_LONG;
|
import static android.content.Intent.ACTION_VIEW;
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static java.util.logging.Logger.getLogger;
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.briar.android.util.UiUtils.tryToStartActivity;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
@@ -85,16 +82,9 @@ public class AboutFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void goToUrl(String url) {
|
private void goToUrl(String url) {
|
||||||
Intent i = new Intent(Intent.ACTION_VIEW);
|
Intent i = new Intent(ACTION_VIEW);
|
||||||
i.setData(Uri.parse(url));
|
i.setData(Uri.parse(url));
|
||||||
try {
|
tryToStartActivity(requireActivity(), i);
|
||||||
startActivity(i);
|
|
||||||
} catch (ActivityNotFoundException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
Toast.makeText(requireContext(),
|
|
||||||
R.string.error_start_activity, LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -27,7 +27,6 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
|||||||
import static android.os.Build.VERSION.SDK_INT;
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
import static androidx.core.view.ViewCompat.LAYOUT_DIRECTION_LTR;
|
import static androidx.core.view.ViewCompat.LAYOUT_DIRECTION_LTR;
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
import static java.util.logging.Level.INFO;
|
|
||||||
import static java.util.logging.Logger.getLogger;
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static org.briarproject.briar.android.BriarApplication.ENTRY_ACTIVITY;
|
import static org.briarproject.briar.android.BriarApplication.ENTRY_ACTIVITY;
|
||||||
import static org.briarproject.briar.android.navdrawer.NavDrawerActivity.SIGN_OUT_URI;
|
import static org.briarproject.briar.android.navdrawer.NavDrawerActivity.SIGN_OUT_URI;
|
||||||
@@ -75,12 +74,6 @@ public class DisplayFragment extends PreferenceFragmentCompat {
|
|||||||
Locale locale = Localizer.getLocaleFromTag(tag);
|
Locale locale = Localizer.getLocaleFromTag(tag);
|
||||||
if (locale == null)
|
if (locale == null)
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
// Exclude RTL locales on API < 17, they won't be laid out correctly
|
|
||||||
if (SDK_INT < 17 && !isLeftToRight(locale)) {
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Skipping RTL locale " + tag);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
String nativeName = locale.getDisplayName(locale);
|
String nativeName = locale.getDisplayName(locale);
|
||||||
// Fallback to English if the name is unknown in both native and
|
// Fallback to English if the name is unknown in both native and
|
||||||
// current locale.
|
// current locale.
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user