Compare commits

...

65 Commits

Author SHA1 Message Date
akwizgran
72136cc627 DO NOT MERGE: Log contact name and alias.
Use the logger. Log the contact name, alias and message status.
2023-07-24 16:53:43 +01:00
Sebastian Kürten
411ace13aa Add some logging to ClientVersioningManagerImpl 2023-07-24 16:09:24 +02:00
akwizgran
952ee42ad1 Merge branch 'blog-txns' into 'master'
Add transactional versions of BlogManager methods and a bug fix

See merge request briar/briar!1802
2023-07-13 20:30:21 +00:00
Torsten Grote
f61b09d5a9 Fix BlogManager tests after last commits 2023-07-13 14:50:05 -03:00
Torsten Grote
8f735d176e Add transactional versions of BlogManager methods 2023-07-13 13:01:30 -03:00
Torsten Grote
c47253fc5f Mark our own reblogs as read automatically 2023-07-13 12:54:17 -03:00
akwizgran
7a0fb74c09 Merge branch '2266-target-sdk-33' into 'master'
Target SDK 33

Closes #2266

See merge request briar/briar!1800
2023-07-03 10:55:46 +00:00
akwizgran
882f536b8d Don't try to get Bluetooth address from settings. 2023-06-30 18:14:12 +01:00
Torsten Grote
74f8e84a9b React to device light idle mode in DozeWatchdog as well 2023-06-29 10:58:32 -03:00
Torsten Grote
23df2a41c2 Add @NotNullByDefault annotation to ConditionManagers 2023-06-29 10:58:32 -03:00
Torsten Grote
c77eaf16d9 Log more mode changes in AndroidBatteryManager 2023-06-29 10:58:31 -03:00
Torsten Grote
9a6bb4b203 Set dozed to true when we are in LowPowerStandby 2023-06-29 10:58:31 -03:00
Torsten Grote
3d237a9104 Introduce tryToStartActivity() helper method 2023-06-29 10:58:31 -03:00
Torsten Grote
fa216ffc6f Move requestEnableWiFi() into AbstractConditionManager 2023-06-29 10:58:31 -03:00
Torsten Grote
a34631d36c Catch ActivityNotFoundException in places where we launch external intents 2023-06-29 10:58:31 -03:00
Torsten Grote
45cda191e5 Log changes to DeviceLightIdleMode in AndroidBatteryManager 2023-06-29 10:58:31 -03:00
Torsten Grote
2495b6f5c0 Add LowPowerStandby stub to DozeWatchdogImpl 2023-06-29 10:58:31 -03:00
Torsten Grote
03fc504f7d Log changes to LowPowerStandby in AndroidBatteryManager 2023-06-29 10:58:30 -03:00
Torsten Grote
d19062e319 Don't disable hotspot start button after click
to avoid issues when coming back to the screen after granting permissions.
2023-06-29 10:58:30 -03:00
Torsten Grote
fdb429ab7a Ask for NEARBY_WIFI_DEVICES permission on SDK 33 and up 2023-06-29 10:58:30 -03:00
Torsten Grote
d0c59a6d75 Target SDK 33 and ask for notification permission
when creating account and when signing in
2023-06-29 10:58:30 -03:00
akwizgran
3bb39c2aa3 Merge branch 'fix-macos-x86-issue' into 'master'
Fix architecture detection for macOS Intel CPUs

See merge request briar/briar!1799
2023-06-28 11:07:26 +00:00
Sebastian Kürten
917fc5e5b6 Fix architecture detection for macOS Intel CPUs 2023-06-28 12:57:20 +02:00
akwizgran
caa078585b Merge branch 'macos3' into 'master'
macOS support

See merge request briar/briar!1790
2023-06-22 17:04:09 +00:00
akwizgran
e68c0c7f4b Merge branch 'onionwrapper-0.0.4' into 'master'
Upgrade onionwrapper to 0.0.4

See merge request briar/briar!1798
2023-06-22 12:26:21 +00:00
akwizgran
a6b3749fb6 Extend comment explaining TorState -> State mapping. 2023-06-22 13:12:58 +01:00
Torsten Grote
a8f6e8e4bd Merge branch 'check-network-status-periodically' into 'master'
Check network status periodically

See merge request briar/briar!1797
2023-06-21 13:32:39 +00:00
akwizgran
4d884601f0 Check more often, only broadcast status if changed. 2023-06-20 17:01:45 +01:00
akwizgran
b71198d9b1 Check network status periodically on JavaSE. 2023-06-20 16:34:45 +01:00
Sebastian Kürten
079c6e0475 Add comment why we choose a differnt port for headless on macOS 2023-06-20 12:21:11 +02:00
Sebastian Kürten
3a0f8ed85c Document building of macOS headless jars and fix included native binaries on macOS 2023-06-15 18:07:18 +02:00
Sebastian Kürten
57f7501780 macOS support 2023-06-15 12:48:01 +02:00
akwizgran
3cc5699fe0 Upgrade onionwrapper to 0.0.4. 2023-06-14 17:06:10 +01:00
akwizgran
7d761710e6 Bump version numbers for 1.5.4 release. 2023-06-02 13:53:20 +01:00
Torsten Grote
7461d3c943 Merge branch '2434-use-us-locale-for-lowercasing-onion-hostname' into 'master'
Use US locale for lowercasing onion hostname

Closes #2434

See merge request briar/briar!1796
2023-05-31 16:27:24 +00:00
akwizgran
9291613175 Fix some other uses of toLowerCase() without a locale. 2023-05-30 22:06:18 +01:00
akwizgran
ce6739a9fd Use US locale for lowercasing onion hostname. 2023-05-30 22:00:41 +01:00
akwizgran
1f1a97f62d Bump version numbers for 1.5.3 release. 2023-05-24 11:33:49 +01:00
akwizgran
7a33d26533 Merge branch 'new-handshake' 2023-05-24 11:22:23 +01:00
akwizgran
f2c85f37be Merge branch '2391-share-link' into 'master'
Share a link to the Briar download page via another app

Closes #2391

See merge request briar/briar!1795
2023-05-22 15:35:43 +00:00
Torsten Grote
8e3fa872fd Move About settings item to the bottom 2023-05-17 08:53:24 -03:00
akwizgran
0d1e81ebdb Merge branch 'use-default-secure-random-provider-on-macos' into 'master'
Use system default secure random provider on macOS

Closes briar-desktop#132

See merge request briar/briar!1794
2023-05-17 09:25:42 +00:00
Sebastian Kürten
bded4e7bc8 Use system default secure random provider on macOS 2023-05-17 11:13:43 +02:00
Torsten Grote
bf1a5cf218 Allow sharing download link for Briar from settings actions 2023-05-16 16:55:19 -03:00
akwizgran
dd7a638984 Merge branch 'fa-string-fix' into 'master'
Fix translation

See merge request briar/briar!1793
2023-05-16 10:38:47 +00:00
paul
942222131e Fix translation. 2023-05-15 21:48:02 +00:00
akwizgran
643757e407 Bump version numbers for 1.5.2 release. 2023-05-15 15:48:50 +01:00
akwizgran
7c530ad7a3 Update translations. 2023-05-15 15:48:00 +01:00
Torsten Grote
23b2dfa4a8 Merge branch 'conversationmanager-txn' into 'master'
Add transactional versions to delete message functions

See merge request briar/briar!1792
2023-05-15 12:03:53 +00:00
ialokim
ce10e6770f add transactional versions to delete message functions 2023-05-12 23:58:59 +02:00
Torsten Grote
b88dbee881 Merge branch 'onionwrapper-0.0.2' into 'master'
Upgrade onionwrapper to 0.0.2 and dont-kill-me-lib to 0.2.7

See merge request briar/briar!1791
2023-05-10 17:00:38 +00:00
akwizgran
0ca21ad4c0 Upgrade onionwrapper to 0.0.2 and dont-kill-me-lib to 0.2.7. 2023-05-09 15:23:17 +01:00
akwizgran
a14f62dcc3 Update translations. 2023-05-05 14:57:46 +01:00
akwizgran
f0c1ebcc1b Merge branch 'kill-android4' into 'master'
Drop support for Android 4

See merge request briar/briar!1770
2023-05-05 13:54:07 +00:00
akwizgran
4a4b04bec3 Rename version constant. 2023-04-26 17:10:23 +01:00
Torsten Grote
6f57ec8281 Merge branch 'privategroup-testdata' into 'master'
TestDataCreator with support for private groups

See merge request briar/briar!1788
2023-04-26 12:38:53 +00:00
Sebastian Kürten
0eb0bbdc99 Add ability to add private group test data in settings 2023-04-26 00:05:49 +02:00
Torsten Grote
76344344d2 Downgrade material library due to upstream bug
https://github.com/material-components/material-components-android/issues/3191
2023-04-25 11:29:51 -03:00
akwizgran
624f11a61f Bump version numbers for 1.5.1 release. 2023-04-21 15:51:55 +01:00
Torsten Grote
fbc32830bd Force kotlin standard lib to latest version to prevent jetifier issues 2023-04-19 11:02:33 -03:00
Torsten Grote
145117a1dc Update most of the things 2023-04-19 11:02:33 -03:00
Torsten Grote
6ed55bcd7d Drop support for Android 4
new minSdk is 21
2023-04-19 11:02:31 -03:00
ialokim
f8015272f4 private group support for TestDataCreator 2023-04-06 16:43:18 +02:00
akwizgran
462f57c966 Upgrade handshake protocol to new key agreement method. 2023-03-10 16:05:59 +00:00
akwizgran
8d20c5d8b8 Reify RecordPredicate for easier testing. 2023-03-10 15:15:29 +00:00
166 changed files with 2152 additions and 1221 deletions

View File

@@ -11,10 +11,10 @@ android {
} }
defaultConfig { defaultConfig {
minSdkVersion 16 minSdkVersion 21
targetSdkVersion 31 targetSdkVersion 33
versionCode 10500 versionCode 10504
versionName "1.5.0" 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,

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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();

View File

@@ -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);
}
} }

View File

@@ -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 " +

View File

@@ -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);
} }

View File

@@ -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;

View 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();
}
}

View File

@@ -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);
}
}
} }

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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();
} }

View File

@@ -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

View File

@@ -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',

View File

@@ -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.
* *

View File

@@ -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;
}
} }

View File

@@ -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> {
}
} }

View File

@@ -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();
}

View File

@@ -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);
} }

View File

@@ -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',
] ]
} }

View File

@@ -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"

View File

@@ -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());

View File

@@ -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.

View File

@@ -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;

View File

@@ -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

View File

@@ -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;

View File

@@ -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;
} }

View File

@@ -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);
} }
} }

View File

@@ -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());

View File

@@ -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();
}
}

View File

@@ -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;
} }

View File

@@ -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;

View File

@@ -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;

View File

@@ -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());

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -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));
}});
}
}

View File

@@ -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;
} }

View File

@@ -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);

View File

@@ -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());
}
}
}

View File

@@ -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));
}});
}
} }

View File

@@ -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);
}; };

View File

@@ -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]);
} }

View File

@@ -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));

View File

@@ -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',
] ]
} }

View File

@@ -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"

View File

@@ -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());
}
}
}

View File

@@ -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;

View File

@@ -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() {
}
} }

View File

@@ -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;
} }
} }

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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
} }
} }

View File

@@ -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();
}
}

View File

@@ -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

View File

@@ -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',

View File

@@ -24,10 +24,10 @@ android {
} }
defaultConfig { defaultConfig {
minSdkVersion 16 minSdkVersion 21
targetSdkVersion 31 targetSdkVersion 33
versionCode 10500 versionCode 10504
versionName "1.5.0" 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"

View File

@@ -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"

View File

@@ -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;

View File

@@ -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());
} }

View File

@@ -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;
} }

View File

@@ -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);
}
}
} }
} }
} }

View File

@@ -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;
} }

View File

@@ -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);
} }

View File

@@ -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

View File

@@ -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);
} }
} }

View File

@@ -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());
} }
} }

View File

@@ -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);

View File

@@ -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);
} }
} }

View File

@@ -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);
} }

View File

@@ -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);

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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() {

View File

@@ -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(

View File

@@ -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(

View File

@@ -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,

View File

@@ -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;

View File

@@ -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) -> {

View File

@@ -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();
}
}
} }

View File

@@ -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;
} }
} }

View File

@@ -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));
}
} }

View File

@@ -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);
}
}

View File

@@ -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));
} }
} }

View File

@@ -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();
} }

View File

@@ -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";
} }

View File

@@ -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());
} }

View File

@@ -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);
} }

View File

@@ -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) {

View File

@@ -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

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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();

View File

@@ -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();
}
} }
} }

View File

@@ -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();
}
} }
} }

View File

@@ -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