mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-15 04:18:53 +01:00
Compare commits
68 Commits
beta-2017-
...
beta-2017-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe963edd9d | ||
|
|
96f006068f | ||
|
|
74f1fa5690 | ||
|
|
6b3a1fd6d4 | ||
|
|
bcabcfce8c | ||
|
|
db0a3bf380 | ||
|
|
d5d9436e28 | ||
|
|
9d0dbe9210 | ||
|
|
1f7d1bf515 | ||
|
|
fb85ecf07b | ||
|
|
a931e6b316 | ||
|
|
3aa4644339 | ||
|
|
9a638c804a | ||
|
|
df3254c634 | ||
|
|
ba353b9f2b | ||
|
|
04c4e70dd1 | ||
|
|
d381e25e86 | ||
|
|
0c085f139a | ||
|
|
4123f4a5ce | ||
|
|
7bc269fda4 | ||
|
|
a22931bae6 | ||
|
|
403f886110 | ||
|
|
b7866be38d | ||
|
|
a1b415330e | ||
|
|
58318bb79f | ||
|
|
10bb30e190 | ||
|
|
199a2ffc46 | ||
|
|
f6ad2992f2 | ||
|
|
f039bd1239 | ||
|
|
da22d91ef3 | ||
|
|
cd360ec877 | ||
|
|
8e1ada4cdc | ||
|
|
ac063b4c79 | ||
|
|
10e6163e94 | ||
|
|
ebc3402307 | ||
|
|
d9c63bbcfe | ||
|
|
9c89e83c20 | ||
|
|
adc9bdeb68 | ||
|
|
ff7f0bdc63 | ||
|
|
c5f6980c69 | ||
|
|
2574354997 | ||
|
|
c4e42949cf | ||
|
|
1c5897f1cc | ||
|
|
510f99c7da | ||
|
|
1918346ae8 | ||
|
|
2a59515c72 | ||
|
|
7161152b41 | ||
|
|
b42660edab | ||
|
|
b405bbf98e | ||
|
|
c167938b61 | ||
|
|
24b531e6b2 | ||
|
|
9cffff715a | ||
|
|
804e912e19 | ||
|
|
d67e3900e3 | ||
|
|
e682f31898 | ||
|
|
a9053808b4 | ||
|
|
d9a62a0431 | ||
|
|
15ba73276d | ||
|
|
720dda784e | ||
|
|
0ae55404f5 | ||
|
|
9c41437870 | ||
|
|
da9cde083f | ||
|
|
ce3156c9fe | ||
|
|
be3752bf2f | ||
|
|
ef74db65aa | ||
|
|
867a233b6f | ||
|
|
59af25b2cd | ||
|
|
79c78518fb |
9
.gitignore
vendored
9
.gitignore
vendored
@@ -9,17 +9,18 @@ Thumbs.db
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
# Eclipse project files
|
# Eclipse project files
|
||||||
#.classpath
|
.classpath
|
||||||
#.project
|
.project
|
||||||
|
.settings
|
||||||
|
|
||||||
# Local configuration file (sdk path, etc)
|
# Local configuration file (sdk path, etc)
|
||||||
local.properties
|
local.properties
|
||||||
|
|
||||||
# Android Studio
|
# Android Studio
|
||||||
.idea/*
|
.idea/*
|
||||||
|
!.idea/runConfigurations/
|
||||||
!.idea/codeStyleSettings.xml
|
!.idea/codeStyleSettings.xml
|
||||||
.gradle
|
.gradle
|
||||||
build/
|
build/
|
||||||
*.iml
|
*.iml
|
||||||
.gitignore
|
projectFilesBackup/
|
||||||
src/test/
|
|
||||||
28
.idea/runConfigurations/All_tests.xml
generated
Normal file
28
.idea/runConfigurations/All_tests.xml
generated
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="All tests" type="AndroidJUnit" factoryName="Android JUnit">
|
||||||
|
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||||
|
<module name="briar-android" />
|
||||||
|
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||||
|
<option name="ALTERNATIVE_JRE_PATH" />
|
||||||
|
<option name="PACKAGE_NAME" value="" />
|
||||||
|
<option name="MAIN_CLASS_NAME" value="" />
|
||||||
|
<option name="METHOD_NAME" value="" />
|
||||||
|
<option name="TEST_OBJECT" value="package" />
|
||||||
|
<option name="VM_PARAMETERS" value="-ea" />
|
||||||
|
<option name="PARAMETERS" value="" />
|
||||||
|
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/briar-android" />
|
||||||
|
<option name="ENV_VARIABLES" />
|
||||||
|
<option name="PASS_PARENT_ENVS" value="true" />
|
||||||
|
<option name="TEST_SEARCH_SCOPE">
|
||||||
|
<value defaultName="singleModule" />
|
||||||
|
</option>
|
||||||
|
<envs />
|
||||||
|
<patterns />
|
||||||
|
<method>
|
||||||
|
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-api" run_configuration_type="AndroidJUnit" />
|
||||||
|
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-core" run_configuration_type="AndroidJUnit" />
|
||||||
|
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-j2se" run_configuration_type="AndroidJUnit" />
|
||||||
|
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in briar-core" run_configuration_type="AndroidJUnit" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
23
.idea/runConfigurations/All_tests_in_bramble_api.xml
generated
Normal file
23
.idea/runConfigurations/All_tests_in_bramble_api.xml
generated
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="All tests in bramble-api" type="AndroidJUnit" factoryName="Android JUnit">
|
||||||
|
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||||
|
<module name="bramble-api" />
|
||||||
|
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||||
|
<option name="ALTERNATIVE_JRE_PATH" />
|
||||||
|
<option name="PACKAGE_NAME" value="" />
|
||||||
|
<option name="MAIN_CLASS_NAME" value="" />
|
||||||
|
<option name="METHOD_NAME" value="" />
|
||||||
|
<option name="TEST_OBJECT" value="package" />
|
||||||
|
<option name="VM_PARAMETERS" value="-ea" />
|
||||||
|
<option name="PARAMETERS" value="" />
|
||||||
|
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-api" />
|
||||||
|
<option name="ENV_VARIABLES" />
|
||||||
|
<option name="PASS_PARENT_ENVS" value="true" />
|
||||||
|
<option name="TEST_SEARCH_SCOPE">
|
||||||
|
<value defaultName="singleModule" />
|
||||||
|
</option>
|
||||||
|
<envs />
|
||||||
|
<patterns />
|
||||||
|
<method />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
23
.idea/runConfigurations/All_tests_in_bramble_core.xml
generated
Normal file
23
.idea/runConfigurations/All_tests_in_bramble_core.xml
generated
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="All tests in bramble-core" type="AndroidJUnit" factoryName="Android JUnit">
|
||||||
|
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||||
|
<module name="bramble-core" />
|
||||||
|
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||||
|
<option name="ALTERNATIVE_JRE_PATH" />
|
||||||
|
<option name="PACKAGE_NAME" value="" />
|
||||||
|
<option name="MAIN_CLASS_NAME" value="" />
|
||||||
|
<option name="METHOD_NAME" value="" />
|
||||||
|
<option name="TEST_OBJECT" value="package" />
|
||||||
|
<option name="VM_PARAMETERS" value="-ea" />
|
||||||
|
<option name="PARAMETERS" value="" />
|
||||||
|
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-core" />
|
||||||
|
<option name="ENV_VARIABLES" />
|
||||||
|
<option name="PASS_PARENT_ENVS" value="true" />
|
||||||
|
<option name="TEST_SEARCH_SCOPE">
|
||||||
|
<value defaultName="singleModule" />
|
||||||
|
</option>
|
||||||
|
<envs />
|
||||||
|
<patterns />
|
||||||
|
<method />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
23
.idea/runConfigurations/All_tests_in_bramble_j2se.xml
generated
Normal file
23
.idea/runConfigurations/All_tests_in_bramble_j2se.xml
generated
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="All tests in bramble-j2se" type="AndroidJUnit" factoryName="Android JUnit">
|
||||||
|
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||||
|
<module name="bramble-j2se" />
|
||||||
|
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||||
|
<option name="ALTERNATIVE_JRE_PATH" />
|
||||||
|
<option name="PACKAGE_NAME" value="" />
|
||||||
|
<option name="MAIN_CLASS_NAME" value="" />
|
||||||
|
<option name="METHOD_NAME" value="" />
|
||||||
|
<option name="TEST_OBJECT" value="package" />
|
||||||
|
<option name="VM_PARAMETERS" value="-ea -Djava.library.path=libs" />
|
||||||
|
<option name="PARAMETERS" value="" />
|
||||||
|
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-j2se" />
|
||||||
|
<option name="ENV_VARIABLES" />
|
||||||
|
<option name="PASS_PARENT_ENVS" value="true" />
|
||||||
|
<option name="TEST_SEARCH_SCOPE">
|
||||||
|
<value defaultName="singleModule" />
|
||||||
|
</option>
|
||||||
|
<envs />
|
||||||
|
<patterns />
|
||||||
|
<method />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
23
.idea/runConfigurations/All_tests_in_briar_android.xml
generated
Normal file
23
.idea/runConfigurations/All_tests_in_briar_android.xml
generated
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="All tests in briar-android" type="AndroidJUnit" factoryName="Android JUnit">
|
||||||
|
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||||
|
<module name="briar-android" />
|
||||||
|
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||||
|
<option name="ALTERNATIVE_JRE_PATH" />
|
||||||
|
<option name="PACKAGE_NAME" value="" />
|
||||||
|
<option name="MAIN_CLASS_NAME" value="" />
|
||||||
|
<option name="METHOD_NAME" value="" />
|
||||||
|
<option name="TEST_OBJECT" value="package" />
|
||||||
|
<option name="VM_PARAMETERS" value="-ea" />
|
||||||
|
<option name="PARAMETERS" value="" />
|
||||||
|
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/briar-android" />
|
||||||
|
<option name="ENV_VARIABLES" />
|
||||||
|
<option name="PASS_PARENT_ENVS" value="true" />
|
||||||
|
<option name="TEST_SEARCH_SCOPE">
|
||||||
|
<value defaultName="singleModule" />
|
||||||
|
</option>
|
||||||
|
<envs />
|
||||||
|
<patterns />
|
||||||
|
<method />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
23
.idea/runConfigurations/All_tests_in_briar_core.xml
generated
Normal file
23
.idea/runConfigurations/All_tests_in_briar_core.xml
generated
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="All tests in briar-core" type="AndroidJUnit" factoryName="Android JUnit">
|
||||||
|
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||||
|
<module name="briar-core" />
|
||||||
|
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||||
|
<option name="ALTERNATIVE_JRE_PATH" />
|
||||||
|
<option name="PACKAGE_NAME" value="" />
|
||||||
|
<option name="MAIN_CLASS_NAME" value="" />
|
||||||
|
<option name="METHOD_NAME" value="" />
|
||||||
|
<option name="TEST_OBJECT" value="package" />
|
||||||
|
<option name="VM_PARAMETERS" value="-ea" />
|
||||||
|
<option name="PARAMETERS" value="" />
|
||||||
|
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/briar-core" />
|
||||||
|
<option name="ENV_VARIABLES" />
|
||||||
|
<option name="PASS_PARENT_ENVS" value="true" />
|
||||||
|
<option name="TEST_SEARCH_SCOPE">
|
||||||
|
<value defaultName="singleModule" />
|
||||||
|
</option>
|
||||||
|
<envs />
|
||||||
|
<patterns />
|
||||||
|
<method />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
@@ -67,6 +67,7 @@ import static java.util.logging.Level.INFO;
|
|||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_BLUETOOTH;
|
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_BLUETOOTH;
|
||||||
import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
|
import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
|
||||||
|
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PREF_BT_ENABLE;
|
||||||
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_ADDRESS;
|
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_ADDRESS;
|
||||||
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_UUID;
|
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_UUID;
|
||||||
import static org.briarproject.bramble.api.plugin.BluetoothConstants.UUID_BYTES;
|
import static org.briarproject.bramble.api.plugin.BluetoothConstants.UUID_BYTES;
|
||||||
@@ -164,7 +165,7 @@ class DroidtoothPlugin implements DuplexPlugin {
|
|||||||
bind();
|
bind();
|
||||||
} else {
|
} else {
|
||||||
// Enable Bluetooth if settings allow
|
// Enable Bluetooth if settings allow
|
||||||
if (callback.getSettings().getBoolean("enable", false)) {
|
if (callback.getSettings().getBoolean(PREF_BT_ENABLE, false)) {
|
||||||
wasEnabledByUs = true;
|
wasEnabledByUs = true;
|
||||||
if (adapter.enable()) LOG.info("Enabling Bluetooth");
|
if (adapter.enable()) LOG.info("Enabling Bluetooth");
|
||||||
else LOG.info("Could not enable Bluetooth");
|
else LOG.info("Could not enable Bluetooth");
|
||||||
|
|||||||
@@ -79,6 +79,12 @@ import static java.util.logging.Level.WARNING;
|
|||||||
import static net.freehaven.tor.control.TorControlCommands.HS_ADDRESS;
|
import static net.freehaven.tor.control.TorControlCommands.HS_ADDRESS;
|
||||||
import static net.freehaven.tor.control.TorControlCommands.HS_PRIVKEY;
|
import static net.freehaven.tor.control.TorControlCommands.HS_PRIVKEY;
|
||||||
import static org.briarproject.bramble.api.plugin.TorConstants.CONTROL_PORT;
|
import static org.briarproject.bramble.api.plugin.TorConstants.CONTROL_PORT;
|
||||||
|
import static org.briarproject.bramble.api.plugin.TorConstants.ID;
|
||||||
|
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK;
|
||||||
|
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_ALWAYS;
|
||||||
|
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_NEVER;
|
||||||
|
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_WIFI;
|
||||||
|
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_PORT;
|
||||||
import static org.briarproject.bramble.util.PrivacyUtils.scrubOnion;
|
import static org.briarproject.bramble.util.PrivacyUtils.scrubOnion;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@@ -182,19 +188,31 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
String torPath = torFile.getAbsolutePath();
|
String torPath = torFile.getAbsolutePath();
|
||||||
String configPath = configFile.getAbsolutePath();
|
String configPath = configFile.getAbsolutePath();
|
||||||
String pid = String.valueOf(android.os.Process.myPid());
|
String pid = String.valueOf(android.os.Process.myPid());
|
||||||
String[] cmd = {torPath, "-f", configPath, OWNER, pid};
|
|
||||||
String[] env = {"HOME=" + torDirectory.getAbsolutePath()};
|
|
||||||
Process torProcess;
|
Process torProcess;
|
||||||
|
ProcessBuilder pb =
|
||||||
|
new ProcessBuilder(torPath, "-f", configPath, OWNER, pid);
|
||||||
|
Map<String, String> env = pb.environment();
|
||||||
|
env.put("HOME", torDirectory.getAbsolutePath());
|
||||||
|
pb.directory(torDirectory);
|
||||||
try {
|
try {
|
||||||
torProcess = Runtime.getRuntime().exec(cmd, env, torDirectory);
|
torProcess = pb.start();
|
||||||
} catch (SecurityException | IOException e) {
|
} catch (SecurityException | IOException e) {
|
||||||
throw new PluginException(e);
|
throw new PluginException(e);
|
||||||
}
|
}
|
||||||
// Log the process's standard output until it detaches
|
// Log the process's standard output until it detaches
|
||||||
if (LOG.isLoggable(INFO)) {
|
if (LOG.isLoggable(INFO)) {
|
||||||
Scanner stdout = new Scanner(torProcess.getInputStream());
|
Scanner stdout = new Scanner(torProcess.getInputStream());
|
||||||
while (stdout.hasNextLine()) LOG.info(stdout.nextLine());
|
Scanner stderr = new Scanner(torProcess.getErrorStream());
|
||||||
|
while (stdout.hasNextLine() || stderr.hasNextLine()){
|
||||||
|
if(stdout.hasNextLine()) {
|
||||||
|
LOG.info(stdout.nextLine());
|
||||||
|
}
|
||||||
|
if(stderr.hasNextLine()){
|
||||||
|
LOG.info(stderr.nextLine());
|
||||||
|
}
|
||||||
|
}
|
||||||
stdout.close();
|
stdout.close();
|
||||||
|
stderr.close();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
// Wait for the process to detach or exit
|
// Wait for the process to detach or exit
|
||||||
@@ -366,7 +384,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
// If there's already a port number stored in config, reuse it
|
// If there's already a port number stored in config, reuse it
|
||||||
String portString = callback.getSettings().get("port");
|
String portString = callback.getSettings().get(PREF_TOR_PORT);
|
||||||
int port;
|
int port;
|
||||||
if (StringUtils.isNullOrEmpty(portString)) port = 0;
|
if (StringUtils.isNullOrEmpty(portString)) port = 0;
|
||||||
else port = Integer.parseInt(portString);
|
else port = Integer.parseInt(portString);
|
||||||
@@ -389,7 +407,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
// Store the port number
|
// Store the port number
|
||||||
final String localPort = String.valueOf(ss.getLocalPort());
|
final String localPort = String.valueOf(ss.getLocalPort());
|
||||||
Settings s = new Settings();
|
Settings s = new Settings();
|
||||||
s.put("port", localPort);
|
s.put(PREF_TOR_PORT, localPort);
|
||||||
callback.mergeSettings(s);
|
callback.mergeSettings(s);
|
||||||
// Create a hidden service if necessary
|
// Create a hidden service if necessary
|
||||||
ioExecutor.execute(new Runnable() {
|
ioExecutor.execute(new Runnable() {
|
||||||
@@ -666,7 +684,8 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
@Override
|
@Override
|
||||||
public void eventOccurred(Event e) {
|
public void eventOccurred(Event e) {
|
||||||
if (e instanceof SettingsUpdatedEvent) {
|
if (e instanceof SettingsUpdatedEvent) {
|
||||||
if (((SettingsUpdatedEvent) e).getNamespace().equals("tor")) {
|
SettingsUpdatedEvent s = (SettingsUpdatedEvent) e;
|
||||||
|
if (s.getNamespace().equals(ID.getString())) {
|
||||||
LOG.info("Tor settings updated");
|
LOG.info("Tor settings updated");
|
||||||
updateConnectionStatus();
|
updateConnectionStatus();
|
||||||
}
|
}
|
||||||
@@ -688,7 +707,8 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
boolean blocked = TorNetworkMetadata.isTorProbablyBlocked(
|
boolean blocked = TorNetworkMetadata.isTorProbablyBlocked(
|
||||||
country);
|
country);
|
||||||
Settings s = callback.getSettings();
|
Settings s = callback.getSettings();
|
||||||
boolean useMobileData = s.getBoolean("torOverMobile", true);
|
int network = s.getInt(PREF_TOR_NETWORK,
|
||||||
|
PREF_TOR_NETWORK_ALWAYS);
|
||||||
|
|
||||||
if (LOG.isLoggable(INFO)) {
|
if (LOG.isLoggable(INFO)) {
|
||||||
LOG.info("Online: " + online + ", wifi: " + wifi);
|
LOG.info("Online: " + online + ", wifi: " + wifi);
|
||||||
@@ -703,7 +723,8 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
} else if (blocked) {
|
} else if (blocked) {
|
||||||
LOG.info("Disabling network, country is blocked");
|
LOG.info("Disabling network, country is blocked");
|
||||||
enableNetwork(false);
|
enableNetwork(false);
|
||||||
} else if (!wifi && !useMobileData) {
|
} else if (network == PREF_TOR_NETWORK_NEVER
|
||||||
|
|| (network == PREF_TOR_NETWORK_WIFI && !wifi)) {
|
||||||
LOG.info("Disabling network due to data setting");
|
LOG.info("Disabling network due to data setting");
|
||||||
enableNetwork(false);
|
enableNetwork(false);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
package org.briarproject.bramble.system;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.wifi.WifiConfiguration;
|
||||||
|
import android.net.wifi.WifiManager;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.provider.Settings;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static android.content.Context.WIFI_SERVICE;
|
||||||
|
import static android.provider.Settings.Secure.ANDROID_ID;
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
class AndroidSecureRandomProvider extends LinuxSecureRandomProvider {
|
||||||
|
|
||||||
|
private static final int SEED_LENGTH = 32;
|
||||||
|
|
||||||
|
private final Context appContext;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
AndroidSecureRandomProvider(Application app) {
|
||||||
|
appContext = app.getApplicationContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeToEntropyPool(DataOutputStream out) throws IOException {
|
||||||
|
super.writeToEntropyPool(out);
|
||||||
|
out.writeInt(android.os.Process.myPid());
|
||||||
|
out.writeInt(android.os.Process.myTid());
|
||||||
|
out.writeInt(android.os.Process.myUid());
|
||||||
|
if (Build.FINGERPRINT != null) out.writeUTF(Build.FINGERPRINT);
|
||||||
|
if (Build.SERIAL != null) out.writeUTF(Build.SERIAL);
|
||||||
|
ContentResolver contentResolver = appContext.getContentResolver();
|
||||||
|
String id = Settings.Secure.getString(contentResolver, ANDROID_ID);
|
||||||
|
if (id != null) out.writeUTF(id);
|
||||||
|
Parcel parcel = Parcel.obtain();
|
||||||
|
WifiManager wm =
|
||||||
|
(WifiManager) appContext.getSystemService(WIFI_SERVICE);
|
||||||
|
List<WifiConfiguration> configs = wm.getConfiguredNetworks();
|
||||||
|
if (configs != null) {
|
||||||
|
for (WifiConfiguration config : configs)
|
||||||
|
parcel.writeParcelable(config, 0);
|
||||||
|
}
|
||||||
|
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
|
||||||
|
if (bt != null) {
|
||||||
|
for (BluetoothDevice device : bt.getBondedDevices())
|
||||||
|
parcel.writeParcelable(device, 0);
|
||||||
|
}
|
||||||
|
out.write(parcel.marshall());
|
||||||
|
parcel.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeSeed() {
|
||||||
|
super.writeSeed();
|
||||||
|
if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT <= 18)
|
||||||
|
applyOpenSslFix();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
|
||||||
|
private void applyOpenSslFix() {
|
||||||
|
byte[] seed = new LinuxSecureRandomSpi().engineGenerateSeed(
|
||||||
|
SEED_LENGTH);
|
||||||
|
try {
|
||||||
|
// Seed the OpenSSL PRNG
|
||||||
|
Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto")
|
||||||
|
.getMethod("RAND_seed", byte[].class)
|
||||||
|
.invoke(null, seed);
|
||||||
|
// Mix the output of the Linux PRNG into the OpenSSL PRNG
|
||||||
|
int bytesRead = (Integer) Class.forName(
|
||||||
|
"org.apache.harmony.xnet.provider.jsse.NativeCrypto")
|
||||||
|
.getMethod("RAND_load_file", String.class, long.class)
|
||||||
|
.invoke(null, "/dev/urandom", 1024);
|
||||||
|
if (bytesRead != 1024) throw new IOException();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new SecurityException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
package org.briarproject.bramble.system;
|
|
||||||
|
|
||||||
import android.app.Application;
|
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.provider.Settings;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import static android.provider.Settings.Secure.ANDROID_ID;
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
@NotNullByDefault
|
|
||||||
class AndroidSeedProvider extends LinuxSeedProvider {
|
|
||||||
|
|
||||||
private final Context appContext;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
AndroidSeedProvider(Application app) {
|
|
||||||
appContext = app.getApplicationContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void writeToEntropyPool(DataOutputStream out) throws IOException {
|
|
||||||
out.writeInt(android.os.Process.myPid());
|
|
||||||
out.writeInt(android.os.Process.myTid());
|
|
||||||
out.writeInt(android.os.Process.myUid());
|
|
||||||
if (Build.FINGERPRINT != null) out.writeUTF(Build.FINGERPRINT);
|
|
||||||
if (Build.SERIAL != null) out.writeUTF(Build.SERIAL);
|
|
||||||
ContentResolver contentResolver = appContext.getContentResolver();
|
|
||||||
String id = Settings.Secure.getString(contentResolver, ANDROID_ID);
|
|
||||||
if (id != null) out.writeUTF(id);
|
|
||||||
super.writeToEntropyPool(out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,7 @@ import android.app.Application;
|
|||||||
|
|
||||||
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.LocationUtils;
|
||||||
import org.briarproject.bramble.api.system.SeedProvider;
|
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
@@ -16,8 +16,8 @@ public class AndroidSystemModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
SeedProvider provideSeedProvider(Application app) {
|
SecureRandomProvider provideSecureRandomProvider(Application app) {
|
||||||
return new AndroidSeedProvider(app);
|
return new AndroidSecureRandomProvider(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
public interface PasswordStrengthEstimator {
|
public interface PasswordStrengthEstimator {
|
||||||
|
|
||||||
float NONE = 0;
|
float NONE = 0;
|
||||||
float WEAK = 0.4f;
|
float WEAK = 0.25f;
|
||||||
float QUITE_WEAK = 0.6f;
|
float QUITE_WEAK = 0.5f;
|
||||||
float QUITE_STRONG = 0.8f;
|
float QUITE_STRONG = 0.75f;
|
||||||
float STRONG = 1;
|
float STRONG = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -9,4 +9,5 @@ public interface BluetoothConstants {
|
|||||||
String PROP_ADDRESS = "address";
|
String PROP_ADDRESS = "address";
|
||||||
String PROP_UUID = "uuid";
|
String PROP_UUID = "uuid";
|
||||||
|
|
||||||
|
String PREF_BT_ENABLE = "enable";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,4 +4,5 @@ public interface LanTcpConstants {
|
|||||||
|
|
||||||
TransportId ID = new TransportId("org.briarproject.bramble.lan");
|
TransportId ID = new TransportId("org.briarproject.bramble.lan");
|
||||||
|
|
||||||
|
String PREF_LAN_IP_PORTS = "ipPorts";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,4 +8,11 @@ public interface TorConstants {
|
|||||||
int CONTROL_PORT = 59051;
|
int CONTROL_PORT = 59051;
|
||||||
|
|
||||||
int CONNECT_TO_PROXY_TIMEOUT = 5000; // Milliseconds
|
int CONNECT_TO_PROXY_TIMEOUT = 5000; // Milliseconds
|
||||||
|
|
||||||
|
String PREF_TOR_NETWORK = "network";
|
||||||
|
String PREF_TOR_PORT = "port";
|
||||||
|
|
||||||
|
int PREF_TOR_NETWORK_NEVER = 0;
|
||||||
|
int PREF_TOR_NETWORK_WIFI = 1;
|
||||||
|
int PREF_TOR_NETWORK_ALWAYS = 2;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package org.briarproject.bramble.api.system;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import java.security.Provider;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper for a platform-specific secure random number generator.
|
||||||
|
*/
|
||||||
|
@NotNullByDefault
|
||||||
|
public interface SecureRandomProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@link Provider} that provides a strong {@link SecureRandom}
|
||||||
|
* implementation, or null if the platform's default implementation should
|
||||||
|
* be used.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
Provider getProvider();
|
||||||
|
}
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.system;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Uses a platform-specific source to provide a seed for a pseudo-random
|
|
||||||
* number generator.
|
|
||||||
*/
|
|
||||||
@NotNullByDefault
|
|
||||||
public interface SeedProvider {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The length of the seed in bytes.
|
|
||||||
*/
|
|
||||||
int SEED_BYTES = 32;
|
|
||||||
|
|
||||||
byte[] getSeed();
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
package org.briarproject.bramble;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.GuardedBy;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.FINE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An {@link Executor} that delegates its tasks to another {@link Executor}
|
||||||
|
* while limiting the number of tasks that are delegated concurrently. Tasks
|
||||||
|
* are delegated in the order they are submitted to this executor.
|
||||||
|
*/
|
||||||
|
@NotNullByDefault
|
||||||
|
public class PoliteExecutor implements Executor {
|
||||||
|
|
||||||
|
private static final Level LOG_LEVEL = FINE;
|
||||||
|
|
||||||
|
private final Object lock = new Object();
|
||||||
|
@GuardedBy("lock")
|
||||||
|
private final Queue<Runnable> queue = new LinkedList<Runnable>();
|
||||||
|
private final Executor delegate;
|
||||||
|
private final int maxConcurrentTasks;
|
||||||
|
private final Logger log;
|
||||||
|
|
||||||
|
@GuardedBy("lock")
|
||||||
|
private int concurrentTasks = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param tag the tag to be used for logging
|
||||||
|
* @param delegate the executor to which tasks will be delegated
|
||||||
|
* @param maxConcurrentTasks the maximum number of tasks that will be
|
||||||
|
* delegated concurrently. If this is set to 1, tasks submitted to this
|
||||||
|
* executor will run in the order they are submitted and will not run
|
||||||
|
* concurrently
|
||||||
|
*/
|
||||||
|
public PoliteExecutor(String tag, Executor delegate,
|
||||||
|
int maxConcurrentTasks) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
this.maxConcurrentTasks = maxConcurrentTasks;
|
||||||
|
log = Logger.getLogger(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(final Runnable r) {
|
||||||
|
final long submitted = System.currentTimeMillis();
|
||||||
|
Runnable wrapped = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (log.isLoggable(LOG_LEVEL)) {
|
||||||
|
long queued = System.currentTimeMillis() - submitted;
|
||||||
|
log.log(LOG_LEVEL, "Queue time " + queued + " ms");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
r.run();
|
||||||
|
} finally {
|
||||||
|
scheduleNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
synchronized (lock) {
|
||||||
|
if (concurrentTasks < maxConcurrentTasks) {
|
||||||
|
concurrentTasks++;
|
||||||
|
delegate.execute(wrapped);
|
||||||
|
} else {
|
||||||
|
queue.add(wrapped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scheduleNext() {
|
||||||
|
synchronized (lock) {
|
||||||
|
Runnable next = queue.poll();
|
||||||
|
if (next == null) concurrentTasks--;
|
||||||
|
else delegate.execute(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package org.briarproject.bramble;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.RejectedExecutionHandler;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.FINE;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
public class TimeLoggingExecutor extends ThreadPoolExecutor {
|
||||||
|
|
||||||
|
private static final Level LOG_LEVEL = FINE;
|
||||||
|
|
||||||
|
private final Logger log;
|
||||||
|
|
||||||
|
public TimeLoggingExecutor(String tag, int corePoolSize, int maxPoolSize,
|
||||||
|
long keepAliveTime, TimeUnit unit,
|
||||||
|
BlockingQueue<Runnable> workQueue,
|
||||||
|
RejectedExecutionHandler handler) {
|
||||||
|
super(corePoolSize, maxPoolSize, keepAliveTime, unit, workQueue,
|
||||||
|
handler);
|
||||||
|
log = Logger.getLogger(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(final Runnable r) {
|
||||||
|
if (log.isLoggable(LOG_LEVEL)) {
|
||||||
|
final long submitted = System.currentTimeMillis();
|
||||||
|
super.execute(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
long started = System.currentTimeMillis();
|
||||||
|
long queued = started - submitted;
|
||||||
|
log.log(LOG_LEVEL, "Queue time " + queued + " ms");
|
||||||
|
r.run();
|
||||||
|
long executing = System.currentTimeMillis() - started;
|
||||||
|
log.log(LOG_LEVEL, "Execution time " + executing + " ms");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
super.execute(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
|
||||||
|
|
||||||
import java.security.Provider;
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.security.SecureRandomSpi;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link SecureRandom} implementation that combines the outputs of two or
|
|
||||||
* more other implementations using XOR.
|
|
||||||
*/
|
|
||||||
class CombinedSecureRandom extends SecureRandom {
|
|
||||||
|
|
||||||
private static final Provider PROVIDER = new CombinedProvider();
|
|
||||||
|
|
||||||
CombinedSecureRandom(SecureRandom... randoms) {
|
|
||||||
super(new CombinedSecureRandomSpi(randoms), PROVIDER);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class CombinedSecureRandomSpi extends SecureRandomSpi {
|
|
||||||
|
|
||||||
private final SecureRandom[] randoms;
|
|
||||||
|
|
||||||
private CombinedSecureRandomSpi(SecureRandom... randoms) {
|
|
||||||
if (randoms.length < 2) throw new IllegalArgumentException();
|
|
||||||
this.randoms = randoms;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected byte[] engineGenerateSeed(int numBytes) {
|
|
||||||
byte[] combined = new byte[numBytes];
|
|
||||||
for (SecureRandom random : randoms) {
|
|
||||||
byte[] b = random.generateSeed(numBytes);
|
|
||||||
int length = Math.min(numBytes, b.length);
|
|
||||||
for (int i = 0; i < length; i++)
|
|
||||||
combined[i] = (byte) (combined[i] ^ b[i]);
|
|
||||||
}
|
|
||||||
return combined;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void engineNextBytes(byte[] b) {
|
|
||||||
byte[] temp = new byte[b.length];
|
|
||||||
for (SecureRandom random : randoms) {
|
|
||||||
random.nextBytes(temp);
|
|
||||||
for (int i = 0; i < b.length; i++)
|
|
||||||
b[i] = (byte) (b[i] ^ temp[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void engineSetSeed(byte[] seed) {
|
|
||||||
for (SecureRandom random : randoms) random.setSeed(seed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class CombinedProvider extends Provider {
|
|
||||||
|
|
||||||
private CombinedProvider() {
|
|
||||||
super("Combined", 1.0, "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,7 +8,7 @@ import org.briarproject.bramble.api.crypto.PseudoRandom;
|
|||||||
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.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
import org.briarproject.bramble.api.system.SeedProvider;
|
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||||
import org.briarproject.bramble.api.transport.IncomingKeys;
|
import org.briarproject.bramble.api.transport.IncomingKeys;
|
||||||
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
||||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||||
@@ -29,7 +29,10 @@ import org.spongycastle.crypto.params.KeyParameter;
|
|||||||
|
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.Provider;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
import java.security.Security;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -101,16 +104,26 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
private final MessageEncrypter messageEncrypter;
|
private final MessageEncrypter messageEncrypter;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
CryptoComponentImpl(SeedProvider seedProvider) {
|
CryptoComponentImpl(SecureRandomProvider secureRandomProvider) {
|
||||||
if (!FortunaSecureRandom.selfTest()) throw new RuntimeException();
|
|
||||||
SecureRandom platformSecureRandom = new SecureRandom();
|
|
||||||
if (LOG.isLoggable(INFO)) {
|
if (LOG.isLoggable(INFO)) {
|
||||||
String provider = platformSecureRandom.getProvider().getName();
|
SecureRandom defaultSecureRandom = new SecureRandom();
|
||||||
String algorithm = platformSecureRandom.getAlgorithm();
|
String name = defaultSecureRandom.getProvider().getName();
|
||||||
LOG.info("Default SecureRandom: " + provider + " " + algorithm);
|
String algorithm = defaultSecureRandom.getAlgorithm();
|
||||||
|
LOG.info("Default SecureRandom: " + name + " " + algorithm);
|
||||||
}
|
}
|
||||||
SecureRandom fortuna = new FortunaSecureRandom(seedProvider.getSeed());
|
Provider provider = secureRandomProvider.getProvider();
|
||||||
secureRandom = new CombinedSecureRandom(platformSecureRandom, fortuna);
|
if (provider == null) {
|
||||||
|
LOG.info("Using default");
|
||||||
|
} else {
|
||||||
|
installSecureRandomProvider(provider);
|
||||||
|
if (LOG.isLoggable(INFO)) {
|
||||||
|
SecureRandom installedSecureRandom = new SecureRandom();
|
||||||
|
String name = installedSecureRandom.getProvider().getName();
|
||||||
|
String algorithm = installedSecureRandom.getAlgorithm();
|
||||||
|
LOG.info("Installed SecureRandom: " + name + " " + algorithm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
secureRandom = new SecureRandom();
|
||||||
ECKeyGenerationParameters params = new ECKeyGenerationParameters(
|
ECKeyGenerationParameters params = new ECKeyGenerationParameters(
|
||||||
PARAMETERS, secureRandom);
|
PARAMETERS, secureRandom);
|
||||||
agreementKeyPairGenerator = new ECKeyPairGenerator();
|
agreementKeyPairGenerator = new ECKeyPairGenerator();
|
||||||
@@ -124,6 +137,31 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
messageEncrypter = new MessageEncrypter(secureRandom);
|
messageEncrypter = new MessageEncrypter(secureRandom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
|
||||||
|
private void installSecureRandomProvider(Provider provider) {
|
||||||
|
Provider[] providers = Security.getProviders("SecureRandom.SHA1PRNG");
|
||||||
|
if (providers == null || providers.length == 0
|
||||||
|
|| !provider.getClass().equals(providers[0].getClass())) {
|
||||||
|
Security.insertProviderAt(provider, 1);
|
||||||
|
}
|
||||||
|
// Check the new provider is the default when no algorithm is specified
|
||||||
|
SecureRandom random = new SecureRandom();
|
||||||
|
if (!provider.getClass().equals(random.getProvider().getClass())) {
|
||||||
|
throw new SecurityException("Wrong SecureRandom provider: "
|
||||||
|
+ random.getProvider().getClass());
|
||||||
|
}
|
||||||
|
// Check the new provider is the default when SHA1PRNG is specified
|
||||||
|
try {
|
||||||
|
random = SecureRandom.getInstance("SHA1PRNG");
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new SecurityException(e);
|
||||||
|
}
|
||||||
|
if (!provider.getClass().equals(random.getProvider().getClass())) {
|
||||||
|
throw new SecurityException("Wrong SHA1PRNG provider: "
|
||||||
|
+ random.getProvider().getClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SecretKey generateSecretKey() {
|
public SecretKey generateSecretKey() {
|
||||||
byte[] b = new byte[SecretKey.LENGTH];
|
byte[] b = new byte[SecretKey.LENGTH];
|
||||||
@@ -133,7 +171,10 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PseudoRandom getPseudoRandom(int seed1, int seed2) {
|
public PseudoRandom getPseudoRandom(int seed1, int seed2) {
|
||||||
return new PseudoRandomImpl(seed1, seed2);
|
byte[] seed = new byte[INT_32_BYTES * 2];
|
||||||
|
ByteUtils.writeUint32(seed1, seed, 0);
|
||||||
|
ByteUtils.writeUint32(seed2, seed, INT_32_BYTES);
|
||||||
|
return new PseudoRandomImpl(seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -296,7 +337,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
public SecretKey deriveMasterSecret(byte[] theirPublicKey,
|
public SecretKey deriveMasterSecret(byte[] theirPublicKey,
|
||||||
KeyPair ourKeyPair, boolean alice) throws GeneralSecurityException {
|
KeyPair ourKeyPair, boolean alice) throws GeneralSecurityException {
|
||||||
return deriveMasterSecret(deriveSharedSecret(
|
return deriveMasterSecret(deriveSharedSecret(
|
||||||
theirPublicKey,ourKeyPair, alice));
|
theirPublicKey, ourKeyPair, alice));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -607,7 +648,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private long sampleRunningTime(int iterations) {
|
private long sampleRunningTime(int iterations) {
|
||||||
byte[] password = { 'p', 'a', 's', 's', 'w', 'o', 'r', 'd' };
|
byte[] password = {'p', 'a', 's', 's', 'w', 'o', 'r', 'd'};
|
||||||
byte[] salt = new byte[PBKDF_SALT_BYTES];
|
byte[] salt = new byte[PBKDF_SALT_BYTES];
|
||||||
int keyLengthInBits = SecretKey.LENGTH * 8;
|
int keyLengthInBits = SecretKey.LENGTH * 8;
|
||||||
long start = System.nanoTime();
|
long start = System.nanoTime();
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.TimeLoggingExecutor;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoExecutor;
|
import org.briarproject.bramble.api.crypto.CryptoExecutor;
|
||||||
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
|
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
|
||||||
import org.briarproject.bramble.api.crypto.StreamDecrypterFactory;
|
import org.briarproject.bramble.api.crypto.StreamDecrypterFactory;
|
||||||
import org.briarproject.bramble.api.crypto.StreamEncrypterFactory;
|
import org.briarproject.bramble.api.crypto.StreamEncrypterFactory;
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.api.system.SeedProvider;
|
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||||
|
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
@@ -31,14 +32,17 @@ public class CryptoModule {
|
|||||||
public static class EagerSingletons {
|
public static class EagerSingletons {
|
||||||
@Inject
|
@Inject
|
||||||
@CryptoExecutor
|
@CryptoExecutor
|
||||||
Executor cryptoExecutor;
|
ExecutorService cryptoExecutor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum number of executor threads.
|
* The maximum number of executor threads.
|
||||||
|
* <p>
|
||||||
|
* The number of available processors can change during the lifetime of the
|
||||||
|
* JVM, so this is just a reasonable guess.
|
||||||
*/
|
*/
|
||||||
private static final int MAX_EXECUTOR_THREADS =
|
private static final int MAX_EXECUTOR_THREADS =
|
||||||
Runtime.getRuntime().availableProcessors();
|
Math.max(1, Runtime.getRuntime().availableProcessors() - 1);
|
||||||
|
|
||||||
private final ExecutorService cryptoExecutor;
|
private final ExecutorService cryptoExecutor;
|
||||||
|
|
||||||
@@ -49,8 +53,8 @@ public class CryptoModule {
|
|||||||
RejectedExecutionHandler policy =
|
RejectedExecutionHandler policy =
|
||||||
new ThreadPoolExecutor.DiscardPolicy();
|
new ThreadPoolExecutor.DiscardPolicy();
|
||||||
// Create a limited # of threads and keep them in the pool for 60 secs
|
// Create a limited # of threads and keep them in the pool for 60 secs
|
||||||
cryptoExecutor = new ThreadPoolExecutor(0, MAX_EXECUTOR_THREADS,
|
cryptoExecutor = new TimeLoggingExecutor("CryptoExecutor", 0,
|
||||||
60, SECONDS, queue, policy);
|
MAX_EXECUTOR_THREADS, 60, SECONDS, queue, policy);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@@ -60,8 +64,9 @@ public class CryptoModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
CryptoComponent provideCryptoComponent(SeedProvider seedProvider) {
|
CryptoComponent provideCryptoComponent(
|
||||||
return new CryptoComponentImpl(seedProvider);
|
SecureRandomProvider secureRandomProvider) {
|
||||||
|
return new CryptoComponentImpl(secureRandomProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@@ -84,11 +89,18 @@ public class CryptoModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
@CryptoExecutor
|
@CryptoExecutor
|
||||||
Executor getCryptoExecutor(LifecycleManager lifecycleManager) {
|
ExecutorService getCryptoExecutorService(
|
||||||
|
LifecycleManager lifecycleManager) {
|
||||||
lifecycleManager.registerForShutdown(cryptoExecutor);
|
lifecycleManager.registerForShutdown(cryptoExecutor);
|
||||||
return cryptoExecutor;
|
return cryptoExecutor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@CryptoExecutor
|
||||||
|
Executor getCryptoExecutor() {
|
||||||
|
return cryptoExecutor;
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
SecureRandom getSecureRandom(CryptoComponent crypto) {
|
SecureRandom getSecureRandom(CryptoComponent crypto) {
|
||||||
return crypto.getSecureRandom();
|
return crypto.getSecureRandom();
|
||||||
|
|||||||
@@ -1,76 +0,0 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.spongycastle.crypto.Digest;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.NotThreadSafe;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A message digest that prevents length extension attacks - see Ferguson and
|
|
||||||
* Schneier, <i>Practical Cryptography</i>, chapter 6.
|
|
||||||
* <p>
|
|
||||||
* "Let h be an interative hash function. The hash function h<sub>d</sub> is
|
|
||||||
* defined by h<sub>d</sub> := h(h(m)), and has a claimed security level of
|
|
||||||
* min(k, n/2) where k is the security level of h and n is the size of the hash
|
|
||||||
* result."
|
|
||||||
*/
|
|
||||||
@NotThreadSafe
|
|
||||||
@NotNullByDefault
|
|
||||||
class DoubleDigest implements Digest {
|
|
||||||
|
|
||||||
private final Digest delegate;
|
|
||||||
|
|
||||||
DoubleDigest(Digest delegate) {
|
|
||||||
this.delegate = delegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] digest() {
|
|
||||||
byte[] digest = new byte[delegate.getDigestSize()];
|
|
||||||
delegate.doFinal(digest, 0); // h(m)
|
|
||||||
delegate.update(digest, 0, digest.length);
|
|
||||||
delegate.doFinal(digest, 0); // h(h(m))
|
|
||||||
return digest;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int digest(byte[] buf, int offset, int len) {
|
|
||||||
byte[] digest = digest();
|
|
||||||
len = Math.min(len, digest.length);
|
|
||||||
System.arraycopy(digest, 0, buf, offset, len);
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getDigestSize() {
|
|
||||||
return delegate.getDigestSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getAlgorithmName() {
|
|
||||||
return "Double " + delegate.getAlgorithmName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reset() {
|
|
||||||
delegate.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void update(byte input) {
|
|
||||||
delegate.update(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void update(byte[] input) {
|
|
||||||
delegate.update(input, 0, input.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void update(byte[] input, int offset, int len) {
|
|
||||||
delegate.update(input, offset, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int doFinal(byte[] out, int outOff) {
|
|
||||||
return digest(out, outOff, delegate.getDigestSize());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.spongycastle.crypto.BlockCipher;
|
|
||||||
import org.spongycastle.crypto.digests.SHA256Digest;
|
|
||||||
import org.spongycastle.crypto.engines.AESLightEngine;
|
|
||||||
import org.spongycastle.crypto.params.KeyParameter;
|
|
||||||
|
|
||||||
import java.util.concurrent.locks.Lock;
|
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements the Fortuna pseudo-random number generator, as described in
|
|
||||||
* Ferguson and Schneier, <i>Practical Cryptography</i>, chapter 9.
|
|
||||||
*/
|
|
||||||
@ThreadSafe
|
|
||||||
@NotNullByDefault
|
|
||||||
class FortunaGenerator {
|
|
||||||
|
|
||||||
private static final int MAX_BYTES_PER_REQUEST = 1024 * 1024;
|
|
||||||
private static final int KEY_BYTES = 32;
|
|
||||||
private static final int BLOCK_BYTES = 16;
|
|
||||||
|
|
||||||
private final Lock lock = new ReentrantLock();
|
|
||||||
|
|
||||||
// The following are locking: lock
|
|
||||||
private final DoubleDigest digest = new DoubleDigest(new SHA256Digest());
|
|
||||||
private final BlockCipher cipher = new AESLightEngine();
|
|
||||||
private final byte[] key = new byte[KEY_BYTES];
|
|
||||||
private final byte[] counter = new byte[BLOCK_BYTES];
|
|
||||||
private final byte[] buffer = new byte[BLOCK_BYTES];
|
|
||||||
private final byte[] newKey = new byte[KEY_BYTES];
|
|
||||||
|
|
||||||
FortunaGenerator(byte[] seed) {
|
|
||||||
reseed(seed);
|
|
||||||
}
|
|
||||||
|
|
||||||
void reseed(byte[] seed) {
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
digest.update(key);
|
|
||||||
digest.update(seed);
|
|
||||||
digest.digest(key, 0, KEY_BYTES);
|
|
||||||
incrementCounter();
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Package access for testing
|
|
||||||
void incrementCounter() {
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
counter[0]++;
|
|
||||||
for (int i = 0; counter[i] == 0; i++) {
|
|
||||||
if (i + 1 == BLOCK_BYTES)
|
|
||||||
throw new RuntimeException("Counter exhausted");
|
|
||||||
counter[i + 1]++;
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Package access for testing
|
|
||||||
byte[] getCounter() {
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
return counter;
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int nextBytes(byte[] dest, int off, int len) {
|
|
||||||
lock.lock();
|
|
||||||
try {
|
|
||||||
// Don't write more than the maximum number of bytes in one request
|
|
||||||
if (len > MAX_BYTES_PER_REQUEST) len = MAX_BYTES_PER_REQUEST;
|
|
||||||
cipher.init(true, new KeyParameter(key));
|
|
||||||
// Generate full blocks directly into the output buffer
|
|
||||||
int fullBlocks = len / BLOCK_BYTES;
|
|
||||||
for (int i = 0; i < fullBlocks; i++) {
|
|
||||||
cipher.processBlock(counter, 0, dest, off + i * BLOCK_BYTES);
|
|
||||||
incrementCounter();
|
|
||||||
}
|
|
||||||
// Generate a partial block if needed
|
|
||||||
int done = fullBlocks * BLOCK_BYTES, remaining = len - done;
|
|
||||||
if (remaining >= BLOCK_BYTES) throw new AssertionError();
|
|
||||||
if (remaining > 0) {
|
|
||||||
cipher.processBlock(counter, 0, buffer, 0);
|
|
||||||
incrementCounter();
|
|
||||||
// Copy the partial block to the output buffer and erase our copy
|
|
||||||
System.arraycopy(buffer, 0, dest, off + done, remaining);
|
|
||||||
for (int i = 0; i < BLOCK_BYTES; i++) buffer[i] = 0;
|
|
||||||
}
|
|
||||||
// Generate a new key
|
|
||||||
for (int i = 0; i < KEY_BYTES / BLOCK_BYTES; i++) {
|
|
||||||
cipher.processBlock(counter, 0, newKey, i * BLOCK_BYTES);
|
|
||||||
incrementCounter();
|
|
||||||
}
|
|
||||||
System.arraycopy(newKey, 0, key, 0, KEY_BYTES);
|
|
||||||
for (int i = 0; i < KEY_BYTES; i++) newKey[i] = 0;
|
|
||||||
// Return the number of bytes written
|
|
||||||
return len;
|
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
|
||||||
|
|
||||||
import java.security.Provider;
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.security.SecureRandomSpi;
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link java.security.SecureRandom SecureRandom} implementation based on a
|
|
||||||
* {@link FortunaGenerator}.
|
|
||||||
*/
|
|
||||||
class FortunaSecureRandom extends SecureRandom {
|
|
||||||
|
|
||||||
// Package access for testing
|
|
||||||
static final byte[] SELF_TEST_VECTOR_1 =
|
|
||||||
StringUtils.fromHexString("4BD6EA599D47E3EE9DD911833C29CA22");
|
|
||||||
static final byte[] SELF_TEST_VECTOR_2 =
|
|
||||||
StringUtils.fromHexString("10984D576E6850E505CA9F42A9BFD88A");
|
|
||||||
static final byte[] SELF_TEST_VECTOR_3 =
|
|
||||||
StringUtils.fromHexString("1E12DA166BD86DCECDE50A8296018DE2");
|
|
||||||
|
|
||||||
private static final Provider PROVIDER = new FortunaProvider();
|
|
||||||
|
|
||||||
FortunaSecureRandom(byte[] seed) {
|
|
||||||
super(new FortunaSecureRandomSpi(seed), PROVIDER);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests that the {@link #nextBytes(byte[])} and {@link #setSeed(byte[])}
|
|
||||||
* methods are passed through to the generator in the expected way.
|
|
||||||
*/
|
|
||||||
static boolean selfTest() {
|
|
||||||
byte[] seed = new byte[32];
|
|
||||||
SecureRandom r = new FortunaSecureRandom(seed);
|
|
||||||
byte[] output = new byte[16];
|
|
||||||
r.nextBytes(output);
|
|
||||||
if (!Arrays.equals(SELF_TEST_VECTOR_1, output)) return false;
|
|
||||||
r.nextBytes(output);
|
|
||||||
if (!Arrays.equals(SELF_TEST_VECTOR_2, output)) return false;
|
|
||||||
r.setSeed(seed);
|
|
||||||
r.nextBytes(output);
|
|
||||||
return Arrays.equals(SELF_TEST_VECTOR_3, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class FortunaSecureRandomSpi extends SecureRandomSpi {
|
|
||||||
|
|
||||||
private final FortunaGenerator generator;
|
|
||||||
|
|
||||||
private FortunaSecureRandomSpi(byte[] seed) {
|
|
||||||
generator = new FortunaGenerator(seed);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected byte[] engineGenerateSeed(int numBytes) {
|
|
||||||
byte[] b = new byte[numBytes];
|
|
||||||
engineNextBytes(b);
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void engineNextBytes(byte[] b) {
|
|
||||||
int offset = 0;
|
|
||||||
while (offset < b.length)
|
|
||||||
offset += generator.nextBytes(b, offset, b.length - offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void engineSetSeed(byte[] seed) {
|
|
||||||
generator.reseed(seed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class FortunaProvider extends Provider {
|
|
||||||
|
|
||||||
private FortunaProvider() {
|
|
||||||
super("Fortuna", 1.0, "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -11,31 +11,14 @@ import javax.annotation.concurrent.Immutable;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class PasswordStrengthEstimatorImpl implements PasswordStrengthEstimator {
|
class PasswordStrengthEstimatorImpl implements PasswordStrengthEstimator {
|
||||||
|
|
||||||
private static final int LOWER = 26;
|
// The minimum number of unique characters in a strong password
|
||||||
private static final int UPPER = 26;
|
private static final int STRONG_UNIQUE_CHARS = 12;
|
||||||
private static final int DIGIT = 10;
|
|
||||||
private static final int OTHER = 10;
|
|
||||||
private static final double STRONG = Math.log(Math.pow(LOWER + UPPER +
|
|
||||||
DIGIT + OTHER, 10));
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float estimateStrength(String password) {
|
public float estimateStrength(String password) {
|
||||||
HashSet<Character> unique = new HashSet<Character>();
|
HashSet<Character> unique = new HashSet<Character>();
|
||||||
int length = password.length();
|
int length = password.length();
|
||||||
for (int i = 0; i < length; i++) unique.add(password.charAt(i));
|
for (int i = 0; i < length; i++) unique.add(password.charAt(i));
|
||||||
boolean lower = false, upper = false, digit = false, other = false;
|
return Math.min(1, (float) unique.size() / STRONG_UNIQUE_CHARS);
|
||||||
for (char c : unique) {
|
|
||||||
if (Character.isLowerCase(c)) lower = true;
|
|
||||||
else if (Character.isUpperCase(c)) upper = true;
|
|
||||||
else if (Character.isDigit(c)) digit = true;
|
|
||||||
else other = true;
|
|
||||||
}
|
|
||||||
int alphabetSize = 0;
|
|
||||||
if (lower) alphabetSize += LOWER;
|
|
||||||
if (upper) alphabetSize += UPPER;
|
|
||||||
if (digit) alphabetSize += DIGIT;
|
|
||||||
if (other) alphabetSize += OTHER;
|
|
||||||
double score = Math.log(Math.pow(alphabetSize, unique.size()));
|
|
||||||
return Math.min(1, (float) (score / STRONG));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,30 +2,34 @@ package org.briarproject.bramble.crypto;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.PseudoRandom;
|
import org.briarproject.bramble.api.crypto.PseudoRandom;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.util.ByteUtils;
|
import org.spongycastle.crypto.Digest;
|
||||||
|
import org.spongycastle.crypto.engines.Salsa20Engine;
|
||||||
|
import org.spongycastle.crypto.params.KeyParameter;
|
||||||
|
import org.spongycastle.crypto.params.ParametersWithIV;
|
||||||
|
|
||||||
import javax.annotation.concurrent.NotThreadSafe;
|
import javax.annotation.concurrent.NotThreadSafe;
|
||||||
|
|
||||||
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
|
|
||||||
|
|
||||||
@NotThreadSafe
|
@NotThreadSafe
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class PseudoRandomImpl implements PseudoRandom {
|
class PseudoRandomImpl implements PseudoRandom {
|
||||||
|
|
||||||
private final FortunaGenerator generator;
|
private final Salsa20Engine cipher = new Salsa20Engine();
|
||||||
|
|
||||||
PseudoRandomImpl(int seed1, int seed2) {
|
PseudoRandomImpl(byte[] seed) {
|
||||||
byte[] seed = new byte[INT_32_BYTES * 2];
|
// Hash the seed to produce a 32-byte key
|
||||||
ByteUtils.writeUint32(seed1, seed, 0);
|
byte[] key = new byte[32];
|
||||||
ByteUtils.writeUint32(seed2, seed, INT_32_BYTES);
|
Digest digest = new Blake2sDigest();
|
||||||
generator = new FortunaGenerator(seed);
|
digest.update(seed, 0, seed.length);
|
||||||
|
digest.doFinal(key, 0);
|
||||||
|
// Initialise the stream cipher with an all-zero nonce
|
||||||
|
byte[] nonce = new byte[8];
|
||||||
|
cipher.init(true, new ParametersWithIV(new KeyParameter(key), nonce));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] nextBytes(int length) {
|
public byte[] nextBytes(int length) {
|
||||||
byte[] b = new byte[length];
|
byte[] in = new byte[length], out = new byte[length];
|
||||||
int offset = 0;
|
cipher.processBytes(in, 0, length, out, 0);
|
||||||
while (offset < length) offset += generator.nextBytes(b, offset, length);
|
return out;
|
||||||
return b;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,6 +62,8 @@ class StreamEncrypterImpl implements StreamEncrypter {
|
|||||||
@Override
|
@Override
|
||||||
public void writeFrame(byte[] payload, int payloadLength,
|
public void writeFrame(byte[] payload, int payloadLength,
|
||||||
int paddingLength, boolean finalFrame) throws IOException {
|
int paddingLength, boolean finalFrame) throws IOException {
|
||||||
|
if (payloadLength < 0 || paddingLength < 0)
|
||||||
|
throw new IllegalArgumentException();
|
||||||
if (payloadLength + paddingLength > MAX_PAYLOAD_LENGTH)
|
if (payloadLength + paddingLength > MAX_PAYLOAD_LENGTH)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
// Don't allow the frame counter to wrap
|
// Don't allow the frame counter to wrap
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ import javax.annotation.Nullable;
|
|||||||
import javax.annotation.concurrent.ThreadSafe;
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.FINE;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
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;
|
||||||
@@ -130,8 +131,14 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
// Don't allow reentrant locking
|
// Don't allow reentrant locking
|
||||||
if (lock.getReadHoldCount() > 0) throw new IllegalStateException();
|
if (lock.getReadHoldCount() > 0) throw new IllegalStateException();
|
||||||
if (lock.getWriteHoldCount() > 0) throw new IllegalStateException();
|
if (lock.getWriteHoldCount() > 0) throw new IllegalStateException();
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
if (readOnly) lock.readLock().lock();
|
if (readOnly) lock.readLock().lock();
|
||||||
else lock.writeLock().lock();
|
else lock.writeLock().lock();
|
||||||
|
if (LOG.isLoggable(FINE)) {
|
||||||
|
long duration = System.currentTimeMillis() - start;
|
||||||
|
if (readOnly) LOG.fine("Waited " + duration + " ms for read lock");
|
||||||
|
else LOG.fine("Waited " + duration + " ms for write lock");
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
return new Transaction(db.startTransaction(), readOnly);
|
return new Transaction(db.startTransaction(), readOnly);
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.db;
|
package org.briarproject.bramble.db;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.TimeLoggingExecutor;
|
||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
|
|
||||||
@@ -36,8 +37,8 @@ public class DatabaseExecutorModule {
|
|||||||
RejectedExecutionHandler policy =
|
RejectedExecutionHandler policy =
|
||||||
new ThreadPoolExecutor.DiscardPolicy();
|
new ThreadPoolExecutor.DiscardPolicy();
|
||||||
// Use a single thread and keep it in the pool for 60 secs
|
// Use a single thread and keep it in the pool for 60 secs
|
||||||
databaseExecutor = new ThreadPoolExecutor(0, 1, 60, SECONDS, queue,
|
databaseExecutor = new TimeLoggingExecutor("DatabaseExecutor", 0, 1,
|
||||||
policy);
|
60, SECONDS, queue, policy);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import static java.util.logging.Level.INFO;
|
|||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_LAN;
|
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_LAN;
|
||||||
import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID;
|
import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID;
|
||||||
|
import static org.briarproject.bramble.api.plugin.LanTcpConstants.PREF_LAN_IP_PORTS;
|
||||||
import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED;
|
import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED;
|
||||||
import static org.briarproject.bramble.util.PrivacyUtils.scrubSocketAddress;
|
import static org.briarproject.bramble.util.PrivacyUtils.scrubSocketAddress;
|
||||||
|
|
||||||
@@ -43,7 +44,7 @@ class LanTcpPlugin extends TcpPlugin {
|
|||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(LanTcpPlugin.class.getName());
|
Logger.getLogger(LanTcpPlugin.class.getName());
|
||||||
|
|
||||||
private static final int MAX_ADDRESSES = 5;
|
private static final int MAX_ADDRESSES = 4;
|
||||||
private static final String PROP_IP_PORTS = "ipPorts";
|
private static final String PROP_IP_PORTS = "ipPorts";
|
||||||
private static final String SEPARATOR = ",";
|
private static final String SEPARATOR = ",";
|
||||||
|
|
||||||
@@ -82,19 +83,19 @@ class LanTcpPlugin extends TcpPlugin {
|
|||||||
private List<InetSocketAddress> parseSocketAddresses(String ipPorts) {
|
private List<InetSocketAddress> parseSocketAddresses(String ipPorts) {
|
||||||
if (StringUtils.isNullOrEmpty(ipPorts)) return Collections.emptyList();
|
if (StringUtils.isNullOrEmpty(ipPorts)) return Collections.emptyList();
|
||||||
String[] split = ipPorts.split(SEPARATOR);
|
String[] split = ipPorts.split(SEPARATOR);
|
||||||
List<InetSocketAddress> remotes = new ArrayList<InetSocketAddress>();
|
List<InetSocketAddress> addresses = new ArrayList<InetSocketAddress>();
|
||||||
for (String ipPort : split) {
|
for (String ipPort : split) {
|
||||||
InetSocketAddress a = parseSocketAddress(ipPort);
|
InetSocketAddress a = parseSocketAddress(ipPort);
|
||||||
if (a != null) remotes.add(a);
|
if (a != null) addresses.add(a);
|
||||||
}
|
}
|
||||||
return remotes;
|
return addresses;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void setLocalSocketAddress(InetSocketAddress a) {
|
protected void setLocalSocketAddress(InetSocketAddress a) {
|
||||||
String ipPort = getIpPortString(a);
|
String ipPort = getIpPortString(a);
|
||||||
// Get the list of recently used addresses
|
// Get the list of recently used addresses
|
||||||
String setting = callback.getSettings().get(PROP_IP_PORTS);
|
String setting = callback.getSettings().get(PREF_LAN_IP_PORTS);
|
||||||
List<String> recent = new ArrayList<String>();
|
List<String> recent = new ArrayList<String>();
|
||||||
if (!StringUtils.isNullOrEmpty(setting))
|
if (!StringUtils.isNullOrEmpty(setting))
|
||||||
Collections.addAll(recent, setting.split(SEPARATOR));
|
Collections.addAll(recent, setting.split(SEPARATOR));
|
||||||
@@ -120,7 +121,7 @@ class LanTcpPlugin extends TcpPlugin {
|
|||||||
}
|
}
|
||||||
// Save the setting
|
// Save the setting
|
||||||
Settings settings = new Settings();
|
Settings settings = new Settings();
|
||||||
settings.put(PROP_IP_PORTS, setting);
|
settings.put(PREF_LAN_IP_PORTS, setting);
|
||||||
callback.mergeSettings(settings);
|
callback.mergeSettings(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,12 +6,28 @@ import org.briarproject.bramble.util.IoUtils;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.net.InetAddress;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
class SocksSocket extends Socket {
|
class SocksSocket extends Socket {
|
||||||
|
|
||||||
|
private static final String[] ERRORS = {
|
||||||
|
"Succeeded",
|
||||||
|
"General SOCKS server failure",
|
||||||
|
"Connection not allowed by ruleset",
|
||||||
|
"Network unreachable",
|
||||||
|
"Host unreachable",
|
||||||
|
"Connection refused",
|
||||||
|
"TTL expired",
|
||||||
|
"Command not supported",
|
||||||
|
"Address type not supported"
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final byte[] UNSPECIFIED_ADDRESS = new byte[4];
|
||||||
|
|
||||||
private final SocketAddress proxy;
|
private final SocketAddress proxy;
|
||||||
private final int connectToProxyTimeout;
|
private final int connectToProxyTimeout;
|
||||||
|
|
||||||
@@ -28,6 +44,11 @@ class SocksSocket extends Socket {
|
|||||||
if (!(endpoint instanceof InetSocketAddress))
|
if (!(endpoint instanceof InetSocketAddress))
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
InetSocketAddress inet = (InetSocketAddress) endpoint;
|
InetSocketAddress inet = (InetSocketAddress) endpoint;
|
||||||
|
InetAddress address = inet.getAddress();
|
||||||
|
if (address != null
|
||||||
|
&& !Arrays.equals(address.getAddress(), UNSPECIFIED_ADDRESS)) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
String host = inet.getHostName();
|
String host = inet.getHostName();
|
||||||
if (host.length() > 255) throw new IllegalArgumentException();
|
if (host.length() > 255) throw new IllegalArgumentException();
|
||||||
int port = inet.getPort();
|
int port = inet.getPort();
|
||||||
@@ -93,13 +114,16 @@ class SocksSocket extends Socket {
|
|||||||
private void receiveConnectResponse(InputStream in) throws IOException {
|
private void receiveConnectResponse(InputStream in) throws IOException {
|
||||||
byte[] connectResponse = new byte[4];
|
byte[] connectResponse = new byte[4];
|
||||||
IoUtils.read(in, connectResponse);
|
IoUtils.read(in, connectResponse);
|
||||||
byte version = connectResponse[0];
|
int version = connectResponse[0] & 0xFF;
|
||||||
byte reply = connectResponse[1];
|
int reply = connectResponse[1] & 0xFF;
|
||||||
byte addressType = connectResponse[3];
|
int addressType = connectResponse[3] & 0xFF;
|
||||||
if (version != 5)
|
if (version != 5)
|
||||||
throw new IOException("Unsupported SOCKS version: " + version);
|
throw new IOException("Unsupported SOCKS version: " + version);
|
||||||
if (reply != 0)
|
if (reply != 0) {
|
||||||
throw new IOException("Connection failed: " + reply);
|
if (reply < ERRORS.length)
|
||||||
|
throw new IOException("Connection failed: " + ERRORS[reply]);
|
||||||
|
else throw new IOException("Connection failed: " + reply);
|
||||||
|
}
|
||||||
if (addressType == 1) IoUtils.read(in, new byte[4]); // IPv4
|
if (addressType == 1) IoUtils.read(in, new byte[4]); // IPv4
|
||||||
else if (addressType == 4) IoUtils.read(in, new byte[16]); // IPv6
|
else if (addressType == 4) IoUtils.read(in, new byte[16]); // IPv6
|
||||||
else throw new IOException("Unsupported address type: " + addressType);
|
else throw new IOException("Unsupported address type: " + addressType);
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package org.briarproject.bramble.sync;
|
package org.briarproject.bramble.sync;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.PoliteExecutor;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
|
import org.briarproject.bramble.api.crypto.CryptoExecutor;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
@@ -29,6 +31,16 @@ public class SyncModule {
|
|||||||
ValidationManager validationManager;
|
ValidationManager validationManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum number of validation tasks to delegate to the crypto
|
||||||
|
* executor concurrently.
|
||||||
|
* <p>
|
||||||
|
* The number of available processors can change during the lifetime of the
|
||||||
|
* JVM, so this is just a reasonable guess.
|
||||||
|
*/
|
||||||
|
private static final int MAX_CONCURRENT_VALIDATION_TASKS =
|
||||||
|
Math.max(1, Runtime.getRuntime().availableProcessors() - 1);
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
GroupFactory provideGroupFactory(CryptoComponent crypto) {
|
GroupFactory provideGroupFactory(CryptoComponent crypto) {
|
||||||
return new GroupFactoryImpl(crypto);
|
return new GroupFactoryImpl(crypto);
|
||||||
@@ -62,10 +74,20 @@ public class SyncModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
ValidationManager getValidationManager(LifecycleManager lifecycleManager,
|
ValidationManager provideValidationManager(
|
||||||
EventBus eventBus, ValidationManagerImpl validationManager) {
|
LifecycleManager lifecycleManager, EventBus eventBus,
|
||||||
|
ValidationManagerImpl validationManager) {
|
||||||
lifecycleManager.registerService(validationManager);
|
lifecycleManager.registerService(validationManager);
|
||||||
eventBus.addListener(validationManager);
|
eventBus.addListener(validationManager);
|
||||||
return validationManager;
|
return validationManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
@ValidationExecutor
|
||||||
|
Executor provideValidationExecutor(
|
||||||
|
@CryptoExecutor Executor cryptoExecutor) {
|
||||||
|
return new PoliteExecutor("ValidationExecutor", cryptoExecutor,
|
||||||
|
MAX_CONCURRENT_VALIDATION_TASKS);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package org.briarproject.bramble.sync;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import javax.inject.Qualifier;
|
||||||
|
|
||||||
|
import static java.lang.annotation.ElementType.FIELD;
|
||||||
|
import static java.lang.annotation.ElementType.METHOD;
|
||||||
|
import static java.lang.annotation.ElementType.PARAMETER;
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotation for injecting the executor for validation tasks. Also used for
|
||||||
|
* annotating methods that should run on the validation executor.
|
||||||
|
* <p>
|
||||||
|
* The contract of this executor is that tasks may be run concurrently, and
|
||||||
|
* submitting a task will never block. Tasks must not run indefinitely. Tasks
|
||||||
|
* submitted during shutdown are discarded.
|
||||||
|
*/
|
||||||
|
@Qualifier
|
||||||
|
@Target({FIELD, METHOD, PARAMETER})
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
@interface ValidationExecutor {
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.briarproject.bramble.sync;
|
package org.briarproject.bramble.sync;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoExecutor;
|
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
@@ -50,8 +49,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
Logger.getLogger(ValidationManagerImpl.class.getName());
|
Logger.getLogger(ValidationManagerImpl.class.getName());
|
||||||
|
|
||||||
private final DatabaseComponent db;
|
private final DatabaseComponent db;
|
||||||
private final Executor dbExecutor;
|
private final Executor dbExecutor, validationExecutor;
|
||||||
private final Executor cryptoExecutor;
|
|
||||||
private final MessageFactory messageFactory;
|
private final MessageFactory messageFactory;
|
||||||
private final Map<ClientId, MessageValidator> validators;
|
private final Map<ClientId, MessageValidator> validators;
|
||||||
private final Map<ClientId, IncomingMessageHook> hooks;
|
private final Map<ClientId, IncomingMessageHook> hooks;
|
||||||
@@ -60,11 +58,11 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
@Inject
|
@Inject
|
||||||
ValidationManagerImpl(DatabaseComponent db,
|
ValidationManagerImpl(DatabaseComponent db,
|
||||||
@DatabaseExecutor Executor dbExecutor,
|
@DatabaseExecutor Executor dbExecutor,
|
||||||
@CryptoExecutor Executor cryptoExecutor,
|
@ValidationExecutor Executor validationExecutor,
|
||||||
MessageFactory messageFactory) {
|
MessageFactory messageFactory) {
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.dbExecutor = dbExecutor;
|
this.dbExecutor = dbExecutor;
|
||||||
this.cryptoExecutor = cryptoExecutor;
|
this.validationExecutor = validationExecutor;
|
||||||
this.messageFactory = messageFactory;
|
this.messageFactory = messageFactory;
|
||||||
validators = new ConcurrentHashMap<ClientId, MessageValidator>();
|
validators = new ConcurrentHashMap<ClientId, MessageValidator>();
|
||||||
hooks = new ConcurrentHashMap<ClientId, IncomingMessageHook>();
|
hooks = new ConcurrentHashMap<ClientId, IncomingMessageHook>();
|
||||||
@@ -104,6 +102,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DatabaseExecutor
|
||||||
private void validateOutstandingMessages(ClientId c) {
|
private void validateOutstandingMessages(ClientId c) {
|
||||||
try {
|
try {
|
||||||
Queue<MessageId> unvalidated = new LinkedList<MessageId>();
|
Queue<MessageId> unvalidated = new LinkedList<MessageId>();
|
||||||
@@ -130,6 +129,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DatabaseExecutor
|
||||||
private void validateNextMessage(Queue<MessageId> unvalidated) {
|
private void validateNextMessage(Queue<MessageId> unvalidated) {
|
||||||
try {
|
try {
|
||||||
Message m;
|
Message m;
|
||||||
@@ -167,6 +167,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DatabaseExecutor
|
||||||
private void deliverOutstandingMessages(ClientId c) {
|
private void deliverOutstandingMessages(ClientId c) {
|
||||||
try {
|
try {
|
||||||
Queue<MessageId> pending = new LinkedList<MessageId>();
|
Queue<MessageId> pending = new LinkedList<MessageId>();
|
||||||
@@ -194,6 +195,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DatabaseExecutor
|
||||||
private void deliverNextPendingMessage(Queue<MessageId> pending) {
|
private void deliverNextPendingMessage(Queue<MessageId> pending) {
|
||||||
try {
|
try {
|
||||||
boolean anyInvalid = false, allDelivered = true;
|
boolean anyInvalid = false, allDelivered = true;
|
||||||
@@ -220,8 +222,8 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
Message m = messageFactory.createMessage(id, raw);
|
Message m = messageFactory.createMessage(id, raw);
|
||||||
Group g = db.getGroup(txn, m.getGroupId());
|
Group g = db.getGroup(txn, m.getGroupId());
|
||||||
ClientId c = g.getClientId();
|
ClientId c = g.getClientId();
|
||||||
Metadata meta = db.getMessageMetadataForValidator(txn,
|
Metadata meta =
|
||||||
id);
|
db.getMessageMetadataForValidator(txn, id);
|
||||||
DeliveryResult result = deliverMessage(txn, m, c, meta);
|
DeliveryResult result = deliverMessage(txn, m, c, meta);
|
||||||
if (result.valid) {
|
if (result.valid) {
|
||||||
pending.addAll(getPendingDependents(txn, id));
|
pending.addAll(getPendingDependents(txn, id));
|
||||||
@@ -240,8 +242,8 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
db.endTransaction(txn);
|
db.endTransaction(txn);
|
||||||
}
|
}
|
||||||
if (invalidate != null) invalidateNextMessageAsync(invalidate);
|
if (invalidate != null) invalidateNextMessageAsync(invalidate);
|
||||||
deliverNextPendingMessageAsync(pending);
|
|
||||||
if (toShare != null) shareNextMessageAsync(toShare);
|
if (toShare != null) shareNextMessageAsync(toShare);
|
||||||
|
deliverNextPendingMessageAsync(pending);
|
||||||
} catch (NoSuchMessageException e) {
|
} catch (NoSuchMessageException e) {
|
||||||
LOG.info("Message removed before delivery");
|
LOG.info("Message removed before delivery");
|
||||||
deliverNextPendingMessageAsync(pending);
|
deliverNextPendingMessageAsync(pending);
|
||||||
@@ -249,13 +251,12 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
LOG.info("Group removed before delivery");
|
LOG.info("Group removed before delivery");
|
||||||
deliverNextPendingMessageAsync(pending);
|
deliverNextPendingMessageAsync(pending);
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
if (LOG.isLoggable(WARNING))
|
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
LOG.log(WARNING, e.toString(), e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateMessageAsync(final Message m, final Group g) {
|
private void validateMessageAsync(final Message m, final Group g) {
|
||||||
cryptoExecutor.execute(new Runnable() {
|
validationExecutor.execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
validateMessage(m, g);
|
validateMessage(m, g);
|
||||||
@@ -263,10 +264,12 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ValidationExecutor
|
||||||
private void validateMessage(Message m, Group g) {
|
private void validateMessage(Message m, Group g) {
|
||||||
MessageValidator v = validators.get(g.getClientId());
|
MessageValidator v = validators.get(g.getClientId());
|
||||||
if (v == null) {
|
if (v == null) {
|
||||||
LOG.warning("No validator");
|
if (LOG.isLoggable(WARNING))
|
||||||
|
LOG.warning("No validator for " + g.getClientId().getString());
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
MessageContext context = v.validateMessage(m, g);
|
MessageContext context = v.validateMessage(m, g);
|
||||||
@@ -291,6 +294,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DatabaseExecutor
|
||||||
private void storeMessageContext(Message m, ClientId c,
|
private void storeMessageContext(Message m, ClientId c,
|
||||||
MessageContext context) {
|
MessageContext context) {
|
||||||
try {
|
try {
|
||||||
@@ -353,6 +357,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DatabaseExecutor
|
||||||
private DeliveryResult deliverMessage(Transaction txn, Message m,
|
private DeliveryResult deliverMessage(Transaction txn, Message m,
|
||||||
ClientId c, Metadata meta) throws DbException {
|
ClientId c, Metadata meta) throws DbException {
|
||||||
// Deliver the message to the client if it's registered a hook
|
// Deliver the message to the client if it's registered a hook
|
||||||
@@ -362,10 +367,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
try {
|
try {
|
||||||
shareMsg = hook.incomingMessage(txn, m, meta);
|
shareMsg = hook.incomingMessage(txn, m, meta);
|
||||||
} catch (InvalidMessageException e) {
|
} catch (InvalidMessageException e) {
|
||||||
// message is invalid, mark it as such and delete it
|
invalidateMessage(txn, m.getId());
|
||||||
db.setMessageState(txn, m.getId(), INVALID);
|
|
||||||
db.deleteMessageMetadata(txn, m.getId());
|
|
||||||
db.deleteMessage(txn, m.getId());
|
|
||||||
return new DeliveryResult(false, false);
|
return new DeliveryResult(false, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -373,6 +375,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
return new DeliveryResult(true, shareMsg);
|
return new DeliveryResult(true, shareMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DatabaseExecutor
|
||||||
private Queue<MessageId> getPendingDependents(Transaction txn, MessageId m)
|
private Queue<MessageId> getPendingDependents(Transaction txn, MessageId m)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
Queue<MessageId> pending = new LinkedList<MessageId>();
|
Queue<MessageId> pending = new LinkedList<MessageId>();
|
||||||
@@ -392,6 +395,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DatabaseExecutor
|
||||||
private void shareOutstandingMessages(ClientId c) {
|
private void shareOutstandingMessages(ClientId c) {
|
||||||
try {
|
try {
|
||||||
Queue<MessageId> toShare = new LinkedList<MessageId>();
|
Queue<MessageId> toShare = new LinkedList<MessageId>();
|
||||||
@@ -424,6 +428,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DatabaseExecutor
|
||||||
private void shareNextMessage(Queue<MessageId> toShare) {
|
private void shareNextMessage(Queue<MessageId> toShare) {
|
||||||
try {
|
try {
|
||||||
Transaction txn = db.startTransaction(false);
|
Transaction txn = db.startTransaction(false);
|
||||||
@@ -457,6 +462,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DatabaseExecutor
|
||||||
private void invalidateNextMessage(Queue<MessageId> invalidate) {
|
private void invalidateNextMessage(Queue<MessageId> invalidate) {
|
||||||
try {
|
try {
|
||||||
Transaction txn = db.startTransaction(false);
|
Transaction txn = db.startTransaction(false);
|
||||||
@@ -479,6 +485,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DatabaseExecutor
|
||||||
private void invalidateMessage(Transaction txn, MessageId m)
|
private void invalidateMessage(Transaction txn, MessageId m)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
db.setMessageState(txn, m, INVALID);
|
db.setMessageState(txn, m, INVALID);
|
||||||
@@ -486,6 +493,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
db.deleteMessageMetadata(txn, m);
|
db.deleteMessageMetadata(txn, m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DatabaseExecutor
|
||||||
private Queue<MessageId> getDependentsToInvalidate(Transaction txn,
|
private Queue<MessageId> getDependentsToInvalidate(Transaction txn,
|
||||||
MessageId m) throws DbException {
|
MessageId m) throws DbException {
|
||||||
Queue<MessageId> invalidate = new LinkedList<MessageId>();
|
Queue<MessageId> invalidate = new LinkedList<MessageId>();
|
||||||
@@ -515,6 +523,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DatabaseExecutor
|
||||||
private void loadGroupAndValidate(final Message m) {
|
private void loadGroupAndValidate(final Message m) {
|
||||||
try {
|
try {
|
||||||
Group g;
|
Group g;
|
||||||
@@ -534,6 +543,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static class DeliveryResult {
|
private static class DeliveryResult {
|
||||||
|
|
||||||
private final boolean valid, share;
|
private final boolean valid, share;
|
||||||
|
|
||||||
private DeliveryResult(boolean valid, boolean share) {
|
private DeliveryResult(boolean valid, boolean share) {
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package org.briarproject.bramble.system;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||||
|
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.NetworkInterface;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
abstract class AbstractSecureRandomProvider implements SecureRandomProvider {
|
||||||
|
|
||||||
|
// Contribute whatever slightly unpredictable info we have to the pool
|
||||||
|
protected void writeToEntropyPool(DataOutputStream out) throws IOException {
|
||||||
|
out.writeLong(System.currentTimeMillis());
|
||||||
|
out.writeLong(System.nanoTime());
|
||||||
|
out.writeLong(Runtime.getRuntime().freeMemory());
|
||||||
|
List<NetworkInterface> ifaces =
|
||||||
|
Collections.list(NetworkInterface.getNetworkInterfaces());
|
||||||
|
for (NetworkInterface i : ifaces) {
|
||||||
|
List<InetAddress> addrs = Collections.list(i.getInetAddresses());
|
||||||
|
for (InetAddress a : addrs) out.write(a.getAddress());
|
||||||
|
byte[] hardware = i.getHardwareAddress();
|
||||||
|
if (hardware != null) out.write(hardware);
|
||||||
|
}
|
||||||
|
for (Entry<String, String> e : System.getenv().entrySet()) {
|
||||||
|
out.writeUTF(e.getKey());
|
||||||
|
out.writeUTF(e.getValue());
|
||||||
|
}
|
||||||
|
Properties properties = System.getProperties();
|
||||||
|
for (String key : properties.stringPropertyNames())
|
||||||
|
out.writeUTF(properties.getProperty(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package org.briarproject.bramble.system;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.Provider;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.WARNING;
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
class LinuxSecureRandomProvider extends AbstractSecureRandomProvider {
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
Logger.getLogger(LinuxSecureRandomProvider.class.getName());
|
||||||
|
|
||||||
|
private static final File RANDOM_DEVICE = new File("/dev/urandom");
|
||||||
|
|
||||||
|
private final AtomicBoolean seeded = new AtomicBoolean(false);
|
||||||
|
private final File outputDevice;
|
||||||
|
|
||||||
|
LinuxSecureRandomProvider() {
|
||||||
|
this(RANDOM_DEVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
LinuxSecureRandomProvider(File outputDevice) {
|
||||||
|
this.outputDevice = outputDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Provider getProvider() {
|
||||||
|
if (!seeded.getAndSet(true)) writeSeed();
|
||||||
|
return new LinuxProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void writeSeed() {
|
||||||
|
try {
|
||||||
|
DataOutputStream out = new DataOutputStream(
|
||||||
|
new FileOutputStream(outputDevice));
|
||||||
|
writeToEntropyPool(out);
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// On some devices /dev/urandom isn't writable - this isn't fatal
|
||||||
|
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
|
||||||
|
private static class LinuxProvider extends Provider {
|
||||||
|
|
||||||
|
private LinuxProvider() {
|
||||||
|
super("LinuxPRNG", 1.1, "A Linux-specific PRNG using /dev/urandom");
|
||||||
|
// Although /dev/urandom is not a SHA-1 PRNG, some callers
|
||||||
|
// explicitly request a SHA1PRNG SecureRandom and we need to
|
||||||
|
// prevent them from getting the default implementation whose
|
||||||
|
// output may have low entropy.
|
||||||
|
put("SecureRandom.SHA1PRNG", LinuxSecureRandomSpi.class.getName());
|
||||||
|
put("SecureRandom.SHA1PRNG ImplementedIn", "Software");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package org.briarproject.bramble.system;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.SecureRandomSpi;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.WARNING;
|
||||||
|
|
||||||
|
public class LinuxSecureRandomSpi extends SecureRandomSpi {
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
Logger.getLogger(LinuxSecureRandomSpi.class.getName());
|
||||||
|
|
||||||
|
private static final File RANDOM_DEVICE = new File("/dev/urandom");
|
||||||
|
|
||||||
|
private final File inputDevice, outputDevice;
|
||||||
|
|
||||||
|
public LinuxSecureRandomSpi() {
|
||||||
|
this(RANDOM_DEVICE, RANDOM_DEVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
LinuxSecureRandomSpi(File inputDevice, File outputDevice) {
|
||||||
|
this.inputDevice = inputDevice;
|
||||||
|
this.outputDevice = outputDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void engineSetSeed(byte[] seed) {
|
||||||
|
try {
|
||||||
|
DataOutputStream out = new DataOutputStream(
|
||||||
|
new FileOutputStream(outputDevice));
|
||||||
|
out.write(seed);
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// On some devices /dev/urandom isn't writable - this isn't fatal
|
||||||
|
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void engineNextBytes(byte[] bytes) {
|
||||||
|
try {
|
||||||
|
DataInputStream in = new DataInputStream(
|
||||||
|
new FileInputStream(inputDevice));
|
||||||
|
in.readFully(bytes);
|
||||||
|
in.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected byte[] engineGenerateSeed(int len) {
|
||||||
|
byte[] seed = new byte[len];
|
||||||
|
engineNextBytes(seed);
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
package org.briarproject.bramble.system;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.system.SeedProvider;
|
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.NetworkInterface;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
@NotNullByDefault
|
|
||||||
class LinuxSeedProvider implements SeedProvider {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
Logger.getLogger(LinuxSeedProvider.class.getName());
|
|
||||||
|
|
||||||
private final String outputFile, inputFile;
|
|
||||||
|
|
||||||
LinuxSeedProvider() {
|
|
||||||
this("/dev/urandom", "/dev/urandom");
|
|
||||||
}
|
|
||||||
|
|
||||||
LinuxSeedProvider(String outputFile, String inputFile) {
|
|
||||||
this.outputFile = outputFile;
|
|
||||||
this.inputFile = inputFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] getSeed() {
|
|
||||||
byte[] seed = new byte[SEED_BYTES];
|
|
||||||
// Contribute whatever slightly unpredictable info we have to the pool
|
|
||||||
try {
|
|
||||||
DataOutputStream out = new DataOutputStream(
|
|
||||||
new FileOutputStream(outputFile));
|
|
||||||
writeToEntropyPool(out);
|
|
||||||
out.flush();
|
|
||||||
out.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
// On some devices /dev/urandom isn't writable - this isn't fatal
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
}
|
|
||||||
// Read the seed from the pool
|
|
||||||
try {
|
|
||||||
DataInputStream in = new DataInputStream(
|
|
||||||
new FileInputStream(inputFile));
|
|
||||||
in.readFully(seed);
|
|
||||||
in.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
return seed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void writeToEntropyPool(DataOutputStream out) throws IOException {
|
|
||||||
out.writeLong(System.currentTimeMillis());
|
|
||||||
out.writeLong(System.nanoTime());
|
|
||||||
List<NetworkInterface> ifaces =
|
|
||||||
Collections.list(NetworkInterface.getNetworkInterfaces());
|
|
||||||
for (NetworkInterface i : ifaces) {
|
|
||||||
List<InetAddress> addrs = Collections.list(i.getInetAddresses());
|
|
||||||
for (InetAddress a : addrs) out.write(a.getAddress());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
package org.briarproject.bramble;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Vector;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
public class PoliteExecutorTest extends BrambleTestCase {
|
||||||
|
|
||||||
|
private static final String TAG = "Test";
|
||||||
|
private static final int TASKS = 10;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTasksAreDelegatedInOrderOfSubmission() throws Exception {
|
||||||
|
// Delegate to a single-threaded executor
|
||||||
|
Executor delegate = Executors.newSingleThreadExecutor();
|
||||||
|
// Allow all the tasks to be delegated straight away
|
||||||
|
PoliteExecutor polite = new PoliteExecutor(TAG, delegate, TASKS * 2);
|
||||||
|
final List<Integer> list = new Vector<Integer>();
|
||||||
|
final CountDownLatch latch = new CountDownLatch(TASKS);
|
||||||
|
for (int i = 0; i < TASKS; i++) {
|
||||||
|
final int result = i;
|
||||||
|
polite.execute(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
list.add(result);
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Wait for all the tasks to finish
|
||||||
|
latch.await();
|
||||||
|
// The tasks should have run in the order they were submitted
|
||||||
|
assertEquals(ascendingOrder(), list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testQueuedTasksAreDelegatedInOrderOfSubmission()
|
||||||
|
throws Exception {
|
||||||
|
// Delegate to a single-threaded executor
|
||||||
|
Executor delegate = Executors.newSingleThreadExecutor();
|
||||||
|
// Allow two tasks to be delegated at a time
|
||||||
|
PoliteExecutor polite = new PoliteExecutor(TAG, delegate, 2);
|
||||||
|
final List<Integer> list = new Vector<Integer>();
|
||||||
|
final CountDownLatch latch = new CountDownLatch(TASKS);
|
||||||
|
for (int i = 0; i < TASKS; i++) {
|
||||||
|
final int result = i;
|
||||||
|
polite.execute(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
list.add(result);
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Wait for all the tasks to finish
|
||||||
|
latch.await();
|
||||||
|
// The tasks should have run in the order they were submitted
|
||||||
|
assertEquals(ascendingOrder(), list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTasksRunInParallelOnDelegate() throws Exception {
|
||||||
|
// Delegate to a multi-threaded executor
|
||||||
|
Executor delegate = Executors.newCachedThreadPool();
|
||||||
|
// Allow all the tasks to be delegated straight away
|
||||||
|
PoliteExecutor polite = new PoliteExecutor(TAG, delegate, TASKS * 2);
|
||||||
|
final List<Integer> list = new Vector<Integer>();
|
||||||
|
final CountDownLatch[] latches = new CountDownLatch[TASKS];
|
||||||
|
for (int i = 0; i < TASKS; i++) latches[i] = new CountDownLatch(1);
|
||||||
|
for (int i = 0; i < TASKS; i++) {
|
||||||
|
final int result = i;
|
||||||
|
polite.execute(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
// Each task waits for the next task, if any, to finish
|
||||||
|
if (result < TASKS - 1) latches[result + 1].await();
|
||||||
|
list.add(result);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
latches[result].countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Wait for all the tasks to finish
|
||||||
|
for (int i = 0; i < TASKS; i++) latches[i].await();
|
||||||
|
// The tasks should have finished in reverse order
|
||||||
|
assertEquals(descendingOrder(), list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTasksDoNotRunInParallelOnDelegate() throws Exception {
|
||||||
|
// Delegate to a multi-threaded executor
|
||||||
|
Executor delegate = Executors.newCachedThreadPool();
|
||||||
|
// Allow one task to be delegated at a time
|
||||||
|
PoliteExecutor polite = new PoliteExecutor(TAG, delegate, 1);
|
||||||
|
final List<Integer> list = new Vector<Integer>();
|
||||||
|
final CountDownLatch latch = new CountDownLatch(TASKS);
|
||||||
|
for (int i = 0; i < TASKS; i++) {
|
||||||
|
final int result = i;
|
||||||
|
polite.execute(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
// Each task runs faster than the previous task
|
||||||
|
Thread.sleep(TASKS - result);
|
||||||
|
list.add(result);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Wait for all the tasks to finish
|
||||||
|
latch.await();
|
||||||
|
// The tasks should have finished in the order they were submitted
|
||||||
|
assertEquals(ascendingOrder(), list);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Integer> ascendingOrder() {
|
||||||
|
Integer[] array = new Integer[TASKS];
|
||||||
|
for (int i = 0; i < TASKS; i++) array[i] = i;
|
||||||
|
return Arrays.asList(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Integer> descendingOrder() {
|
||||||
|
Integer[] array = new Integer[TASKS];
|
||||||
|
for (int i = 0; i < TASKS; i++) array[i] = TASKS - 1 - i;
|
||||||
|
return Arrays.asList(array);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -45,7 +45,7 @@ public class EllipticCurveMultiplicationTest extends BrambleTestCase {
|
|||||||
byte[] seed = new byte[32];
|
byte[] seed = new byte[32];
|
||||||
new SecureRandom().nextBytes(seed);
|
new SecureRandom().nextBytes(seed);
|
||||||
// Montgomery ladder multiplier
|
// Montgomery ladder multiplier
|
||||||
SecureRandom random = new FortunaSecureRandom(seed);
|
SecureRandom random = new PseudoSecureRandom(seed);
|
||||||
ECKeyGenerationParameters montgomeryGeneratorParams =
|
ECKeyGenerationParameters montgomeryGeneratorParams =
|
||||||
new ECKeyGenerationParameters(PARAMETERS, random);
|
new ECKeyGenerationParameters(PARAMETERS, random);
|
||||||
ECKeyPairGenerator montgomeryGenerator = new ECKeyPairGenerator();
|
ECKeyPairGenerator montgomeryGenerator = new ECKeyPairGenerator();
|
||||||
@@ -63,7 +63,7 @@ public class EllipticCurveMultiplicationTest extends BrambleTestCase {
|
|||||||
ECPublicKeyParameters montgomeryPublic2 =
|
ECPublicKeyParameters montgomeryPublic2 =
|
||||||
(ECPublicKeyParameters) montgomeryKeyPair2.getPublic();
|
(ECPublicKeyParameters) montgomeryKeyPair2.getPublic();
|
||||||
// Default multiplier
|
// Default multiplier
|
||||||
random = new FortunaSecureRandom(seed);
|
random = new PseudoSecureRandom(seed);
|
||||||
ECKeyGenerationParameters defaultGeneratorParams =
|
ECKeyGenerationParameters defaultGeneratorParams =
|
||||||
new ECKeyGenerationParameters(defaultParameters, random);
|
new ECKeyGenerationParameters(defaultParameters, random);
|
||||||
ECKeyPairGenerator defaultGenerator = new ECKeyPairGenerator();
|
ECKeyPairGenerator defaultGenerator = new ECKeyPairGenerator();
|
||||||
|
|||||||
@@ -1,99 +0,0 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.spongycastle.crypto.BlockCipher;
|
|
||||||
import org.spongycastle.crypto.engines.AESLightEngine;
|
|
||||||
import org.spongycastle.crypto.params.KeyParameter;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
|
|
||||||
public class FortunaGeneratorTest extends BrambleTestCase {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCounterInitialisedToOne() {
|
|
||||||
FortunaGenerator f = new FortunaGenerator(new byte[32]);
|
|
||||||
// The counter is little-endian
|
|
||||||
byte[] expected = new byte[16];
|
|
||||||
expected[0] = 1;
|
|
||||||
assertArrayEquals(expected, f.getCounter());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testIncrementCounter() {
|
|
||||||
FortunaGenerator f = new FortunaGenerator(new byte[32]);
|
|
||||||
// Increment the counter until it reaches 255
|
|
||||||
for (int i = 1; i < 255; i++) f.incrementCounter();
|
|
||||||
byte[] expected = new byte[16];
|
|
||||||
expected[0] = (byte) 255;
|
|
||||||
assertArrayEquals(expected, f.getCounter());
|
|
||||||
// Increment the counter again - it should carry into the next byte
|
|
||||||
f.incrementCounter();
|
|
||||||
expected[0] = 0;
|
|
||||||
expected[1] = 1;
|
|
||||||
assertArrayEquals(expected, f.getCounter());
|
|
||||||
// Increment the counter until it carries into the next byte
|
|
||||||
for (int i = 256; i < 65536; i++) f.incrementCounter();
|
|
||||||
expected[0] = 0;
|
|
||||||
expected[1] = 0;
|
|
||||||
expected[2] = 1;
|
|
||||||
assertArrayEquals(expected, f.getCounter());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNextBytes() {
|
|
||||||
// Generate several outputs with the same seed - they should all match
|
|
||||||
byte[] seed = new byte[32];
|
|
||||||
byte[] out1 = new byte[48];
|
|
||||||
new FortunaGenerator(seed).nextBytes(out1, 0, 48);
|
|
||||||
// One byte longer than a block, with an offset of one
|
|
||||||
byte[] out2 = new byte[49];
|
|
||||||
new FortunaGenerator(seed).nextBytes(out2, 1, 48);
|
|
||||||
for (int i = 0; i < 48; i++) assertEquals(out1[i], out2[i + 1]);
|
|
||||||
// One byte shorter than a block
|
|
||||||
byte[] out3 = new byte[47];
|
|
||||||
new FortunaGenerator(seed).nextBytes(out3, 0, 47);
|
|
||||||
for (int i = 0; i < 47; i++) assertEquals(out1[i], out3[i]);
|
|
||||||
// Less than a block, with an offset greater than a block
|
|
||||||
byte[] out4 = new byte[32];
|
|
||||||
new FortunaGenerator(seed).nextBytes(out4, 17, 15);
|
|
||||||
for (int i = 0; i < 15; i++) assertEquals(out1[i], out4[i + 17]);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRekeying() {
|
|
||||||
byte[] seed = new byte[32];
|
|
||||||
FortunaGenerator f = new FortunaGenerator(seed);
|
|
||||||
// Generate three blocks of output
|
|
||||||
byte[] out1 = new byte[48];
|
|
||||||
f.nextBytes(out1, 0, 48);
|
|
||||||
// Create another generator with the same seed and generate one block
|
|
||||||
f = new FortunaGenerator(seed);
|
|
||||||
byte[] out2 = new byte[16];
|
|
||||||
f.nextBytes(out2, 0, 16);
|
|
||||||
// The generator should have rekeyed with the 2nd and 3rd blocks
|
|
||||||
byte[] expectedKey = new byte[32];
|
|
||||||
System.arraycopy(out1, 16, expectedKey, 0, 32);
|
|
||||||
// The generator's counter should have been incremented 3 times
|
|
||||||
byte[] expectedCounter = new byte[16];
|
|
||||||
expectedCounter[0] = 4;
|
|
||||||
// The next expected output block is the counter encrypted with the key
|
|
||||||
byte[] expectedOutput = new byte[16];
|
|
||||||
BlockCipher c = new AESLightEngine();
|
|
||||||
c.init(true, new KeyParameter(expectedKey));
|
|
||||||
c.processBlock(expectedCounter, 0, expectedOutput, 0);
|
|
||||||
// Check that the generator produces the expected output block
|
|
||||||
byte[] out3 = new byte[16];
|
|
||||||
f.nextBytes(out3, 0, 16);
|
|
||||||
assertArrayEquals(expectedOutput, out3);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMaximumRequestLength() {
|
|
||||||
int expectedMax = 1024 * 1024;
|
|
||||||
byte[] output = new byte[expectedMax + 123];
|
|
||||||
FortunaGenerator f = new FortunaGenerator(new byte[32]);
|
|
||||||
assertEquals(expectedMax, f.nextBytes(output, 0, output.length));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.spongycastle.crypto.BlockCipher;
|
|
||||||
import org.spongycastle.crypto.digests.SHA256Digest;
|
|
||||||
import org.spongycastle.crypto.engines.AESLightEngine;
|
|
||||||
import org.spongycastle.crypto.params.KeyParameter;
|
|
||||||
|
|
||||||
import static org.briarproject.bramble.crypto.FortunaSecureRandom.SELF_TEST_VECTOR_1;
|
|
||||||
import static org.briarproject.bramble.crypto.FortunaSecureRandom.SELF_TEST_VECTOR_2;
|
|
||||||
import static org.briarproject.bramble.crypto.FortunaSecureRandom.SELF_TEST_VECTOR_3;
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
public class FortunaSecureRandomTest extends BrambleTestCase {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testClassPassesSelfTest() {
|
|
||||||
assertTrue(FortunaSecureRandom.selfTest());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSelfTestVectorsAreReproducible() {
|
|
||||||
byte[] key = new byte[32], seed = new byte[32];
|
|
||||||
byte[] counter = new byte[16], output = new byte[16];
|
|
||||||
byte[] newKey = new byte[32];
|
|
||||||
// Calculate the initial key
|
|
||||||
DoubleDigest digest = new DoubleDigest(new SHA256Digest());
|
|
||||||
digest.update(key);
|
|
||||||
digest.update(seed);
|
|
||||||
digest.digest(key, 0, 32);
|
|
||||||
// Calculate the first output block and the new key
|
|
||||||
BlockCipher c = new AESLightEngine();
|
|
||||||
c.init(true, new KeyParameter(key));
|
|
||||||
counter[0] = 1;
|
|
||||||
c.processBlock(counter, 0, output, 0);
|
|
||||||
counter[0] = 2;
|
|
||||||
c.processBlock(counter, 0, newKey, 0);
|
|
||||||
counter[0] = 3;
|
|
||||||
c.processBlock(counter, 0, newKey, 16);
|
|
||||||
System.arraycopy(newKey, 0, key, 0, 32);
|
|
||||||
// The first self-test vector should match the first output block
|
|
||||||
assertArrayEquals(SELF_TEST_VECTOR_1, output);
|
|
||||||
// Calculate the second output block and the new key before reseeding
|
|
||||||
c.init(true, new KeyParameter(key));
|
|
||||||
counter[0] = 4;
|
|
||||||
c.processBlock(counter, 0, output, 0);
|
|
||||||
counter[0] = 5;
|
|
||||||
c.processBlock(counter, 0, newKey, 0);
|
|
||||||
counter[0] = 6;
|
|
||||||
c.processBlock(counter, 0, newKey, 16);
|
|
||||||
System.arraycopy(newKey, 0, key, 0, 32);
|
|
||||||
// The second self-test vector should match the second output block
|
|
||||||
assertArrayEquals(SELF_TEST_VECTOR_2, output);
|
|
||||||
// Calculate the new key after reseeding
|
|
||||||
digest.update(key);
|
|
||||||
digest.update(seed);
|
|
||||||
digest.digest(key, 0, 32);
|
|
||||||
// Calculate the third output block
|
|
||||||
c.init(true, new KeyParameter(key));
|
|
||||||
counter[0] = 8;
|
|
||||||
c.processBlock(counter, 0, output, 0);
|
|
||||||
// The third self-test vector should match the third output block
|
|
||||||
assertArrayEquals(SELF_TEST_VECTOR_3, output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,7 @@ package org.briarproject.bramble.crypto;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestSeedProvider;
|
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ public class HashTest extends BrambleTestCase {
|
|||||||
private final byte[] inputBytes2 = new byte[0];
|
private final byte[] inputBytes2 = new byte[0];
|
||||||
|
|
||||||
public HashTest() {
|
public HashTest() {
|
||||||
crypto = new CryptoComponentImpl(new TestSeedProvider());
|
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ package org.briarproject.bramble.crypto;
|
|||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.system.SeedProvider;
|
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestSeedProvider;
|
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
@@ -14,8 +14,9 @@ public class KeyAgreementTest extends BrambleTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeriveMasterSecret() throws Exception {
|
public void testDeriveMasterSecret() throws Exception {
|
||||||
SeedProvider seedProvider = new TestSeedProvider();
|
SecureRandomProvider
|
||||||
CryptoComponent crypto = new CryptoComponentImpl(seedProvider);
|
secureRandomProvider = new TestSecureRandomProvider();
|
||||||
|
CryptoComponent crypto = new CryptoComponentImpl(secureRandomProvider);
|
||||||
KeyPair aPair = crypto.generateAgreementKeyPair();
|
KeyPair aPair = crypto.generateAgreementKeyPair();
|
||||||
byte[] aPub = aPair.getPublic().getEncoded();
|
byte[] aPub = aPair.getPublic().getEncoded();
|
||||||
KeyPair bPair = crypto.generateAgreementKeyPair();
|
KeyPair bPair = crypto.generateAgreementKeyPair();
|
||||||
@@ -27,8 +28,9 @@ public class KeyAgreementTest extends BrambleTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeriveSharedSecret() throws Exception {
|
public void testDeriveSharedSecret() throws Exception {
|
||||||
SeedProvider seedProvider = new TestSeedProvider();
|
SecureRandomProvider
|
||||||
CryptoComponent crypto = new CryptoComponentImpl(seedProvider);
|
secureRandomProvider = new TestSecureRandomProvider();
|
||||||
|
CryptoComponent crypto = new CryptoComponentImpl(secureRandomProvider);
|
||||||
KeyPair aPair = crypto.generateAgreementKeyPair();
|
KeyPair aPair = crypto.generateAgreementKeyPair();
|
||||||
byte[] aPub = aPair.getPublic().getEncoded();
|
byte[] aPub = aPair.getPublic().getEncoded();
|
||||||
KeyPair bPair = crypto.generateAgreementKeyPair();
|
KeyPair bPair = crypto.generateAgreementKeyPair();
|
||||||
|
|||||||
@@ -1,20 +1,24 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.Bytes;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestSeedProvider;
|
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public class KeyDerivationTest extends BrambleTestCase {
|
public class KeyDerivationTest extends BrambleTestCase {
|
||||||
|
|
||||||
@@ -23,7 +27,7 @@ public class KeyDerivationTest extends BrambleTestCase {
|
|||||||
private final SecretKey master;
|
private final SecretKey master;
|
||||||
|
|
||||||
public KeyDerivationTest() {
|
public KeyDerivationTest() {
|
||||||
crypto = new CryptoComponentImpl(new TestSeedProvider());
|
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||||
master = TestUtils.getSecretKey();
|
master = TestUtils.getSecretKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,11 +160,7 @@ public class KeyDerivationTest extends BrambleTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void assertAllDifferent(List<SecretKey> keys) {
|
private void assertAllDifferent(List<SecretKey> keys) {
|
||||||
for (SecretKey ki : keys) {
|
Set<Bytes> set = new HashSet<Bytes>();
|
||||||
for (SecretKey kj : keys) {
|
for (SecretKey k : keys) assertTrue(set.add(new Bytes(k.getBytes())));
|
||||||
if (ki == kj) assertArrayEquals(ki.getBytes(), kj.getBytes());
|
|
||||||
else assertFalse(Arrays.equals(ki.getBytes(), kj.getBytes()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import org.briarproject.bramble.api.crypto.KeyParser;
|
|||||||
import org.briarproject.bramble.api.crypto.PrivateKey;
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestSeedProvider;
|
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ import static org.junit.Assert.assertTrue;
|
|||||||
public class KeyEncodingAndParsingTest extends BrambleTestCase {
|
public class KeyEncodingAndParsingTest extends BrambleTestCase {
|
||||||
|
|
||||||
private final CryptoComponentImpl crypto =
|
private final CryptoComponentImpl crypto =
|
||||||
new CryptoComponentImpl(new TestSeedProvider());
|
new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAgreementPublicKeyLength() throws Exception {
|
public void testAgreementPublicKeyLength() throws Exception {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package org.briarproject.bramble.crypto;
|
|||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestSeedProvider;
|
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ public class MacTest extends BrambleTestCase {
|
|||||||
private final byte[] inputBytes2 = new byte[0];
|
private final byte[] inputBytes2 = new byte[0];
|
||||||
|
|
||||||
public MacTest() {
|
public MacTest() {
|
||||||
crypto = new CryptoComponentImpl(new TestSeedProvider());
|
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestSeedProvider;
|
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ import static org.junit.Assert.assertTrue;
|
|||||||
public class PasswordBasedKdfTest extends BrambleTestCase {
|
public class PasswordBasedKdfTest extends BrambleTestCase {
|
||||||
|
|
||||||
private final CryptoComponentImpl crypto =
|
private final CryptoComponentImpl crypto =
|
||||||
new CryptoComponentImpl(new TestSeedProvider());
|
new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEncryptionAndDecryption() {
|
public void testEncryptionAndDecryption() {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
|
|||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.NONE;
|
||||||
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_STRONG;
|
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_STRONG;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
@@ -12,7 +13,7 @@ public class PasswordStrengthEstimatorImplTest extends BrambleTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testWeakPasswords() {
|
public void testWeakPasswords() {
|
||||||
PasswordStrengthEstimator e = new PasswordStrengthEstimatorImpl();
|
PasswordStrengthEstimator e = new PasswordStrengthEstimatorImpl();
|
||||||
assertTrue(e.estimateStrength("") < QUITE_STRONG);
|
assertTrue(e.estimateStrength("") == NONE);
|
||||||
assertTrue(e.estimateStrength("password") < QUITE_STRONG);
|
assertTrue(e.estimateStrength("password") < QUITE_STRONG);
|
||||||
assertTrue(e.estimateStrength("letmein") < QUITE_STRONG);
|
assertTrue(e.estimateStrength("letmein") < QUITE_STRONG);
|
||||||
assertTrue(e.estimateStrength("123456") < QUITE_STRONG);
|
assertTrue(e.estimateStrength("123456") < QUITE_STRONG);
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.PseudoRandom;
|
||||||
|
|
||||||
|
import java.security.Provider;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.security.SecureRandomSpi;
|
||||||
|
|
||||||
|
class PseudoSecureRandom extends SecureRandom {
|
||||||
|
|
||||||
|
private static final Provider PROVIDER = new PseudoSecureRandomProvider();
|
||||||
|
|
||||||
|
PseudoSecureRandom(byte[] seed) {
|
||||||
|
super(new PseudoSecureRandomSpi(seed), PROVIDER);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class PseudoSecureRandomSpi extends SecureRandomSpi {
|
||||||
|
|
||||||
|
private final PseudoRandom pseudoRandom;
|
||||||
|
|
||||||
|
private PseudoSecureRandomSpi(byte[] seed) {
|
||||||
|
pseudoRandom = new PseudoRandomImpl(seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected byte[] engineGenerateSeed(int length) {
|
||||||
|
return pseudoRandom.nextBytes(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void engineNextBytes(byte[] b) {
|
||||||
|
byte[] random = pseudoRandom.nextBytes(b.length);
|
||||||
|
System.arraycopy(random, 0, b, 0, b.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void engineSetSeed(byte[] seed) {
|
||||||
|
// Thank you for your input
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class PseudoSecureRandomProvider extends Provider {
|
||||||
|
|
||||||
|
private PseudoSecureRandomProvider() {
|
||||||
|
super("PseudoSecureRandom", 1.0, "Only for testing");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ package org.briarproject.bramble.crypto;
|
|||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestSeedProvider;
|
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ public class SignatureTest extends BrambleTestCase {
|
|||||||
private final byte[] inputBytes = TestUtils.getRandomBytes(123);
|
private final byte[] inputBytes = TestUtils.getRandomBytes(123);
|
||||||
|
|
||||||
public SignatureTest() {
|
public SignatureTest() {
|
||||||
crypto = new CryptoComponentImpl(new TestSeedProvider());
|
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||||
KeyPair k = crypto.generateSignatureKeyPair();
|
KeyPair k = crypto.generateSignatureKeyPair();
|
||||||
publicKey = k.getPublic().getEncoded();
|
publicKey = k.getPublic().getEncoded();
|
||||||
privateKey = k.getPrivate().getEncoded();
|
privateKey = k.getPrivate().getEncoded();
|
||||||
|
|||||||
@@ -9,9 +9,13 @@ import java.io.ByteArrayOutputStream;
|
|||||||
|
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAC_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.MAC_LENGTH;
|
||||||
|
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
||||||
|
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH;
|
||||||
|
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
public class StreamEncrypterImplTest extends BrambleTestCase {
|
public class StreamEncrypterImplTest extends BrambleTestCase {
|
||||||
|
|
||||||
@@ -30,6 +34,58 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
|
|||||||
payload = TestUtils.getRandomBytes(payloadLength);
|
payload = TestUtils.getRandomBytes(payloadLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testRejectsNegativePayloadLength() throws Exception {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
||||||
|
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
|
||||||
|
|
||||||
|
s.writeFrame(payload, -1, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testRejectsNegativePaddingLength() throws Exception {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
||||||
|
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
|
||||||
|
|
||||||
|
s.writeFrame(payload, 0, -1, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testRejectsMaxPayloadPlusPadding() throws Exception {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
||||||
|
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
|
||||||
|
|
||||||
|
byte[] bigPayload = new byte[MAX_PAYLOAD_LENGTH + 1];
|
||||||
|
s.writeFrame(bigPayload, MAX_PAYLOAD_LENGTH, 1, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAcceptsMaxPayloadIncludingPadding() throws Exception {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
||||||
|
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
|
||||||
|
|
||||||
|
byte[] bigPayload = new byte[MAX_PAYLOAD_LENGTH];
|
||||||
|
s.writeFrame(bigPayload, MAX_PAYLOAD_LENGTH - 1, 1, false);
|
||||||
|
assertEquals(TAG_LENGTH + STREAM_HEADER_LENGTH + MAX_FRAME_LENGTH,
|
||||||
|
out.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAcceptsMaxPayloadWithoutPadding() throws Exception {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
||||||
|
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
|
||||||
|
|
||||||
|
byte[] bigPayload = new byte[MAX_PAYLOAD_LENGTH];
|
||||||
|
s.writeFrame(bigPayload, MAX_PAYLOAD_LENGTH, 0, false);
|
||||||
|
assertEquals(TAG_LENGTH + STREAM_HEADER_LENGTH + MAX_FRAME_LENGTH,
|
||||||
|
out.size());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWriteUnpaddedNonFinalFrameWithTag() throws Exception {
|
public void testWriteUnpaddedNonFinalFrameWithTag() throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
package org.briarproject.bramble.properties;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
public class TransportPropertyManagerImplTest extends BrambleTestCase {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testUnitTestsExist() {
|
|
||||||
fail(); // FIXME: Write tests
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -19,12 +19,12 @@ import org.briarproject.bramble.api.sync.ValidationManager.IncomingMessageHook;
|
|||||||
import org.briarproject.bramble.api.sync.ValidationManager.MessageValidator;
|
import org.briarproject.bramble.api.sync.ValidationManager.MessageValidator;
|
||||||
import org.briarproject.bramble.api.sync.ValidationManager.State;
|
import org.briarproject.bramble.api.sync.ValidationManager.State;
|
||||||
import org.briarproject.bramble.api.sync.event.MessageAddedEvent;
|
import org.briarproject.bramble.api.sync.event.MessageAddedEvent;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
import org.briarproject.bramble.test.ImmediateExecutor;
|
import org.briarproject.bramble.test.ImmediateExecutor;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
import org.briarproject.bramble.util.ByteUtils;
|
import org.briarproject.bramble.util.ByteUtils;
|
||||||
import org.jmock.Expectations;
|
import org.jmock.Expectations;
|
||||||
import org.jmock.Mockery;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -38,8 +38,18 @@ import static org.briarproject.bramble.api.sync.ValidationManager.State.INVALID;
|
|||||||
import static org.briarproject.bramble.api.sync.ValidationManager.State.PENDING;
|
import static org.briarproject.bramble.api.sync.ValidationManager.State.PENDING;
|
||||||
import static org.briarproject.bramble.api.sync.ValidationManager.State.UNKNOWN;
|
import static org.briarproject.bramble.api.sync.ValidationManager.State.UNKNOWN;
|
||||||
|
|
||||||
public class ValidationManagerImplTest extends BrambleTestCase {
|
public class ValidationManagerImplTest extends BrambleMockTestCase {
|
||||||
|
|
||||||
|
private final DatabaseComponent db = context.mock(DatabaseComponent.class);
|
||||||
|
private final MessageFactory messageFactory =
|
||||||
|
context.mock(MessageFactory.class);
|
||||||
|
private final MessageValidator validator =
|
||||||
|
context.mock(MessageValidator.class);
|
||||||
|
private final IncomingMessageHook hook =
|
||||||
|
context.mock(IncomingMessageHook.class);
|
||||||
|
|
||||||
|
private final Executor dbExecutor = new ImmediateExecutor();
|
||||||
|
private final Executor validationExecutor = new ImmediateExecutor();
|
||||||
private final ClientId clientId =
|
private final ClientId clientId =
|
||||||
new ClientId(TestUtils.getRandomString(5));
|
new ClientId(TestUtils.getRandomString(5));
|
||||||
private final MessageId messageId = new MessageId(TestUtils.getRandomId());
|
private final MessageId messageId = new MessageId(TestUtils.getRandomId());
|
||||||
@@ -63,23 +73,58 @@ public class ValidationManagerImplTest extends BrambleTestCase {
|
|||||||
private final MessageContext validResultWithDependencies =
|
private final MessageContext validResultWithDependencies =
|
||||||
new MessageContext(metadata, Collections.singletonList(messageId1));
|
new MessageContext(metadata, Collections.singletonList(messageId1));
|
||||||
|
|
||||||
|
private ValidationManagerImpl vm;
|
||||||
|
|
||||||
public ValidationManagerImplTest() {
|
public ValidationManagerImplTest() {
|
||||||
// Encode the messages
|
// Encode the messages
|
||||||
System.arraycopy(groupId.getBytes(), 0, raw, 0, UniqueId.LENGTH);
|
System.arraycopy(groupId.getBytes(), 0, raw, 0, UniqueId.LENGTH);
|
||||||
ByteUtils.writeUint64(timestamp, raw, UniqueId.LENGTH);
|
ByteUtils.writeUint64(timestamp, raw, UniqueId.LENGTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
vm = new ValidationManagerImpl(db, dbExecutor, validationExecutor,
|
||||||
|
messageFactory);
|
||||||
|
vm.registerMessageValidator(clientId, validator);
|
||||||
|
vm.registerIncomingMessageHook(clientId, hook);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStartAndStop() throws Exception {
|
||||||
|
final Transaction txn = new Transaction(null, true);
|
||||||
|
final Transaction txn1 = new Transaction(null, true);
|
||||||
|
final Transaction txn2 = new Transaction(null, true);
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
// validateOutstandingMessages()
|
||||||
|
oneOf(db).startTransaction(true);
|
||||||
|
will(returnValue(txn));
|
||||||
|
oneOf(db).getMessagesToValidate(txn, clientId);
|
||||||
|
will(returnValue(Collections.emptyList()));
|
||||||
|
oneOf(db).commitTransaction(txn);
|
||||||
|
oneOf(db).endTransaction(txn);
|
||||||
|
// deliverOutstandingMessages()
|
||||||
|
oneOf(db).startTransaction(true);
|
||||||
|
will(returnValue(txn1));
|
||||||
|
oneOf(db).getPendingMessages(txn1, clientId);
|
||||||
|
will(returnValue(Collections.emptyList()));
|
||||||
|
oneOf(db).commitTransaction(txn1);
|
||||||
|
oneOf(db).endTransaction(txn1);
|
||||||
|
// shareOutstandingMessages()
|
||||||
|
oneOf(db).startTransaction(true);
|
||||||
|
will(returnValue(txn2));
|
||||||
|
oneOf(db).getMessagesToShare(txn2, clientId);
|
||||||
|
will(returnValue(Collections.emptyList()));
|
||||||
|
oneOf(db).commitTransaction(txn2);
|
||||||
|
oneOf(db).endTransaction(txn2);
|
||||||
|
}});
|
||||||
|
|
||||||
|
vm.startService();
|
||||||
|
vm.stopService();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMessagesAreValidatedAtStartup() throws Exception {
|
public void testMessagesAreValidatedAtStartup() throws Exception {
|
||||||
Mockery context = new Mockery();
|
|
||||||
final DatabaseComponent db = context.mock(DatabaseComponent.class);
|
|
||||||
final Executor dbExecutor = new ImmediateExecutor();
|
|
||||||
final Executor cryptoExecutor = new ImmediateExecutor();
|
|
||||||
final MessageFactory messageFactory =
|
|
||||||
context.mock(MessageFactory.class);
|
|
||||||
final MessageValidator validator = context.mock(MessageValidator.class);
|
|
||||||
final IncomingMessageHook hook =
|
|
||||||
context.mock(IncomingMessageHook.class);
|
|
||||||
final Transaction txn = new Transaction(null, true);
|
final Transaction txn = new Transaction(null, true);
|
||||||
final Transaction txn1 = new Transaction(null, true);
|
final Transaction txn1 = new Transaction(null, true);
|
||||||
final Transaction txn2 = new Transaction(null, false);
|
final Transaction txn2 = new Transaction(null, false);
|
||||||
@@ -87,6 +132,7 @@ public class ValidationManagerImplTest extends BrambleTestCase {
|
|||||||
final Transaction txn4 = new Transaction(null, false);
|
final Transaction txn4 = new Transaction(null, false);
|
||||||
final Transaction txn5 = new Transaction(null, true);
|
final Transaction txn5 = new Transaction(null, true);
|
||||||
final Transaction txn6 = new Transaction(null, true);
|
final Transaction txn6 = new Transaction(null, true);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Get messages to validate
|
// Get messages to validate
|
||||||
oneOf(db).startTransaction(true);
|
oneOf(db).startTransaction(true);
|
||||||
@@ -165,26 +211,11 @@ public class ValidationManagerImplTest extends BrambleTestCase {
|
|||||||
oneOf(db).endTransaction(txn6);
|
oneOf(db).endTransaction(txn6);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
|
|
||||||
cryptoExecutor, messageFactory);
|
|
||||||
vm.registerMessageValidator(clientId, validator);
|
|
||||||
vm.registerIncomingMessageHook(clientId, hook);
|
|
||||||
vm.startService();
|
vm.startService();
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPendingMessagesAreDeliveredAtStartup() throws Exception {
|
public void testPendingMessagesAreDeliveredAtStartup() throws Exception {
|
||||||
Mockery context = new Mockery();
|
|
||||||
final DatabaseComponent db = context.mock(DatabaseComponent.class);
|
|
||||||
final Executor dbExecutor = new ImmediateExecutor();
|
|
||||||
final Executor cryptoExecutor = new ImmediateExecutor();
|
|
||||||
final MessageFactory messageFactory =
|
|
||||||
context.mock(MessageFactory.class);
|
|
||||||
final MessageValidator validator = context.mock(MessageValidator.class);
|
|
||||||
final IncomingMessageHook hook =
|
|
||||||
context.mock(IncomingMessageHook.class);
|
|
||||||
final Transaction txn = new Transaction(null, true);
|
final Transaction txn = new Transaction(null, true);
|
||||||
final Transaction txn1 = new Transaction(null, true);
|
final Transaction txn1 = new Transaction(null, true);
|
||||||
final Transaction txn2 = new Transaction(null, false);
|
final Transaction txn2 = new Transaction(null, false);
|
||||||
@@ -266,26 +297,11 @@ public class ValidationManagerImplTest extends BrambleTestCase {
|
|||||||
oneOf(db).endTransaction(txn4);
|
oneOf(db).endTransaction(txn4);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
|
|
||||||
cryptoExecutor, messageFactory);
|
|
||||||
vm.registerMessageValidator(clientId, validator);
|
|
||||||
vm.registerIncomingMessageHook(clientId, hook);
|
|
||||||
vm.startService();
|
vm.startService();
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMessagesAreSharedAtStartup() throws Exception {
|
public void testMessagesAreSharedAtStartup() throws Exception {
|
||||||
Mockery context = new Mockery();
|
|
||||||
final DatabaseComponent db = context.mock(DatabaseComponent.class);
|
|
||||||
final Executor dbExecutor = new ImmediateExecutor();
|
|
||||||
final Executor cryptoExecutor = new ImmediateExecutor();
|
|
||||||
final MessageFactory messageFactory =
|
|
||||||
context.mock(MessageFactory.class);
|
|
||||||
final MessageValidator validator = context.mock(MessageValidator.class);
|
|
||||||
final IncomingMessageHook hook =
|
|
||||||
context.mock(IncomingMessageHook.class);
|
|
||||||
final Transaction txn = new Transaction(null, true);
|
final Transaction txn = new Transaction(null, true);
|
||||||
final Transaction txn1 = new Transaction(null, true);
|
final Transaction txn1 = new Transaction(null, true);
|
||||||
final Transaction txn2 = new Transaction(null, true);
|
final Transaction txn2 = new Transaction(null, true);
|
||||||
@@ -333,29 +349,15 @@ public class ValidationManagerImplTest extends BrambleTestCase {
|
|||||||
oneOf(db).endTransaction(txn4);
|
oneOf(db).endTransaction(txn4);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
|
|
||||||
cryptoExecutor, messageFactory);
|
|
||||||
vm.registerMessageValidator(clientId, validator);
|
|
||||||
vm.registerIncomingMessageHook(clientId, hook);
|
|
||||||
vm.startService();
|
vm.startService();
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIncomingMessagesAreShared() throws Exception {
|
public void testIncomingMessagesAreShared() throws Exception {
|
||||||
Mockery context = new Mockery();
|
|
||||||
final DatabaseComponent db = context.mock(DatabaseComponent.class);
|
|
||||||
final Executor dbExecutor = new ImmediateExecutor();
|
|
||||||
final Executor cryptoExecutor = new ImmediateExecutor();
|
|
||||||
final MessageFactory messageFactory =
|
|
||||||
context.mock(MessageFactory.class);
|
|
||||||
final MessageValidator validator = context.mock(MessageValidator.class);
|
|
||||||
final IncomingMessageHook hook =
|
|
||||||
context.mock(IncomingMessageHook.class);
|
|
||||||
final Transaction txn = new Transaction(null, true);
|
final Transaction txn = new Transaction(null, true);
|
||||||
final Transaction txn1 = new Transaction(null, false);
|
final Transaction txn1 = new Transaction(null, false);
|
||||||
final Transaction txn2 = new Transaction(null, false);
|
final Transaction txn2 = new Transaction(null, false);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Load the group
|
// Load the group
|
||||||
oneOf(db).startTransaction(true);
|
oneOf(db).startTransaction(true);
|
||||||
@@ -396,33 +398,19 @@ public class ValidationManagerImplTest extends BrambleTestCase {
|
|||||||
oneOf(db).endTransaction(txn2);
|
oneOf(db).endTransaction(txn2);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
|
|
||||||
cryptoExecutor, messageFactory);
|
|
||||||
vm.registerMessageValidator(clientId, validator);
|
|
||||||
vm.registerIncomingMessageHook(clientId, hook);
|
|
||||||
vm.eventOccurred(new MessageAddedEvent(message, contactId));
|
vm.eventOccurred(new MessageAddedEvent(message, contactId));
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testValidationContinuesAfterNoSuchMessageException()
|
public void testValidationContinuesAfterNoSuchMessageException()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
Mockery context = new Mockery();
|
|
||||||
final DatabaseComponent db = context.mock(DatabaseComponent.class);
|
|
||||||
final Executor dbExecutor = new ImmediateExecutor();
|
|
||||||
final Executor cryptoExecutor = new ImmediateExecutor();
|
|
||||||
final MessageFactory messageFactory =
|
|
||||||
context.mock(MessageFactory.class);
|
|
||||||
final MessageValidator validator = context.mock(MessageValidator.class);
|
|
||||||
final IncomingMessageHook hook =
|
|
||||||
context.mock(IncomingMessageHook.class);
|
|
||||||
final Transaction txn = new Transaction(null, true);
|
final Transaction txn = new Transaction(null, true);
|
||||||
final Transaction txn1 = new Transaction(null, true);
|
final Transaction txn1 = new Transaction(null, true);
|
||||||
final Transaction txn2 = new Transaction(null, true);
|
final Transaction txn2 = new Transaction(null, true);
|
||||||
final Transaction txn3 = new Transaction(null, false);
|
final Transaction txn3 = new Transaction(null, false);
|
||||||
final Transaction txn4 = new Transaction(null, true);
|
final Transaction txn4 = new Transaction(null, true);
|
||||||
final Transaction txn5 = new Transaction(null, true);
|
final Transaction txn5 = new Transaction(null, true);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Get messages to validate
|
// Get messages to validate
|
||||||
oneOf(db).startTransaction(true);
|
oneOf(db).startTransaction(true);
|
||||||
@@ -481,33 +469,19 @@ public class ValidationManagerImplTest extends BrambleTestCase {
|
|||||||
oneOf(db).endTransaction(txn5);
|
oneOf(db).endTransaction(txn5);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
|
|
||||||
cryptoExecutor, messageFactory);
|
|
||||||
vm.registerMessageValidator(clientId, validator);
|
|
||||||
vm.registerIncomingMessageHook(clientId, hook);
|
|
||||||
vm.startService();
|
vm.startService();
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testValidationContinuesAfterNoSuchGroupException()
|
public void testValidationContinuesAfterNoSuchGroupException()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
Mockery context = new Mockery();
|
|
||||||
final DatabaseComponent db = context.mock(DatabaseComponent.class);
|
|
||||||
final Executor dbExecutor = new ImmediateExecutor();
|
|
||||||
final Executor cryptoExecutor = new ImmediateExecutor();
|
|
||||||
final MessageFactory messageFactory =
|
|
||||||
context.mock(MessageFactory.class);
|
|
||||||
final MessageValidator validator = context.mock(MessageValidator.class);
|
|
||||||
final IncomingMessageHook hook =
|
|
||||||
context.mock(IncomingMessageHook.class);
|
|
||||||
final Transaction txn = new Transaction(null, true);
|
final Transaction txn = new Transaction(null, true);
|
||||||
final Transaction txn1 = new Transaction(null, true);
|
final Transaction txn1 = new Transaction(null, true);
|
||||||
final Transaction txn2 = new Transaction(null, true);
|
final Transaction txn2 = new Transaction(null, true);
|
||||||
final Transaction txn3 = new Transaction(null, false);
|
final Transaction txn3 = new Transaction(null, false);
|
||||||
final Transaction txn4 = new Transaction(null, true);
|
final Transaction txn4 = new Transaction(null, true);
|
||||||
final Transaction txn5 = new Transaction(null, true);
|
final Transaction txn5 = new Transaction(null, true);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Get messages to validate
|
// Get messages to validate
|
||||||
oneOf(db).startTransaction(true);
|
oneOf(db).startTransaction(true);
|
||||||
@@ -571,28 +545,14 @@ public class ValidationManagerImplTest extends BrambleTestCase {
|
|||||||
oneOf(db).endTransaction(txn5);
|
oneOf(db).endTransaction(txn5);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
|
|
||||||
cryptoExecutor, messageFactory);
|
|
||||||
vm.registerMessageValidator(clientId, validator);
|
|
||||||
vm.registerIncomingMessageHook(clientId, hook);
|
|
||||||
vm.startService();
|
vm.startService();
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNonLocalMessagesAreValidatedWhenAdded() throws Exception {
|
public void testNonLocalMessagesAreValidatedWhenAdded() throws Exception {
|
||||||
Mockery context = new Mockery();
|
|
||||||
final DatabaseComponent db = context.mock(DatabaseComponent.class);
|
|
||||||
final Executor dbExecutor = new ImmediateExecutor();
|
|
||||||
final Executor cryptoExecutor = new ImmediateExecutor();
|
|
||||||
final MessageFactory messageFactory =
|
|
||||||
context.mock(MessageFactory.class);
|
|
||||||
final MessageValidator validator = context.mock(MessageValidator.class);
|
|
||||||
final IncomingMessageHook hook =
|
|
||||||
context.mock(IncomingMessageHook.class);
|
|
||||||
final Transaction txn = new Transaction(null, true);
|
final Transaction txn = new Transaction(null, true);
|
||||||
final Transaction txn1 = new Transaction(null, false);
|
final Transaction txn1 = new Transaction(null, false);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Load the group
|
// Load the group
|
||||||
oneOf(db).startTransaction(true);
|
oneOf(db).startTransaction(true);
|
||||||
@@ -619,51 +579,20 @@ public class ValidationManagerImplTest extends BrambleTestCase {
|
|||||||
oneOf(db).endTransaction(txn1);
|
oneOf(db).endTransaction(txn1);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
|
|
||||||
cryptoExecutor, messageFactory);
|
|
||||||
vm.registerMessageValidator(clientId, validator);
|
|
||||||
vm.registerIncomingMessageHook(clientId, hook);
|
|
||||||
vm.eventOccurred(new MessageAddedEvent(message, contactId));
|
vm.eventOccurred(new MessageAddedEvent(message, contactId));
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLocalMessagesAreNotValidatedWhenAdded() throws Exception {
|
public void testLocalMessagesAreNotValidatedWhenAdded() throws Exception {
|
||||||
Mockery context = new Mockery();
|
|
||||||
final DatabaseComponent db = context.mock(DatabaseComponent.class);
|
|
||||||
final Executor dbExecutor = new ImmediateExecutor();
|
|
||||||
final Executor cryptoExecutor = new ImmediateExecutor();
|
|
||||||
final MessageFactory messageFactory =
|
|
||||||
context.mock(MessageFactory.class);
|
|
||||||
final MessageValidator validator = context.mock(MessageValidator.class);
|
|
||||||
final IncomingMessageHook hook =
|
|
||||||
context.mock(IncomingMessageHook.class);
|
|
||||||
|
|
||||||
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
|
|
||||||
cryptoExecutor, messageFactory);
|
|
||||||
vm.registerMessageValidator(clientId, validator);
|
|
||||||
vm.registerIncomingMessageHook(clientId, hook);
|
|
||||||
vm.eventOccurred(new MessageAddedEvent(message, null));
|
vm.eventOccurred(new MessageAddedEvent(message, null));
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMessagesWithUndeliveredDependenciesArePending()
|
public void testMessagesWithUndeliveredDependenciesArePending()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
Mockery context = new Mockery();
|
|
||||||
final DatabaseComponent db = context.mock(DatabaseComponent.class);
|
|
||||||
final Executor dbExecutor = new ImmediateExecutor();
|
|
||||||
final Executor cryptoExecutor = new ImmediateExecutor();
|
|
||||||
final MessageFactory messageFactory =
|
|
||||||
context.mock(MessageFactory.class);
|
|
||||||
final MessageValidator validator = context.mock(MessageValidator.class);
|
|
||||||
final IncomingMessageHook hook =
|
|
||||||
context.mock(IncomingMessageHook.class);
|
|
||||||
final Transaction txn = new Transaction(null, true);
|
final Transaction txn = new Transaction(null, true);
|
||||||
final Transaction txn1 = new Transaction(null, false);
|
final Transaction txn1 = new Transaction(null, false);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Load the group
|
// Load the group
|
||||||
oneOf(db).startTransaction(true);
|
oneOf(db).startTransaction(true);
|
||||||
@@ -688,29 +617,15 @@ public class ValidationManagerImplTest extends BrambleTestCase {
|
|||||||
oneOf(db).endTransaction(txn1);
|
oneOf(db).endTransaction(txn1);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
|
|
||||||
cryptoExecutor, messageFactory);
|
|
||||||
vm.registerMessageValidator(clientId, validator);
|
|
||||||
vm.registerIncomingMessageHook(clientId, hook);
|
|
||||||
vm.eventOccurred(new MessageAddedEvent(message, contactId));
|
vm.eventOccurred(new MessageAddedEvent(message, contactId));
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMessagesWithDeliveredDependenciesGetDelivered()
|
public void testMessagesWithDeliveredDependenciesGetDelivered()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
Mockery context = new Mockery();
|
|
||||||
final DatabaseComponent db = context.mock(DatabaseComponent.class);
|
|
||||||
final Executor dbExecutor = new ImmediateExecutor();
|
|
||||||
final Executor cryptoExecutor = new ImmediateExecutor();
|
|
||||||
final MessageFactory messageFactory =
|
|
||||||
context.mock(MessageFactory.class);
|
|
||||||
final MessageValidator validator = context.mock(MessageValidator.class);
|
|
||||||
final IncomingMessageHook hook =
|
|
||||||
context.mock(IncomingMessageHook.class);
|
|
||||||
final Transaction txn = new Transaction(null, true);
|
final Transaction txn = new Transaction(null, true);
|
||||||
final Transaction txn1 = new Transaction(null, false);
|
final Transaction txn1 = new Transaction(null, false);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Load the group
|
// Load the group
|
||||||
oneOf(db).startTransaction(true);
|
oneOf(db).startTransaction(true);
|
||||||
@@ -741,30 +656,16 @@ public class ValidationManagerImplTest extends BrambleTestCase {
|
|||||||
oneOf(db).endTransaction(txn1);
|
oneOf(db).endTransaction(txn1);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
|
|
||||||
cryptoExecutor, messageFactory);
|
|
||||||
vm.registerMessageValidator(clientId, validator);
|
|
||||||
vm.registerIncomingMessageHook(clientId, hook);
|
|
||||||
vm.eventOccurred(new MessageAddedEvent(message, contactId));
|
vm.eventOccurred(new MessageAddedEvent(message, contactId));
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMessagesWithInvalidDependenciesAreInvalid()
|
public void testMessagesWithInvalidDependenciesAreInvalid()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
Mockery context = new Mockery();
|
|
||||||
final DatabaseComponent db = context.mock(DatabaseComponent.class);
|
|
||||||
final Executor dbExecutor = new ImmediateExecutor();
|
|
||||||
final Executor cryptoExecutor = new ImmediateExecutor();
|
|
||||||
final MessageFactory messageFactory =
|
|
||||||
context.mock(MessageFactory.class);
|
|
||||||
final MessageValidator validator = context.mock(MessageValidator.class);
|
|
||||||
final IncomingMessageHook hook =
|
|
||||||
context.mock(IncomingMessageHook.class);
|
|
||||||
final Transaction txn = new Transaction(null, true);
|
final Transaction txn = new Transaction(null, true);
|
||||||
final Transaction txn1 = new Transaction(null, false);
|
final Transaction txn1 = new Transaction(null, false);
|
||||||
final Transaction txn2 = new Transaction(null, false);
|
final Transaction txn2 = new Transaction(null, false);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Load the group
|
// Load the group
|
||||||
oneOf(db).startTransaction(true);
|
oneOf(db).startTransaction(true);
|
||||||
@@ -809,26 +710,11 @@ public class ValidationManagerImplTest extends BrambleTestCase {
|
|||||||
oneOf(db).endTransaction(txn2);
|
oneOf(db).endTransaction(txn2);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
|
|
||||||
cryptoExecutor, messageFactory);
|
|
||||||
vm.registerMessageValidator(clientId, validator);
|
|
||||||
vm.registerIncomingMessageHook(clientId, hook);
|
|
||||||
vm.eventOccurred(new MessageAddedEvent(message, contactId));
|
vm.eventOccurred(new MessageAddedEvent(message, contactId));
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRecursiveInvalidation() throws Exception {
|
public void testRecursiveInvalidation() throws Exception {
|
||||||
Mockery context = new Mockery();
|
|
||||||
final DatabaseComponent db = context.mock(DatabaseComponent.class);
|
|
||||||
final Executor dbExecutor = new ImmediateExecutor();
|
|
||||||
final Executor cryptoExecutor = new ImmediateExecutor();
|
|
||||||
final MessageFactory messageFactory =
|
|
||||||
context.mock(MessageFactory.class);
|
|
||||||
final MessageValidator validator = context.mock(MessageValidator.class);
|
|
||||||
final IncomingMessageHook hook =
|
|
||||||
context.mock(IncomingMessageHook.class);
|
|
||||||
final MessageId messageId3 = new MessageId(TestUtils.getRandomId());
|
final MessageId messageId3 = new MessageId(TestUtils.getRandomId());
|
||||||
final MessageId messageId4 = new MessageId(TestUtils.getRandomId());
|
final MessageId messageId4 = new MessageId(TestUtils.getRandomId());
|
||||||
final Map<MessageId, State> twoDependents =
|
final Map<MessageId, State> twoDependents =
|
||||||
@@ -842,6 +728,7 @@ public class ValidationManagerImplTest extends BrambleTestCase {
|
|||||||
final Transaction txn4 = new Transaction(null, false);
|
final Transaction txn4 = new Transaction(null, false);
|
||||||
final Transaction txn5 = new Transaction(null, false);
|
final Transaction txn5 = new Transaction(null, false);
|
||||||
final Transaction txn6 = new Transaction(null, false);
|
final Transaction txn6 = new Transaction(null, false);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Load the group
|
// Load the group
|
||||||
oneOf(db).startTransaction(true);
|
oneOf(db).startTransaction(true);
|
||||||
@@ -927,26 +814,11 @@ public class ValidationManagerImplTest extends BrambleTestCase {
|
|||||||
oneOf(db).endTransaction(txn6);
|
oneOf(db).endTransaction(txn6);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
|
|
||||||
cryptoExecutor, messageFactory);
|
|
||||||
vm.registerMessageValidator(clientId, validator);
|
|
||||||
vm.registerIncomingMessageHook(clientId, hook);
|
|
||||||
vm.eventOccurred(new MessageAddedEvent(message, contactId));
|
vm.eventOccurred(new MessageAddedEvent(message, contactId));
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPendingDependentsGetDelivered() throws Exception {
|
public void testPendingDependentsGetDelivered() throws Exception {
|
||||||
Mockery context = new Mockery();
|
|
||||||
final DatabaseComponent db = context.mock(DatabaseComponent.class);
|
|
||||||
final Executor dbExecutor = new ImmediateExecutor();
|
|
||||||
final Executor cryptoExecutor = new ImmediateExecutor();
|
|
||||||
final MessageFactory messageFactory =
|
|
||||||
context.mock(MessageFactory.class);
|
|
||||||
final MessageValidator validator = context.mock(MessageValidator.class);
|
|
||||||
final IncomingMessageHook hook =
|
|
||||||
context.mock(IncomingMessageHook.class);
|
|
||||||
final MessageId messageId3 = new MessageId(TestUtils.getRandomId());
|
final MessageId messageId3 = new MessageId(TestUtils.getRandomId());
|
||||||
final MessageId messageId4 = new MessageId(TestUtils.getRandomId());
|
final MessageId messageId4 = new MessageId(TestUtils.getRandomId());
|
||||||
final Message message3 = new Message(messageId3, groupId, timestamp,
|
final Message message3 = new Message(messageId3, groupId, timestamp,
|
||||||
@@ -968,6 +840,7 @@ public class ValidationManagerImplTest extends BrambleTestCase {
|
|||||||
final Transaction txn4 = new Transaction(null, false);
|
final Transaction txn4 = new Transaction(null, false);
|
||||||
final Transaction txn5 = new Transaction(null, false);
|
final Transaction txn5 = new Transaction(null, false);
|
||||||
final Transaction txn6 = new Transaction(null, false);
|
final Transaction txn6 = new Transaction(null, false);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Load the group
|
// Load the group
|
||||||
oneOf(db).startTransaction(true);
|
oneOf(db).startTransaction(true);
|
||||||
@@ -1100,26 +973,11 @@ public class ValidationManagerImplTest extends BrambleTestCase {
|
|||||||
oneOf(db).endTransaction(txn6);
|
oneOf(db).endTransaction(txn6);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
|
|
||||||
cryptoExecutor, messageFactory);
|
|
||||||
vm.registerMessageValidator(clientId, validator);
|
|
||||||
vm.registerIncomingMessageHook(clientId, hook);
|
|
||||||
vm.eventOccurred(new MessageAddedEvent(message, contactId));
|
vm.eventOccurred(new MessageAddedEvent(message, contactId));
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOnlyReadyPendingDependentsGetDelivered() throws Exception {
|
public void testOnlyReadyPendingDependentsGetDelivered() throws Exception {
|
||||||
Mockery context = new Mockery();
|
|
||||||
final DatabaseComponent db = context.mock(DatabaseComponent.class);
|
|
||||||
final Executor dbExecutor = new ImmediateExecutor();
|
|
||||||
final Executor cryptoExecutor = new ImmediateExecutor();
|
|
||||||
final MessageFactory messageFactory =
|
|
||||||
context.mock(MessageFactory.class);
|
|
||||||
final MessageValidator validator = context.mock(MessageValidator.class);
|
|
||||||
final IncomingMessageHook hook =
|
|
||||||
context.mock(IncomingMessageHook.class);
|
|
||||||
final Map<MessageId, State> twoDependencies =
|
final Map<MessageId, State> twoDependencies =
|
||||||
new LinkedHashMap<MessageId, State>();
|
new LinkedHashMap<MessageId, State>();
|
||||||
twoDependencies.put(messageId, DELIVERED);
|
twoDependencies.put(messageId, DELIVERED);
|
||||||
@@ -1127,6 +985,7 @@ public class ValidationManagerImplTest extends BrambleTestCase {
|
|||||||
final Transaction txn = new Transaction(null, true);
|
final Transaction txn = new Transaction(null, true);
|
||||||
final Transaction txn1 = new Transaction(null, false);
|
final Transaction txn1 = new Transaction(null, false);
|
||||||
final Transaction txn2 = new Transaction(null, false);
|
final Transaction txn2 = new Transaction(null, false);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Load the group
|
// Load the group
|
||||||
oneOf(db).startTransaction(true);
|
oneOf(db).startTransaction(true);
|
||||||
@@ -1162,86 +1021,6 @@ public class ValidationManagerImplTest extends BrambleTestCase {
|
|||||||
oneOf(db).endTransaction(txn2);
|
oneOf(db).endTransaction(txn2);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
|
|
||||||
cryptoExecutor, messageFactory);
|
|
||||||
vm.registerMessageValidator(clientId, validator);
|
|
||||||
vm.registerIncomingMessageHook(clientId, hook);
|
|
||||||
vm.eventOccurred(new MessageAddedEvent(message, contactId));
|
vm.eventOccurred(new MessageAddedEvent(message, contactId));
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMessageDependencyCycle() throws Exception {
|
|
||||||
final MessageContext cycleContext = new MessageContext(metadata,
|
|
||||||
Collections.singletonList(messageId));
|
|
||||||
|
|
||||||
Mockery context = new Mockery();
|
|
||||||
final DatabaseComponent db = context.mock(DatabaseComponent.class);
|
|
||||||
final Executor dbExecutor = new ImmediateExecutor();
|
|
||||||
final Executor cryptoExecutor = new ImmediateExecutor();
|
|
||||||
final MessageFactory messageFactory =
|
|
||||||
context.mock(MessageFactory.class);
|
|
||||||
final MessageValidator validator = context.mock(MessageValidator.class);
|
|
||||||
final IncomingMessageHook hook =
|
|
||||||
context.mock(IncomingMessageHook.class);
|
|
||||||
final Transaction txn = new Transaction(null, true);
|
|
||||||
final Transaction txn1 = new Transaction(null, false);
|
|
||||||
final Transaction txn2 = new Transaction(null, true);
|
|
||||||
final Transaction txn3 = new Transaction(null, false);
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
// Load the group
|
|
||||||
oneOf(db).startTransaction(true);
|
|
||||||
will(returnValue(txn));
|
|
||||||
oneOf(db).getGroup(txn, groupId);
|
|
||||||
will(returnValue(group));
|
|
||||||
oneOf(db).commitTransaction(txn);
|
|
||||||
oneOf(db).endTransaction(txn);
|
|
||||||
// Validate the message: valid
|
|
||||||
oneOf(validator).validateMessage(message, group);
|
|
||||||
will(returnValue(validResultWithDependencies));
|
|
||||||
// Store the validation result
|
|
||||||
oneOf(db).startTransaction(false);
|
|
||||||
will(returnValue(txn1));
|
|
||||||
oneOf(db).addMessageDependencies(txn1, message,
|
|
||||||
validResultWithDependencies.getDependencies());
|
|
||||||
oneOf(db).getMessageDependencies(txn1, messageId);
|
|
||||||
will(returnValue(Collections.singletonMap(messageId1, UNKNOWN)));
|
|
||||||
oneOf(db).mergeMessageMetadata(txn1, messageId, metadata);
|
|
||||||
oneOf(db).setMessageState(txn1, messageId, PENDING);
|
|
||||||
oneOf(db).commitTransaction(txn1);
|
|
||||||
oneOf(db).endTransaction(txn1);
|
|
||||||
// Second message is coming in
|
|
||||||
oneOf(db).startTransaction(true);
|
|
||||||
will(returnValue(txn2));
|
|
||||||
oneOf(db).getGroup(txn2, groupId);
|
|
||||||
will(returnValue(group));
|
|
||||||
oneOf(db).commitTransaction(txn2);
|
|
||||||
oneOf(db).endTransaction(txn2);
|
|
||||||
// Validate the message: valid
|
|
||||||
oneOf(validator).validateMessage(message1, group);
|
|
||||||
will(returnValue(cycleContext));
|
|
||||||
// Store the validation result
|
|
||||||
oneOf(db).startTransaction(false);
|
|
||||||
will(returnValue(txn3));
|
|
||||||
oneOf(db).addMessageDependencies(txn3, message1,
|
|
||||||
cycleContext.getDependencies());
|
|
||||||
oneOf(db).getMessageDependencies(txn3, messageId1);
|
|
||||||
will(returnValue(Collections.singletonMap(messageId, PENDING)));
|
|
||||||
oneOf(db).mergeMessageMetadata(txn3, messageId1, metadata);
|
|
||||||
oneOf(db).setMessageState(txn3, messageId1, PENDING);
|
|
||||||
oneOf(db).commitTransaction(txn3);
|
|
||||||
oneOf(db).endTransaction(txn3);
|
|
||||||
}});
|
|
||||||
|
|
||||||
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
|
|
||||||
cryptoExecutor, messageFactory);
|
|
||||||
vm.registerMessageValidator(clientId, validator);
|
|
||||||
vm.registerIncomingMessageHook(clientId, hook);
|
|
||||||
vm.eventOccurred(new MessageAddedEvent(message, contactId));
|
|
||||||
vm.eventOccurred(new MessageAddedEvent(message1, contactId));
|
|
||||||
|
|
||||||
context.assertIsSatisfied();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package org.briarproject.bramble.system;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
|
import org.briarproject.bramble.util.OsUtils;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.security.Provider;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
public class LinuxSecureRandomProviderTest extends BrambleTestCase {
|
||||||
|
|
||||||
|
private final File testDir = TestUtils.getTestDirectory();
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
testDir.mkdirs();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetProviderWritesToRandomDeviceOnFirstCall()
|
||||||
|
throws Exception {
|
||||||
|
if (!(OsUtils.isLinux())) {
|
||||||
|
System.err.println("WARNING: Skipping test, can't run on this OS");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Redirect the provider's output to a file
|
||||||
|
File urandom = new File(testDir, "urandom");
|
||||||
|
urandom.delete();
|
||||||
|
assertTrue(urandom.createNewFile());
|
||||||
|
assertEquals(0, urandom.length());
|
||||||
|
LinuxSecureRandomProvider p = new LinuxSecureRandomProvider(urandom);
|
||||||
|
// Getting a provider should write entropy to the file
|
||||||
|
Provider provider = p.getProvider();
|
||||||
|
assertNotNull(provider);
|
||||||
|
assertEquals("LinuxPRNG", provider.getName());
|
||||||
|
// There should be at least 16 bytes from the clock, 8 from the runtime
|
||||||
|
long length = urandom.length();
|
||||||
|
assertTrue(length >= 24);
|
||||||
|
// Getting another provider should not write to the file again
|
||||||
|
provider = p.getProvider();
|
||||||
|
assertNotNull(provider);
|
||||||
|
assertEquals("LinuxPRNG", provider.getName());
|
||||||
|
assertEquals(length, urandom.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
TestUtils.deleteTestDirectory(testDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
package org.briarproject.bramble.system;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.Bytes;
|
||||||
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
|
import org.briarproject.bramble.util.IoUtils;
|
||||||
|
import org.briarproject.bramble.util.OsUtils;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
|
||||||
|
public class LinuxSecureRandomSpiTest extends BrambleTestCase {
|
||||||
|
|
||||||
|
private static final File RANDOM_DEVICE = new File("/dev/urandom");
|
||||||
|
private static final int SEED_BYTES = 32;
|
||||||
|
|
||||||
|
private final File testDir = TestUtils.getTestDirectory();
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
testDir.mkdirs();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSeedsAreDistinct() {
|
||||||
|
if (!(OsUtils.isLinux())) {
|
||||||
|
System.err.println("WARNING: Skipping test, can't run on this OS");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Bytes> seeds = new HashSet<Bytes>();
|
||||||
|
LinuxSecureRandomSpi engine = new LinuxSecureRandomSpi();
|
||||||
|
for (int i = 0; i < 1000; i++) {
|
||||||
|
byte[] seed = engine.engineGenerateSeed(SEED_BYTES);
|
||||||
|
assertEquals(SEED_BYTES, seed.length);
|
||||||
|
assertTrue(seeds.add(new Bytes(seed)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEngineSetSeedWritesToRandomDevice() throws Exception {
|
||||||
|
if (!(OsUtils.isLinux())) {
|
||||||
|
System.err.println("WARNING: Skipping test, can't run on this OS");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Redirect the engine's output to a file
|
||||||
|
File urandom = new File(testDir, "urandom");
|
||||||
|
urandom.delete();
|
||||||
|
assertTrue(urandom.createNewFile());
|
||||||
|
assertEquals(0, urandom.length());
|
||||||
|
// Generate a seed
|
||||||
|
byte[] seed = TestUtils.getRandomBytes(SEED_BYTES);
|
||||||
|
// Check that the engine writes the seed to the file
|
||||||
|
LinuxSecureRandomSpi engine = new LinuxSecureRandomSpi(RANDOM_DEVICE,
|
||||||
|
urandom);
|
||||||
|
engine.engineSetSeed(seed);
|
||||||
|
assertEquals(SEED_BYTES, urandom.length());
|
||||||
|
byte[] written = new byte[SEED_BYTES];
|
||||||
|
FileInputStream in = new FileInputStream(urandom);
|
||||||
|
IoUtils.read(in, written);
|
||||||
|
in.close();
|
||||||
|
assertArrayEquals(seed, written);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEngineNextBytesReadsFromRandomDevice() throws Exception {
|
||||||
|
if (!(OsUtils.isLinux())) {
|
||||||
|
System.err.println("WARNING: Skipping test, can't run on this OS");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Generate some entropy
|
||||||
|
byte[] entropy = TestUtils.getRandomBytes(SEED_BYTES);
|
||||||
|
// Write the entropy to a file
|
||||||
|
File urandom = new File(testDir, "urandom");
|
||||||
|
urandom.delete();
|
||||||
|
FileOutputStream out = new FileOutputStream(urandom);
|
||||||
|
out.write(entropy);
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
assertTrue(urandom.exists());
|
||||||
|
assertEquals(SEED_BYTES, urandom.length());
|
||||||
|
// Check that the engine reads from the file
|
||||||
|
LinuxSecureRandomSpi engine = new LinuxSecureRandomSpi(urandom,
|
||||||
|
RANDOM_DEVICE);
|
||||||
|
byte[] b = new byte[SEED_BYTES];
|
||||||
|
engine.engineNextBytes(b);
|
||||||
|
assertArrayEquals(entropy, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEngineGenerateSeedReadsFromRandomDevice() throws Exception {
|
||||||
|
if (!(OsUtils.isLinux())) {
|
||||||
|
System.err.println("WARNING: Skipping test, can't run on this OS");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Generate some entropy
|
||||||
|
byte[] entropy = TestUtils.getRandomBytes(SEED_BYTES);
|
||||||
|
// Write the entropy to a file
|
||||||
|
File urandom = new File(testDir, "urandom");
|
||||||
|
urandom.delete();
|
||||||
|
FileOutputStream out = new FileOutputStream(urandom);
|
||||||
|
out.write(entropy);
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
assertTrue(urandom.exists());
|
||||||
|
assertEquals(SEED_BYTES, urandom.length());
|
||||||
|
// Check that the engine reads from the file
|
||||||
|
LinuxSecureRandomSpi engine = new LinuxSecureRandomSpi(urandom,
|
||||||
|
RANDOM_DEVICE);
|
||||||
|
byte[] b = engine.engineGenerateSeed(SEED_BYTES);
|
||||||
|
assertArrayEquals(entropy, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
TestUtils.deleteTestDirectory(testDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
package org.briarproject.bramble.system;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.Bytes;
|
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
|
||||||
import org.briarproject.bramble.util.OsUtils;
|
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.system.SeedProvider.SEED_BYTES;
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
public class LinuxSeedProviderTest extends BrambleTestCase {
|
|
||||||
|
|
||||||
private final File testDir = TestUtils.getTestDirectory();
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
testDir.mkdirs();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSeedAppearsSane() {
|
|
||||||
if (!(OsUtils.isLinux())) {
|
|
||||||
System.err.println("WARNING: Skipping test, can't run on this OS");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Set<Bytes> seeds = new HashSet<Bytes>();
|
|
||||||
LinuxSeedProvider p = new LinuxSeedProvider();
|
|
||||||
for (int i = 0; i < 1000; i++) {
|
|
||||||
byte[] seed = p.getSeed();
|
|
||||||
assertEquals(SEED_BYTES, seed.length);
|
|
||||||
assertTrue(seeds.add(new Bytes(seed)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEntropyIsWrittenToPool() throws Exception {
|
|
||||||
if (!(OsUtils.isLinux())) {
|
|
||||||
System.err.println("WARNING: Skipping test, can't run on this OS");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Redirect the provider's entropy to a file
|
|
||||||
File urandom = new File(testDir, "urandom");
|
|
||||||
urandom.delete();
|
|
||||||
assertTrue(urandom.createNewFile());
|
|
||||||
assertEquals(0, urandom.length());
|
|
||||||
String path = urandom.getAbsolutePath();
|
|
||||||
LinuxSeedProvider p = new LinuxSeedProvider(path, "/dev/urandom");
|
|
||||||
p.getSeed();
|
|
||||||
// There should be 16 bytes from the clock, plus network interfaces
|
|
||||||
assertTrue(urandom.length() > 20);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSeedIsReadFromPool() throws Exception {
|
|
||||||
if (!(OsUtils.isLinux())) {
|
|
||||||
System.err.println("WARNING: Skipping test, can't run on this OS");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Generate a seed
|
|
||||||
byte[] seed = TestUtils.getRandomBytes(SEED_BYTES);
|
|
||||||
// Write the seed to a file
|
|
||||||
File urandom = new File(testDir, "urandom");
|
|
||||||
urandom.delete();
|
|
||||||
FileOutputStream out = new FileOutputStream(urandom);
|
|
||||||
out.write(seed);
|
|
||||||
out.flush();
|
|
||||||
out.close();
|
|
||||||
assertTrue(urandom.exists());
|
|
||||||
assertEquals(SEED_BYTES, urandom.length());
|
|
||||||
// Check that the provider reads the seed from the file
|
|
||||||
String path = urandom.getAbsolutePath();
|
|
||||||
LinuxSeedProvider p = new LinuxSeedProvider("/dev/urandom", path);
|
|
||||||
assertArrayEquals(seed, p.getSeed());
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
public void tearDown() {
|
|
||||||
TestUtils.deleteTestDirectory(testDir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package org.briarproject.bramble.test;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||||
|
|
||||||
|
import java.security.Provider;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
public class TestSecureRandomProvider implements SecureRandomProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Provider getProvider() {
|
||||||
|
// Use the default provider
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package org.briarproject.bramble.test;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.system.SeedProvider;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
public class TestSeedProvider implements SeedProvider {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] getSeed() {
|
|
||||||
return TestUtils.getRandomBytes(32);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package org.briarproject.bramble.test;
|
package org.briarproject.bramble.test;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.system.SeedProvider;
|
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ public class TestSeedProviderModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
SeedProvider provideSeedProvider() {
|
SecureRandomProvider provideSeedProvider() {
|
||||||
return new TestSeedProvider();
|
return new TestSecureRandomProvider();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package org.briarproject.bramble.system;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||||
|
import org.briarproject.bramble.util.OsUtils;
|
||||||
|
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.Provides;
|
||||||
|
|
||||||
|
@Module
|
||||||
|
public class DesktopSecureRandomModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
SecureRandomProvider provideSecureRandomProvider() {
|
||||||
|
return OsUtils.isLinux() ? new LinuxSecureRandomProvider() : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package org.briarproject.bramble.system;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.system.SeedProvider;
|
|
||||||
import org.briarproject.bramble.util.OsUtils;
|
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import dagger.Module;
|
|
||||||
import dagger.Provides;
|
|
||||||
|
|
||||||
@Module
|
|
||||||
public class DesktopSeedProviderModule {
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
SeedProvider provideSeedProvider() {
|
|
||||||
return OsUtils.isLinux() ? new LinuxSeedProvider() : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
<application
|
<application
|
||||||
android:name=".android.BriarApplicationImpl"
|
android:name=".android.BriarApplicationImpl"
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
android:icon="@drawable/ic_launcher"
|
android:icon="@mipmap/ic_launcher_round"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:logo="@mipmap/ic_launcher_round"
|
android:logo="@mipmap/ic_launcher_round"
|
||||||
android:theme="@style/BriarTheme">
|
android:theme="@style/BriarTheme">
|
||||||
@@ -333,7 +333,8 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name=".android.settings.SettingsActivity"
|
android:name=".android.settings.SettingsActivity"
|
||||||
android:label="@string/settings_button"
|
android:label="@string/settings_button"
|
||||||
android:parentActivityName=".android.navdrawer.NavDrawerActivity">
|
android:parentActivityName=".android.navdrawer.NavDrawerActivity"
|
||||||
|
android:permission="android.permission.READ_NETWORK_USAGE_HISTORY">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".android.navdrawer.NavDrawerActivity"
|
android:value=".android.navdrawer.NavDrawerActivity"
|
||||||
@@ -356,13 +357,12 @@
|
|||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.panic.PanicPreferencesActivity"
|
android:name=".android.panic.PanicPreferencesActivity"
|
||||||
android:label="@string/panic_setting">
|
android:label="@string/panic_setting"
|
||||||
<intent-filter>
|
android:parentActivityName=".android.settings.SettingsActivity">
|
||||||
<action android:name="info.guardianproject.panic.action.CONNECT"/>
|
<meta-data
|
||||||
<action android:name="info.guardianproject.panic.action.DISCONNECT"/>
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
|
android:value=".android.settings.SettingsActivity"
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
/>
|
||||||
</intent-filter>
|
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import org.briarproject.briar.BriarCoreModule;
|
|||||||
import org.briarproject.briar.android.reporting.BriarReportSender;
|
import org.briarproject.briar.android.reporting.BriarReportSender;
|
||||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||||
import org.briarproject.briar.api.android.ReferenceManager;
|
import org.briarproject.briar.api.android.ReferenceManager;
|
||||||
|
import org.briarproject.briar.api.android.ScreenFilterMonitor;
|
||||||
import org.briarproject.briar.api.blog.BlogManager;
|
import org.briarproject.briar.api.blog.BlogManager;
|
||||||
import org.briarproject.briar.api.blog.BlogPostFactory;
|
import org.briarproject.briar.api.blog.BlogPostFactory;
|
||||||
import org.briarproject.briar.api.blog.BlogSharingManager;
|
import org.briarproject.briar.api.blog.BlogSharingManager;
|
||||||
@@ -43,6 +44,8 @@ import org.briarproject.briar.api.privategroup.PrivateGroupFactory;
|
|||||||
import org.briarproject.briar.api.privategroup.PrivateGroupManager;
|
import org.briarproject.briar.api.privategroup.PrivateGroupManager;
|
||||||
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationFactory;
|
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.thoughtcrime.securesms.components.emoji.EmojiProvider;
|
||||||
|
import org.thoughtcrime.securesms.components.emoji.RecentEmojiPageModel;
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
@@ -87,6 +90,8 @@ public interface AndroidComponent
|
|||||||
|
|
||||||
AndroidNotificationManager androidNotificationManager();
|
AndroidNotificationManager androidNotificationManager();
|
||||||
|
|
||||||
|
ScreenFilterMonitor screenFilterMonitor();
|
||||||
|
|
||||||
ConnectionRegistry connectionRegistry();
|
ConnectionRegistry connectionRegistry();
|
||||||
|
|
||||||
ContactManager contactManager();
|
ContactManager contactManager();
|
||||||
@@ -138,10 +143,14 @@ public interface AndroidComponent
|
|||||||
@IoExecutor
|
@IoExecutor
|
||||||
Executor ioExecutor();
|
Executor ioExecutor();
|
||||||
|
|
||||||
void inject(BriarService activity);
|
void inject(BriarService briarService);
|
||||||
|
|
||||||
void inject(BriarReportSender briarReportSender);
|
void inject(BriarReportSender briarReportSender);
|
||||||
|
|
||||||
|
void inject(EmojiProvider emojiProvider);
|
||||||
|
|
||||||
|
void inject(RecentEmojiPageModel recentEmojiPageModel);
|
||||||
|
|
||||||
// Eager singleton load
|
// Eager singleton load
|
||||||
void inject(AppModule.EagerSingletons init);
|
void inject(AppModule.EagerSingletons init);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,8 @@ package org.briarproject.briar.android;
|
|||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
|
||||||
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.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
@@ -73,8 +70,6 @@ import static org.briarproject.briar.android.navdrawer.NavDrawerActivity.INTENT_
|
|||||||
import static org.briarproject.briar.android.navdrawer.NavDrawerActivity.INTENT_CONTACTS;
|
import static org.briarproject.briar.android.navdrawer.NavDrawerActivity.INTENT_CONTACTS;
|
||||||
import static org.briarproject.briar.android.navdrawer.NavDrawerActivity.INTENT_FORUMS;
|
import static org.briarproject.briar.android.navdrawer.NavDrawerActivity.INTENT_FORUMS;
|
||||||
import static org.briarproject.briar.android.navdrawer.NavDrawerActivity.INTENT_GROUPS;
|
import static org.briarproject.briar.android.navdrawer.NavDrawerActivity.INTENT_GROUPS;
|
||||||
import static org.briarproject.briar.android.settings.SettingsFragment.PREF_NOTIFY_BLOG;
|
|
||||||
import static org.briarproject.briar.android.settings.SettingsFragment.PREF_NOTIFY_GROUP;
|
|
||||||
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
|
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
|
||||||
|
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
@@ -92,25 +87,13 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
|
|
||||||
// Content URIs to differentiate between pending intents
|
// Content URIs to differentiate between pending intents
|
||||||
private static final String CONTACT_URI =
|
private static final String CONTACT_URI =
|
||||||
"content://org.briarproject/contact";
|
"content://org.briarproject.briar/contact";
|
||||||
private static final String GROUP_URI =
|
private static final String GROUP_URI =
|
||||||
"content://org.briarproject/group";
|
"content://org.briarproject.briar/group";
|
||||||
private static final String FORUM_URI =
|
private static final String FORUM_URI =
|
||||||
"content://org.briarproject/forum";
|
"content://org.briarproject.briar/forum";
|
||||||
private static final String BLOG_URI =
|
private static final String BLOG_URI =
|
||||||
"content://org.briarproject/blog";
|
"content://org.briarproject.briar/blog";
|
||||||
|
|
||||||
// Actions for intents that are broadcast when notifications are dismissed
|
|
||||||
private static final String CLEAR_PRIVATE_MESSAGE_ACTION =
|
|
||||||
"org.briarproject.briar.CLEAR_PRIVATE_MESSAGE_NOTIFICATION";
|
|
||||||
private static final String CLEAR_GROUP_ACTION =
|
|
||||||
"org.briarproject.briar.CLEAR_GROUP_NOTIFICATION";
|
|
||||||
private static final String CLEAR_FORUM_ACTION =
|
|
||||||
"org.briarproject.briar.CLEAR_FORUM_NOTIFICATION";
|
|
||||||
private static final String CLEAR_BLOG_ACTION =
|
|
||||||
"org.briarproject.briar.CLEAR_BLOG_NOTIFICATION";
|
|
||||||
private static final String CLEAR_INTRODUCTION_ACTION =
|
|
||||||
"org.briarproject.briar.CLEAR_INTRODUCTION_NOTIFICATION";
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(AndroidNotificationManagerImpl.class.getName());
|
Logger.getLogger(AndroidNotificationManagerImpl.class.getName());
|
||||||
@@ -119,7 +102,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
private final SettingsManager settingsManager;
|
private final SettingsManager settingsManager;
|
||||||
private final AndroidExecutor androidExecutor;
|
private final AndroidExecutor androidExecutor;
|
||||||
private final Context appContext;
|
private final Context appContext;
|
||||||
private final BroadcastReceiver receiver = new DeleteIntentReceiver();
|
|
||||||
private final AtomicBoolean used = new AtomicBoolean(false);
|
private final AtomicBoolean used = new AtomicBoolean(false);
|
||||||
|
|
||||||
// The following must only be accessed on the main UI thread
|
// The following must only be accessed on the main UI thread
|
||||||
@@ -157,30 +139,11 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
throw new ServiceException(e);
|
throw new ServiceException(e);
|
||||||
}
|
}
|
||||||
// Register a broadcast receiver for notifications being dismissed
|
|
||||||
Future<Void> f = androidExecutor.runOnUiThread(new Callable<Void>() {
|
|
||||||
@Override
|
|
||||||
public Void call() {
|
|
||||||
IntentFilter filter = new IntentFilter();
|
|
||||||
filter.addAction(CLEAR_PRIVATE_MESSAGE_ACTION);
|
|
||||||
filter.addAction(CLEAR_GROUP_ACTION);
|
|
||||||
filter.addAction(CLEAR_FORUM_ACTION);
|
|
||||||
filter.addAction(CLEAR_BLOG_ACTION);
|
|
||||||
filter.addAction(CLEAR_INTRODUCTION_ACTION);
|
|
||||||
appContext.registerReceiver(receiver, filter);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
try {
|
|
||||||
f.get();
|
|
||||||
} catch (InterruptedException | ExecutionException e) {
|
|
||||||
throw new ServiceException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stopService() throws ServiceException {
|
public void stopService() throws ServiceException {
|
||||||
// Clear all notifications and unregister the broadcast receiver
|
// Clear all notifications
|
||||||
Future<Void> f = androidExecutor.runOnUiThread(new Callable<Void>() {
|
Future<Void> f = androidExecutor.runOnUiThread(new Callable<Void>() {
|
||||||
@Override
|
@Override
|
||||||
public Void call() {
|
public Void call() {
|
||||||
@@ -189,7 +152,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
clearForumPostNotification();
|
clearForumPostNotification();
|
||||||
clearBlogPostNotification();
|
clearBlogPostNotification();
|
||||||
clearIntroductionSuccessNotification();
|
clearIntroductionSuccessNotification();
|
||||||
appContext.unregisterReceiver(receiver);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -325,27 +287,23 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
private void updateContactNotification() {
|
private void updateContactNotification() {
|
||||||
if (contactTotal == 0) {
|
if (contactTotal == 0) {
|
||||||
clearContactNotification();
|
clearContactNotification();
|
||||||
} else if (settings.getBoolean("notifyPrivateMessages", true)) {
|
} else if (settings.getBoolean(PREF_NOTIFY_PRIVATE, true)) {
|
||||||
NotificationCompat.Builder b =
|
NotificationCompat.Builder b =
|
||||||
new NotificationCompat.Builder(appContext);
|
new NotificationCompat.Builder(appContext);
|
||||||
b.setSmallIcon(R.drawable.notification_private_message);
|
b.setSmallIcon(R.drawable.notification_private_message);
|
||||||
b.setColor(ContextCompat.getColor(appContext, R.color.briar_primary));
|
b.setColor(ContextCompat.getColor(appContext,
|
||||||
|
R.color.briar_primary));
|
||||||
b.setContentTitle(appContext.getText(R.string.app_name));
|
b.setContentTitle(appContext.getText(R.string.app_name));
|
||||||
b.setContentText(appContext.getResources().getQuantityString(
|
b.setContentText(appContext.getResources().getQuantityString(
|
||||||
R.plurals.private_message_notification_text, contactTotal,
|
R.plurals.private_message_notification_text, contactTotal,
|
||||||
contactTotal));
|
contactTotal));
|
||||||
boolean sound = settings.getBoolean("notifySound", true);
|
boolean sound = settings.getBoolean(PREF_NOTIFY_SOUND, true);
|
||||||
String ringtoneUri = settings.get("notifyRingtoneUri");
|
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI);
|
||||||
if (sound && !StringUtils.isNullOrEmpty(ringtoneUri))
|
if (sound && !StringUtils.isNullOrEmpty(ringtoneUri))
|
||||||
b.setSound(Uri.parse(ringtoneUri));
|
b.setSound(Uri.parse(ringtoneUri));
|
||||||
b.setDefaults(getDefaults());
|
b.setDefaults(getDefaults());
|
||||||
b.setOnlyAlertOnce(true);
|
b.setOnlyAlertOnce(true);
|
||||||
b.setAutoCancel(true);
|
b.setAutoCancel(true);
|
||||||
// Clear the counters if the notification is dismissed
|
|
||||||
Intent clear = new Intent(CLEAR_PRIVATE_MESSAGE_ACTION);
|
|
||||||
PendingIntent delete = PendingIntent.getBroadcast(appContext, 0,
|
|
||||||
clear, 0);
|
|
||||||
b.setDeleteIntent(delete);
|
|
||||||
if (contactCounts.size() == 1) {
|
if (contactCounts.size() == 1) {
|
||||||
// Touching the notification shows the relevant conversation
|
// Touching the notification shows the relevant conversation
|
||||||
Intent i = new Intent(appContext, ConversationActivity.class);
|
Intent i = new Intent(appContext, ConversationActivity.class);
|
||||||
@@ -381,11 +339,11 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
@UiThread
|
@UiThread
|
||||||
private int getDefaults() {
|
private int getDefaults() {
|
||||||
int defaults = DEFAULT_LIGHTS;
|
int defaults = DEFAULT_LIGHTS;
|
||||||
boolean sound = settings.getBoolean("notifySound", true);
|
boolean sound = settings.getBoolean(PREF_NOTIFY_SOUND, true);
|
||||||
String ringtoneUri = settings.get("notifyRingtoneUri");
|
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI);
|
||||||
if (sound && StringUtils.isNullOrEmpty(ringtoneUri))
|
if (sound && StringUtils.isNullOrEmpty(ringtoneUri))
|
||||||
defaults |= DEFAULT_SOUND;
|
defaults |= DEFAULT_SOUND;
|
||||||
if (settings.getBoolean("notifyVibration", true))
|
if (settings.getBoolean(PREF_NOTIFY_VIBRATION, true))
|
||||||
defaults |= DEFAULT_VIBRATE;
|
defaults |= DEFAULT_VIBRATE;
|
||||||
return defaults;
|
return defaults;
|
||||||
}
|
}
|
||||||
@@ -438,22 +396,18 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
NotificationCompat.Builder b =
|
NotificationCompat.Builder b =
|
||||||
new NotificationCompat.Builder(appContext);
|
new NotificationCompat.Builder(appContext);
|
||||||
b.setSmallIcon(R.drawable.notification_private_group);
|
b.setSmallIcon(R.drawable.notification_private_group);
|
||||||
b.setColor(ContextCompat.getColor(appContext, R.color.briar_primary));
|
b.setColor(ContextCompat.getColor(appContext,
|
||||||
|
R.color.briar_primary));
|
||||||
b.setContentTitle(appContext.getText(R.string.app_name));
|
b.setContentTitle(appContext.getText(R.string.app_name));
|
||||||
b.setContentText(appContext.getResources().getQuantityString(
|
b.setContentText(appContext.getResources().getQuantityString(
|
||||||
R.plurals.group_message_notification_text, groupTotal,
|
R.plurals.group_message_notification_text, groupTotal,
|
||||||
groupTotal));
|
groupTotal));
|
||||||
String ringtoneUri = settings.get("notifyRingtoneUri");
|
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI);
|
||||||
if (!StringUtils.isNullOrEmpty(ringtoneUri))
|
if (!StringUtils.isNullOrEmpty(ringtoneUri))
|
||||||
b.setSound(Uri.parse(ringtoneUri));
|
b.setSound(Uri.parse(ringtoneUri));
|
||||||
b.setDefaults(getDefaults());
|
b.setDefaults(getDefaults());
|
||||||
b.setOnlyAlertOnce(true);
|
b.setOnlyAlertOnce(true);
|
||||||
b.setAutoCancel(true);
|
b.setAutoCancel(true);
|
||||||
// Clear the counters if the notification is dismissed
|
|
||||||
Intent clear = new Intent(CLEAR_GROUP_ACTION);
|
|
||||||
PendingIntent delete = PendingIntent.getBroadcast(appContext, 0,
|
|
||||||
clear, 0);
|
|
||||||
b.setDeleteIntent(delete);
|
|
||||||
if (groupCounts.size() == 1) {
|
if (groupCounts.size() == 1) {
|
||||||
// Touching the notification shows the relevant group
|
// Touching the notification shows the relevant group
|
||||||
Intent i = new Intent(appContext, GroupActivity.class);
|
Intent i = new Intent(appContext, GroupActivity.class);
|
||||||
@@ -530,26 +484,22 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
private void updateForumPostNotification() {
|
private void updateForumPostNotification() {
|
||||||
if (forumTotal == 0) {
|
if (forumTotal == 0) {
|
||||||
clearForumPostNotification();
|
clearForumPostNotification();
|
||||||
} else if (settings.getBoolean("notifyForumPosts", true)) {
|
} else if (settings.getBoolean(PREF_NOTIFY_FORUM, true)) {
|
||||||
NotificationCompat.Builder b =
|
NotificationCompat.Builder b =
|
||||||
new NotificationCompat.Builder(appContext);
|
new NotificationCompat.Builder(appContext);
|
||||||
b.setSmallIcon(R.drawable.notification_forum);
|
b.setSmallIcon(R.drawable.notification_forum);
|
||||||
b.setColor(ContextCompat.getColor(appContext, R.color.briar_primary));
|
b.setColor(ContextCompat.getColor(appContext,
|
||||||
|
R.color.briar_primary));
|
||||||
b.setContentTitle(appContext.getText(R.string.app_name));
|
b.setContentTitle(appContext.getText(R.string.app_name));
|
||||||
b.setContentText(appContext.getResources().getQuantityString(
|
b.setContentText(appContext.getResources().getQuantityString(
|
||||||
R.plurals.forum_post_notification_text, forumTotal,
|
R.plurals.forum_post_notification_text, forumTotal,
|
||||||
forumTotal));
|
forumTotal));
|
||||||
String ringtoneUri = settings.get("notifyRingtoneUri");
|
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI);
|
||||||
if (!StringUtils.isNullOrEmpty(ringtoneUri))
|
if (!StringUtils.isNullOrEmpty(ringtoneUri))
|
||||||
b.setSound(Uri.parse(ringtoneUri));
|
b.setSound(Uri.parse(ringtoneUri));
|
||||||
b.setDefaults(getDefaults());
|
b.setDefaults(getDefaults());
|
||||||
b.setOnlyAlertOnce(true);
|
b.setOnlyAlertOnce(true);
|
||||||
b.setAutoCancel(true);
|
b.setAutoCancel(true);
|
||||||
// Clear the counters if the notification is dismissed
|
|
||||||
Intent clear = new Intent(CLEAR_FORUM_ACTION);
|
|
||||||
PendingIntent delete = PendingIntent.getBroadcast(appContext, 0,
|
|
||||||
clear, 0);
|
|
||||||
b.setDeleteIntent(delete);
|
|
||||||
if (forumCounts.size() == 1) {
|
if (forumCounts.size() == 1) {
|
||||||
// Touching the notification shows the relevant forum
|
// Touching the notification shows the relevant forum
|
||||||
Intent i = new Intent(appContext, ForumActivity.class);
|
Intent i = new Intent(appContext, ForumActivity.class);
|
||||||
@@ -630,22 +580,18 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
NotificationCompat.Builder b =
|
NotificationCompat.Builder b =
|
||||||
new NotificationCompat.Builder(appContext);
|
new NotificationCompat.Builder(appContext);
|
||||||
b.setSmallIcon(R.drawable.notification_blog);
|
b.setSmallIcon(R.drawable.notification_blog);
|
||||||
b.setColor(ContextCompat.getColor(appContext, R.color.briar_primary));
|
b.setColor(ContextCompat.getColor(appContext,
|
||||||
|
R.color.briar_primary));
|
||||||
b.setContentTitle(appContext.getText(R.string.app_name));
|
b.setContentTitle(appContext.getText(R.string.app_name));
|
||||||
b.setContentText(appContext.getResources().getQuantityString(
|
b.setContentText(appContext.getResources().getQuantityString(
|
||||||
R.plurals.blog_post_notification_text, blogTotal,
|
R.plurals.blog_post_notification_text, blogTotal,
|
||||||
blogTotal));
|
blogTotal));
|
||||||
String ringtoneUri = settings.get("notifyRingtoneUri");
|
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI);
|
||||||
if (!StringUtils.isNullOrEmpty(ringtoneUri))
|
if (!StringUtils.isNullOrEmpty(ringtoneUri))
|
||||||
b.setSound(Uri.parse(ringtoneUri));
|
b.setSound(Uri.parse(ringtoneUri));
|
||||||
b.setDefaults(getDefaults());
|
b.setDefaults(getDefaults());
|
||||||
b.setOnlyAlertOnce(true);
|
b.setOnlyAlertOnce(true);
|
||||||
b.setAutoCancel(true);
|
b.setAutoCancel(true);
|
||||||
// Clear the counters if the notification is dismissed
|
|
||||||
Intent clear = new Intent(CLEAR_BLOG_ACTION);
|
|
||||||
PendingIntent delete = PendingIntent.getBroadcast(appContext, 0,
|
|
||||||
clear, 0);
|
|
||||||
b.setDeleteIntent(delete);
|
|
||||||
// Touching the notification shows the combined blog feed
|
// Touching the notification shows the combined blog feed
|
||||||
Intent i = new Intent(appContext, NavDrawerActivity.class);
|
Intent i = new Intent(appContext, NavDrawerActivity.class);
|
||||||
i.putExtra(INTENT_BLOGS, true);
|
i.putExtra(INTENT_BLOGS, true);
|
||||||
@@ -696,17 +642,12 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
b.setContentText(appContext.getResources().getQuantityString(
|
b.setContentText(appContext.getResources().getQuantityString(
|
||||||
R.plurals.introduction_notification_text, introductionTotal,
|
R.plurals.introduction_notification_text, introductionTotal,
|
||||||
introductionTotal));
|
introductionTotal));
|
||||||
String ringtoneUri = settings.get("notifyRingtoneUri");
|
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI);
|
||||||
if (!StringUtils.isNullOrEmpty(ringtoneUri))
|
if (!StringUtils.isNullOrEmpty(ringtoneUri))
|
||||||
b.setSound(Uri.parse(ringtoneUri));
|
b.setSound(Uri.parse(ringtoneUri));
|
||||||
b.setDefaults(getDefaults());
|
b.setDefaults(getDefaults());
|
||||||
b.setOnlyAlertOnce(true);
|
b.setOnlyAlertOnce(true);
|
||||||
b.setAutoCancel(true);
|
b.setAutoCancel(true);
|
||||||
// Clear the counter if the notification is dismissed
|
|
||||||
Intent clear = new Intent(CLEAR_INTRODUCTION_ACTION);
|
|
||||||
PendingIntent delete = PendingIntent.getBroadcast(appContext, 0,
|
|
||||||
clear, 0);
|
|
||||||
b.setDeleteIntent(delete);
|
|
||||||
// Touching the notification shows the contact list
|
// Touching the notification shows the contact list
|
||||||
Intent i = new Intent(appContext, NavDrawerActivity.class);
|
Intent i = new Intent(appContext, NavDrawerActivity.class);
|
||||||
i.putExtra(INTENT_CONTACTS, true);
|
i.putExtra(INTENT_CONTACTS, true);
|
||||||
@@ -847,27 +788,4 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DeleteIntentReceiver extends BroadcastReceiver {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
final String action = intent.getAction();
|
|
||||||
androidExecutor.runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (CLEAR_PRIVATE_MESSAGE_ACTION.equals(action)) {
|
|
||||||
clearContactNotification();
|
|
||||||
} else if (CLEAR_GROUP_ACTION.equals(action)) {
|
|
||||||
clearGroupMessageNotification();
|
|
||||||
} else if (CLEAR_FORUM_ACTION.equals(action)) {
|
|
||||||
clearForumPostNotification();
|
|
||||||
} else if (CLEAR_BLOG_ACTION.equals(action)) {
|
|
||||||
clearBlogPostNotification();
|
|
||||||
} else if (CLEAR_INTRODUCTION_ACTION.equals(action)) {
|
|
||||||
clearIntroductionSuccessNotification();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import org.briarproject.bramble.api.ui.UiCallback;
|
|||||||
import org.briarproject.bramble.util.StringUtils;
|
import org.briarproject.bramble.util.StringUtils;
|
||||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||||
import org.briarproject.briar.api.android.ReferenceManager;
|
import org.briarproject.briar.api.android.ReferenceManager;
|
||||||
|
import org.briarproject.briar.api.android.ScreenFilterMonitor;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
@@ -37,6 +38,8 @@ public class AppModule {
|
|||||||
static class EagerSingletons {
|
static class EagerSingletons {
|
||||||
@Inject
|
@Inject
|
||||||
AndroidNotificationManager androidNotificationManager;
|
AndroidNotificationManager androidNotificationManager;
|
||||||
|
@Inject
|
||||||
|
ScreenFilterMonitor screenFilterMonitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Application application;
|
private final Application application;
|
||||||
@@ -166,4 +169,12 @@ public class AppModule {
|
|||||||
eventBus.addListener(notificationManager);
|
eventBus.addListener(notificationManager);
|
||||||
return notificationManager;
|
return notificationManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
ScreenFilterMonitor provideScreenFilterMonitor(
|
||||||
|
LifecycleManager lifecycleManager, ScreenFilterMonitorImpl sfm) {
|
||||||
|
lifecycleManager.registerService(sfm);
|
||||||
|
return sfm;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,232 @@
|
|||||||
|
package org.briarproject.briar.android;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
|
import android.content.pm.PackageInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.annotation.UiThread;
|
||||||
|
import android.support.v7.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.lifecycle.Service;
|
||||||
|
import org.briarproject.bramble.api.lifecycle.ServiceException;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||||
|
import org.briarproject.briar.api.android.ScreenFilterMonitor;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
|
||||||
|
|
||||||
|
@MethodsNotNullByDefault
|
||||||
|
@ParametersNotNullByDefault
|
||||||
|
public class ScreenFilterMonitorImpl extends BroadcastReceiver
|
||||||
|
implements Service,
|
||||||
|
ScreenFilterMonitor {
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
Logger.getLogger(ScreenFilterMonitorImpl.class.getName());
|
||||||
|
private static final String PREF_SCREEN_FILTER_APPS =
|
||||||
|
"shownScreenFilterApps";
|
||||||
|
private final Context appContext;
|
||||||
|
private final AndroidExecutor androidExecutor;
|
||||||
|
private final LinkedList<String> appNames = new LinkedList<>();
|
||||||
|
private final PackageManager pm;
|
||||||
|
private final SharedPreferences prefs;
|
||||||
|
private final AtomicBoolean used = new AtomicBoolean(false);
|
||||||
|
private final Set<String> apps = new HashSet<>();
|
||||||
|
private final Set<String> shownApps;
|
||||||
|
// Used solely for the UiThread
|
||||||
|
private boolean serviceStarted = false;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ScreenFilterMonitorImpl(AndroidExecutor executor, Application app) {
|
||||||
|
this.androidExecutor = executor;
|
||||||
|
this.appContext = app;
|
||||||
|
pm = appContext.getPackageManager();
|
||||||
|
prefs = PreferenceManager.getDefaultSharedPreferences(appContext);
|
||||||
|
shownApps = getShownScreenFilterApps();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startService() throws ServiceException {
|
||||||
|
if (used.getAndSet(true)) throw new IllegalStateException();
|
||||||
|
Future<Void> f = androidExecutor.runOnUiThread(new Callable<Void>() {
|
||||||
|
@Override
|
||||||
|
public Void call() {
|
||||||
|
IntentFilter intentFilter = new IntentFilter();
|
||||||
|
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
|
||||||
|
intentFilter.addDataScheme("package");
|
||||||
|
appContext.registerReceiver(ScreenFilterMonitorImpl.this,
|
||||||
|
intentFilter);
|
||||||
|
apps.addAll(getInstalledScreenFilterApps());
|
||||||
|
serviceStarted = true;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
f.get();
|
||||||
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
|
throw new ServiceException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stopService() throws ServiceException {
|
||||||
|
Future<Void> f = androidExecutor.runOnUiThread(new Callable<Void>() {
|
||||||
|
@Override
|
||||||
|
public Void call() {
|
||||||
|
serviceStarted = false;
|
||||||
|
appContext.unregisterReceiver(ScreenFilterMonitorImpl.this);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
f.get();
|
||||||
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
|
throw new ServiceException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> getShownScreenFilterApps() {
|
||||||
|
// res must not be modified
|
||||||
|
Set<String> s =
|
||||||
|
prefs.getStringSet(PREF_SCREEN_FILTER_APPS, null);
|
||||||
|
HashSet<String> result = new HashSet<>();
|
||||||
|
if (s != null) {
|
||||||
|
result.addAll(s);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
|
||||||
|
final String packageName =
|
||||||
|
intent.getData().getEncodedSchemeSpecificPart();
|
||||||
|
androidExecutor.runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
String pkg = isOverlayApp(packageName);
|
||||||
|
if (pkg == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
apps.add(pkg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@UiThread
|
||||||
|
public Set<String> getApps() {
|
||||||
|
if (!serviceStarted) {
|
||||||
|
apps.addAll(getInstalledScreenFilterApps());
|
||||||
|
}
|
||||||
|
TreeSet<String> buf = new TreeSet<>();
|
||||||
|
if (apps.isEmpty()) {
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
buf.addAll(apps);
|
||||||
|
buf.removeAll(shownApps);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@UiThread
|
||||||
|
public void storeAppsAsShown(Collection<String> s, boolean persistent) {
|
||||||
|
HashSet<String> buf = new HashSet(s);
|
||||||
|
shownApps.addAll(buf);
|
||||||
|
if (persistent && !s.isEmpty()) {
|
||||||
|
buf.addAll(getShownScreenFilterApps());
|
||||||
|
prefs.edit()
|
||||||
|
.putStringSet(PREF_SCREEN_FILTER_APPS, buf)
|
||||||
|
.apply();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> getInstalledScreenFilterApps() {
|
||||||
|
HashSet<String> screenFilterApps = new HashSet<>();
|
||||||
|
List<PackageInfo> packageInfos =
|
||||||
|
pm.getInstalledPackages(PackageManager.GET_PERMISSIONS);
|
||||||
|
for (PackageInfo packageInfo : packageInfos) {
|
||||||
|
if (isOverlayApp(packageInfo)) {
|
||||||
|
String name = pkgToString(packageInfo);
|
||||||
|
if (name != null) {
|
||||||
|
screenFilterApps.add(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return screenFilterApps;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if pkg uses the SYSTEM_ALERT_WINDOW permission and if so
|
||||||
|
// returns the app name.
|
||||||
|
@Nullable
|
||||||
|
private String isOverlayApp(String pkg) {
|
||||||
|
try {
|
||||||
|
PackageInfo pkgInfo =
|
||||||
|
pm.getPackageInfo(pkg, PackageManager.GET_PERMISSIONS);
|
||||||
|
if (isOverlayApp(pkgInfo)) {
|
||||||
|
return pkgToString(pkgInfo);
|
||||||
|
}
|
||||||
|
} catch (PackageManager.NameNotFoundException ignored) {
|
||||||
|
if (LOG.isLoggable(Level.WARNING)) {
|
||||||
|
LOG.warning("Package name not found: " + pkg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the application name for a given package.
|
||||||
|
@Nullable
|
||||||
|
private String pkgToString(PackageInfo pkgInfo) {
|
||||||
|
CharSequence seq = pm.getApplicationLabel(pkgInfo.applicationInfo);
|
||||||
|
if (seq != null) {
|
||||||
|
return seq.toString();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if an installed pkg is a user app using the permission.
|
||||||
|
private boolean isOverlayApp(PackageInfo packageInfo) {
|
||||||
|
int mask = ApplicationInfo.FLAG_SYSTEM |
|
||||||
|
ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
|
||||||
|
// Ignore system apps
|
||||||
|
if ((packageInfo.applicationInfo.flags & mask) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//Get Permissions
|
||||||
|
String[] requestedPermissions =
|
||||||
|
packageInfo.requestedPermissions;
|
||||||
|
if (requestedPermissions != null) {
|
||||||
|
for (String requestedPermission : requestedPermissions) {
|
||||||
|
if (requestedPermission
|
||||||
|
.equals(SYSTEM_ALERT_WINDOW)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
package org.briarproject.briar.android;
|
package org.briarproject.briar.android;
|
||||||
|
|
||||||
|
import org.briarproject.briar.BuildConfig;
|
||||||
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
@@ -11,7 +13,7 @@ public interface TestingConstants {
|
|||||||
* Whether this is an alpha or beta build. This should be set to false for
|
* Whether this is an alpha or beta build. This should be set to false for
|
||||||
* release builds.
|
* release builds.
|
||||||
*/
|
*/
|
||||||
boolean TESTING = true;
|
boolean TESTING = BuildConfig.DEBUG;
|
||||||
|
|
||||||
/** Default log level. */
|
/** Default log level. */
|
||||||
Level DEFAULT_LOG_LEVEL = TESTING ? INFO : OFF;
|
Level DEFAULT_LOG_LEVEL = TESTING ? INFO : OFF;
|
||||||
|
|||||||
@@ -64,8 +64,6 @@ import org.briarproject.briar.android.sharing.ShareForumFragment;
|
|||||||
import org.briarproject.briar.android.sharing.ShareForumMessageFragment;
|
import org.briarproject.briar.android.sharing.ShareForumMessageFragment;
|
||||||
import org.briarproject.briar.android.sharing.SharingModule;
|
import org.briarproject.briar.android.sharing.SharingModule;
|
||||||
import org.briarproject.briar.android.splash.SplashScreenActivity;
|
import org.briarproject.briar.android.splash.SplashScreenActivity;
|
||||||
import org.thoughtcrime.securesms.components.emoji.EmojiProvider;
|
|
||||||
import org.thoughtcrime.securesms.components.emoji.RecentEmojiPageModel;
|
|
||||||
|
|
||||||
import dagger.Component;
|
import dagger.Component;
|
||||||
|
|
||||||
@@ -151,10 +149,6 @@ public interface ActivityComponent {
|
|||||||
|
|
||||||
void inject(RssFeedManageActivity activity);
|
void inject(RssFeedManageActivity activity);
|
||||||
|
|
||||||
void inject(EmojiProvider emojiProvider);
|
|
||||||
|
|
||||||
void inject(RecentEmojiPageModel recentEmojiPageModel);
|
|
||||||
|
|
||||||
// Fragments
|
// Fragments
|
||||||
void inject(ContactListFragment fragment);
|
void inject(ContactListFragment fragment);
|
||||||
|
|
||||||
|
|||||||
@@ -13,11 +13,15 @@ import org.briarproject.briar.android.BriarApplication;
|
|||||||
import org.briarproject.briar.android.DestroyableContext;
|
import org.briarproject.briar.android.DestroyableContext;
|
||||||
import org.briarproject.briar.android.controller.ActivityLifecycleController;
|
import org.briarproject.briar.android.controller.ActivityLifecycleController;
|
||||||
import org.briarproject.briar.android.forum.ForumModule;
|
import org.briarproject.briar.android.forum.ForumModule;
|
||||||
|
import org.briarproject.briar.android.fragment.SFDialogFragment;
|
||||||
|
import org.briarproject.briar.api.android.ScreenFilterMonitor;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
|
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
|
||||||
import static android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT;
|
import static android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT;
|
||||||
@@ -25,13 +29,16 @@ import static org.briarproject.briar.android.TestingConstants.PREVENT_SCREENSHOT
|
|||||||
|
|
||||||
public abstract class BaseActivity extends AppCompatActivity
|
public abstract class BaseActivity extends AppCompatActivity
|
||||||
implements DestroyableContext {
|
implements DestroyableContext {
|
||||||
|
|
||||||
protected ActivityComponent activityComponent;
|
protected ActivityComponent activityComponent;
|
||||||
|
|
||||||
private final List<ActivityLifecycleController> lifecycleControllers =
|
private final List<ActivityLifecycleController> lifecycleControllers =
|
||||||
new ArrayList<>();
|
new ArrayList<>();
|
||||||
private boolean destroyed = false;
|
private boolean destroyed = false;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
protected ScreenFilterMonitor screenFilterMonitor;
|
||||||
|
private SFDialogFragment dialogFrag;
|
||||||
|
|
||||||
public abstract void injectActivity(ActivityComponent component);
|
public abstract void injectActivity(ActivityComponent component);
|
||||||
|
|
||||||
public void addLifecycleController(ActivityLifecycleController alc) {
|
public void addLifecycleController(ActivityLifecycleController alc) {
|
||||||
@@ -58,6 +65,7 @@ public abstract class BaseActivity extends AppCompatActivity
|
|||||||
for (ActivityLifecycleController alc : lifecycleControllers) {
|
for (ActivityLifecycleController alc : lifecycleControllers) {
|
||||||
alc.onActivityCreate(this);
|
alc.onActivityCreate(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ActivityComponent getActivityComponent() {
|
public ActivityComponent getActivityComponent() {
|
||||||
@@ -89,6 +97,35 @@ public abstract class BaseActivity extends AppCompatActivity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostResume() {
|
||||||
|
super.onPostResume();
|
||||||
|
showNewScreenFilterWarning();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
if (dialogFrag != null) {
|
||||||
|
dialogFrag.dismiss();
|
||||||
|
dialogFrag = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void showNewScreenFilterWarning() {
|
||||||
|
final Set<String> apps = screenFilterMonitor.getApps();
|
||||||
|
if (apps.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dialogFrag = SFDialogFragment.newInstance(new ArrayList<>(apps));
|
||||||
|
dialogFrag.setCancelable(false);
|
||||||
|
dialogFrag.show(getSupportFragmentManager(), "SFDialog");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void rememberShownApps(ArrayList<String> s, boolean permanent) {
|
||||||
|
screenFilterMonitor.storeAppsAsShown(s, permanent);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import android.view.Window;
|
|||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.controller.BriarController;
|
import org.briarproject.briar.android.controller.BriarController;
|
||||||
import org.briarproject.briar.android.controller.DbController;
|
import org.briarproject.briar.android.controller.DbController;
|
||||||
import org.briarproject.briar.android.controller.handler.UiResultHandler;
|
import org.briarproject.briar.android.controller.handler.ResultHandler;
|
||||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||||
import org.briarproject.briar.android.login.PasswordActivity;
|
import org.briarproject.briar.android.login.PasswordActivity;
|
||||||
import org.briarproject.briar.android.panic.ExitActivity;
|
import org.briarproject.briar.android.panic.ExitActivity;
|
||||||
@@ -27,7 +27,6 @@ import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
|
|||||||
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
|
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.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
|
|
||||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PASSWORD;
|
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PASSWORD;
|
||||||
|
|
||||||
@SuppressLint("Registered")
|
@SuppressLint("Registered")
|
||||||
@@ -61,7 +60,6 @@ public abstract class BriarActivity extends BaseActivity {
|
|||||||
super.onStart();
|
super.onStart();
|
||||||
if (!briarController.hasEncryptionKey() && !isFinishing()) {
|
if (!briarController.hasEncryptionKey() && !isFinishing()) {
|
||||||
Intent i = new Intent(this, PasswordActivity.class);
|
Intent i = new Intent(this, PasswordActivity.class);
|
||||||
i.setFlags(FLAG_ACTIVITY_SINGLE_TOP);
|
|
||||||
startActivityForResult(i, REQUEST_PASSWORD);
|
startActivityForResult(i, REQUEST_PASSWORD);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -118,17 +116,28 @@ public abstract class BriarActivity extends BaseActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void signOut(final boolean removeFromRecentApps) {
|
protected void signOut(final boolean removeFromRecentApps) {
|
||||||
briarController.signOut(new UiResultHandler<Void>(this) {
|
if (briarController.hasEncryptionKey()) {
|
||||||
@Override
|
// Don't use UiResultHandler because we want the result even if
|
||||||
public void onResultUi(Void result) {
|
// this activity has been destroyed
|
||||||
if (removeFromRecentApps) startExitActivity();
|
briarController.signOut(new ResultHandler<Void>() {
|
||||||
else finishAndExit();
|
@Override
|
||||||
}
|
public void onResult(Void result) {
|
||||||
});
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
exit(removeFromRecentApps);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
exit(removeFromRecentApps);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void signOut() {
|
private void exit(boolean removeFromRecentApps) {
|
||||||
signOut(false);
|
if (removeFromRecentApps) startExitActivity();
|
||||||
|
else finishAndExit();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startExitActivity() {
|
private void startExitActivity() {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import org.briarproject.briar.api.blog.Blog;
|
|||||||
import org.briarproject.briar.api.blog.BlogCommentHeader;
|
import org.briarproject.briar.api.blog.BlogCommentHeader;
|
||||||
import org.briarproject.briar.api.blog.BlogManager;
|
import org.briarproject.briar.api.blog.BlogManager;
|
||||||
import org.briarproject.briar.api.blog.BlogPostHeader;
|
import org.briarproject.briar.api.blog.BlogPostHeader;
|
||||||
|
import org.briarproject.briar.util.HtmlUtils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -33,6 +34,7 @@ import javax.annotation.Nullable;
|
|||||||
|
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
|
import static org.briarproject.briar.util.HtmlUtils.ARTICLE;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
@@ -229,6 +231,7 @@ abstract class BaseControllerImpl extends DbControllerImpl
|
|||||||
return header;
|
return header;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DatabaseExecutor
|
||||||
private BlogPostItem getItem(BlogPostHeader h) throws DbException {
|
private BlogPostItem getItem(BlogPostHeader h) throws DbException {
|
||||||
String body;
|
String body;
|
||||||
if (h instanceof BlogCommentHeader) {
|
if (h instanceof BlogCommentHeader) {
|
||||||
@@ -243,10 +246,11 @@ abstract class BaseControllerImpl extends DbControllerImpl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DatabaseExecutor
|
||||||
private String getPostBody(MessageId m) throws DbException {
|
private String getPostBody(MessageId m) throws DbException {
|
||||||
String body = bodyCache.get(m);
|
String body = bodyCache.get(m);
|
||||||
if (body == null) {
|
if (body == null) {
|
||||||
body = blogManager.getPostBody(m);
|
body = HtmlUtils.clean(blogManager.getPostBody(m), ARTICLE);
|
||||||
bodyCache.put(m, body);
|
bodyCache.put(m, body);
|
||||||
}
|
}
|
||||||
//noinspection ConstantConditions
|
//noinspection ConstantConditions
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package org.briarproject.briar.android.fragment;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v4.app.DialogFragment;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.CheckBox;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
|
import org.briarproject.briar.R;
|
||||||
|
import org.briarproject.briar.android.activity.BaseActivity;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
@MethodsNotNullByDefault
|
||||||
|
@ParametersNotNullByDefault
|
||||||
|
public class SFDialogFragment extends DialogFragment {
|
||||||
|
|
||||||
|
public static SFDialogFragment newInstance(ArrayList<String> apps) {
|
||||||
|
SFDialogFragment frag = new SFDialogFragment();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putStringArrayList("apps", apps);
|
||||||
|
frag.setArguments(args);
|
||||||
|
return frag;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
||||||
|
AlertDialog.Builder builder =
|
||||||
|
new AlertDialog.Builder(
|
||||||
|
getActivity(),
|
||||||
|
R.style.BriarDialogThemeNoFilter);
|
||||||
|
builder.setTitle(R.string.screen_filter_title);
|
||||||
|
LayoutInflater li = getActivity().getLayoutInflater();
|
||||||
|
//Pass null here because it's an AlertDialog
|
||||||
|
View v =
|
||||||
|
li.inflate(R.layout.alert_dialog_checkbox, null,
|
||||||
|
false);
|
||||||
|
TextView t = (TextView) v.findViewById(R.id.alert_dialog_text);
|
||||||
|
final ArrayList<String> apps =
|
||||||
|
getArguments().getStringArrayList("apps");
|
||||||
|
t.setText(getString(R.string.screen_filter_body, TextUtils
|
||||||
|
.join("\n", apps)));
|
||||||
|
final CheckBox cb =
|
||||||
|
(CheckBox) v.findViewById(
|
||||||
|
R.id.checkBox_screen_filter_reminder);
|
||||||
|
builder.setNeutralButton(R.string.continue_button,
|
||||||
|
new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog,
|
||||||
|
int which) {
|
||||||
|
((BaseActivity) getActivity())
|
||||||
|
.rememberShownApps(apps, cb.isChecked());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
builder.setView(v);
|
||||||
|
return builder.create();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,7 +25,7 @@ import javax.inject.Inject;
|
|||||||
|
|
||||||
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 org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.WEAK;
|
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_WEAK;
|
||||||
|
|
||||||
public class ChangePasswordActivity extends BaseActivity
|
public class ChangePasswordActivity extends BaseActivity
|
||||||
implements OnClickListener, OnEditorActionListener {
|
implements OnClickListener, OnEditorActionListener {
|
||||||
@@ -109,13 +109,13 @@ public class ChangePasswordActivity extends BaseActivity
|
|||||||
strengthMeter.setStrength(strength);
|
strengthMeter.setStrength(strength);
|
||||||
UiUtils.setError(newPasswordEntryWrapper,
|
UiUtils.setError(newPasswordEntryWrapper,
|
||||||
getString(R.string.password_too_weak),
|
getString(R.string.password_too_weak),
|
||||||
firstPassword.length() > 0 && strength < WEAK);
|
firstPassword.length() > 0 && strength < QUITE_WEAK);
|
||||||
UiUtils.setError(newPasswordConfirmationWrapper,
|
UiUtils.setError(newPasswordConfirmationWrapper,
|
||||||
getString(R.string.passwords_do_not_match),
|
getString(R.string.passwords_do_not_match),
|
||||||
secondPassword.length() > 0 && !passwordsMatch);
|
secondPassword.length() > 0 && !passwordsMatch);
|
||||||
changePasswordButton.setEnabled(
|
changePasswordButton.setEnabled(
|
||||||
!currentPassword.getText().toString().isEmpty() &&
|
!currentPassword.getText().toString().isEmpty() &&
|
||||||
passwordsMatch && strength >= WEAK);
|
passwordsMatch && strength >= QUITE_WEAK);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import android.widget.TextView.OnEditorActionListener;
|
|||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
import org.briarproject.briar.android.activity.BaseActivity;
|
import org.briarproject.briar.android.activity.BaseActivity;
|
||||||
|
import org.briarproject.briar.android.controller.BriarController;
|
||||||
import org.briarproject.briar.android.controller.handler.UiResultHandler;
|
import org.briarproject.briar.android.controller.handler.UiResultHandler;
|
||||||
import org.briarproject.briar.android.util.UiUtils;
|
import org.briarproject.briar.android.util.UiUtils;
|
||||||
|
|
||||||
@@ -26,13 +27,18 @@ import javax.inject.Inject;
|
|||||||
|
|
||||||
import static android.content.Intent.ACTION_MAIN;
|
import static android.content.Intent.ACTION_MAIN;
|
||||||
import static android.content.Intent.CATEGORY_HOME;
|
import static android.content.Intent.CATEGORY_HOME;
|
||||||
|
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
|
||||||
|
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||||
import static android.view.View.INVISIBLE;
|
import static android.view.View.INVISIBLE;
|
||||||
import static android.view.View.VISIBLE;
|
import static android.view.View.VISIBLE;
|
||||||
|
|
||||||
public class PasswordActivity extends BaseActivity {
|
public class PasswordActivity extends BaseActivity {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
protected PasswordController passwordController;
|
PasswordController passwordController;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
BriarController briarController;
|
||||||
|
|
||||||
private Button signInButton;
|
private Button signInButton;
|
||||||
private ProgressBar progress;
|
private ProgressBar progress;
|
||||||
@@ -82,6 +88,16 @@ public class PasswordActivity extends BaseActivity {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
// If the user has already signed in, clean up this instance
|
||||||
|
if (briarController.hasEncryptionKey()) {
|
||||||
|
setResult(RESULT_OK);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void injectActivity(ActivityComponent component) {
|
public void injectActivity(ActivityComponent component) {
|
||||||
component.inject(this);
|
component.inject(this);
|
||||||
@@ -98,8 +114,9 @@ public class PasswordActivity extends BaseActivity {
|
|||||||
private void deleteAccount() {
|
private void deleteAccount() {
|
||||||
passwordController.deleteAccount(this);
|
passwordController.deleteAccount(this);
|
||||||
setResult(RESULT_CANCELED);
|
setResult(RESULT_CANCELED);
|
||||||
startActivity(new Intent(this, SetupActivity.class));
|
Intent i = new Intent(this, SetupActivity.class);
|
||||||
supportFinishAfterTransition();
|
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
|
||||||
|
startActivity(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onSignInClick(View v) {
|
public void onSignInClick(View v) {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
|||||||
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 org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.WEAK;
|
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_WEAK;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||||
|
|
||||||
public class SetupActivity extends BaseActivity implements OnClickListener,
|
public class SetupActivity extends BaseActivity implements OnClickListener,
|
||||||
@@ -115,13 +115,13 @@ public class SetupActivity extends BaseActivity implements OnClickListener,
|
|||||||
nicknameLength > MAX_AUTHOR_NAME_LENGTH);
|
nicknameLength > MAX_AUTHOR_NAME_LENGTH);
|
||||||
UiUtils.setError(passwordEntryWrapper,
|
UiUtils.setError(passwordEntryWrapper,
|
||||||
getString(R.string.password_too_weak),
|
getString(R.string.password_too_weak),
|
||||||
firstPassword.length() > 0 && strength < WEAK);
|
firstPassword.length() > 0 && strength < QUITE_WEAK);
|
||||||
UiUtils.setError(passwordConfirmationWrapper,
|
UiUtils.setError(passwordConfirmationWrapper,
|
||||||
getString(R.string.passwords_do_not_match),
|
getString(R.string.passwords_do_not_match),
|
||||||
secondPassword.length() > 0 && !passwordsMatch);
|
secondPassword.length() > 0 && !passwordsMatch);
|
||||||
createAccountButton.setEnabled(nicknameLength > 0
|
createAccountButton.setEnabled(nicknameLength > 0
|
||||||
&& nicknameLength <= MAX_AUTHOR_NAME_LENGTH
|
&& nicknameLength <= MAX_AUTHOR_NAME_LENGTH
|
||||||
&& passwordsMatch && strength >= WEAK);
|
&& passwordsMatch && strength >= QUITE_WEAK);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ public class NavDrawerActivity extends BriarActivity implements
|
|||||||
} else if (getSupportFragmentManager().getBackStackEntryCount() == 0 &&
|
} else if (getSupportFragmentManager().getBackStackEntryCount() == 0 &&
|
||||||
getSupportFragmentManager()
|
getSupportFragmentManager()
|
||||||
.findFragmentByTag(ContactListFragment.TAG) == null) {
|
.findFragmentByTag(ContactListFragment.TAG) == null) {
|
||||||
/**
|
/*
|
||||||
* This Makes sure that the first fragment (ContactListFragment) the
|
* This Makes sure that the first fragment (ContactListFragment) the
|
||||||
* user sees is the same as the last fragment the user sees before
|
* user sees is the same as the last fragment the user sees before
|
||||||
* exiting. This models the typical Google navigation behaviour such
|
* exiting. This models the typical Google navigation behaviour such
|
||||||
@@ -212,11 +212,10 @@ public class NavDrawerActivity extends BriarActivity implements
|
|||||||
drawerToggle.onConfigurationChanged(newConfig);
|
drawerToggle.onConfigurationChanged(newConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void signOut() {
|
||||||
protected void signOut() {
|
|
||||||
drawerLayout.setDrawerLockMode(LOCK_MODE_LOCKED_CLOSED);
|
drawerLayout.setDrawerLockMode(LOCK_MODE_LOCKED_CLOSED);
|
||||||
startFragment(new SignOutFragment());
|
startFragment(new SignOutFragment());
|
||||||
super.signOut();
|
signOut(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startFragment(BaseFragment fragment, int itemId){
|
private void startFragment(BaseFragment fragment, int itemId){
|
||||||
|
|||||||
@@ -103,12 +103,14 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
|
|||||||
showPanicApp(packageName);
|
showPanicApp(packageName);
|
||||||
|
|
||||||
if (packageName.equals(Panic.PACKAGE_NAME_NONE)) {
|
if (packageName.equals(Panic.PACKAGE_NAME_NONE)) {
|
||||||
|
lockPref.setEnabled(false);
|
||||||
purgePref.setChecked(false);
|
purgePref.setChecked(false);
|
||||||
purgePref.setEnabled(false);
|
purgePref.setEnabled(false);
|
||||||
uninstallPref.setChecked(false);
|
uninstallPref.setChecked(false);
|
||||||
uninstallPref.setEnabled(false);
|
uninstallPref.setEnabled(false);
|
||||||
getActivity().setResult(Activity.RESULT_CANCELED);
|
getActivity().setResult(Activity.RESULT_CANCELED);
|
||||||
} else {
|
} else {
|
||||||
|
lockPref.setEnabled(true);
|
||||||
purgePref.setEnabled(true);
|
purgePref.setEnabled(true);
|
||||||
uninstallPref.setEnabled(true);
|
uninstallPref.setEnabled(true);
|
||||||
}
|
}
|
||||||
@@ -126,7 +128,12 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
|
|||||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
intent.setData(Uri.parse(
|
intent.setData(Uri.parse(
|
||||||
"market://details?id=info.guardianproject.ripple"));
|
"market://details?id=info.guardianproject.ripple"));
|
||||||
startActivity(intent);
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
if (intent.resolveActivity(
|
||||||
|
getActivity().getPackageManager()) !=
|
||||||
|
null) {
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -185,6 +192,8 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
|
|||||||
panicAppPref.setIcon(
|
panicAppPref.setIcon(
|
||||||
android.R.drawable.ic_menu_close_clear_cancel);
|
android.R.drawable.ic_menu_close_clear_cancel);
|
||||||
|
|
||||||
|
// disable panic actions
|
||||||
|
lockPref.setEnabled(false);
|
||||||
purgePref.setEnabled(false);
|
purgePref.setEnabled(false);
|
||||||
uninstallPref.setEnabled(false);
|
uninstallPref.setEnabled(false);
|
||||||
} else {
|
} else {
|
||||||
@@ -196,6 +205,8 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
|
|||||||
panicAppPref.setIcon(
|
panicAppPref.setIcon(
|
||||||
pm.getApplicationIcon(triggerPackageName));
|
pm.getApplicationIcon(triggerPackageName));
|
||||||
|
|
||||||
|
// enable panic actions
|
||||||
|
lockPref.setEnabled(true);
|
||||||
purgePref.setEnabled(true);
|
purgePref.setEnabled(true);
|
||||||
uninstallPref.setEnabled(true);
|
uninstallPref.setEnabled(true);
|
||||||
} catch (PackageManager.NameNotFoundException e) {
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
@@ -230,7 +241,7 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
|
|||||||
};
|
};
|
||||||
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(getContext(),
|
AlertDialog.Builder builder = new AlertDialog.Builder(getContext(),
|
||||||
R.style.BriarDialogTheme);
|
R.style.BriarDialogTheme);
|
||||||
builder.setTitle(getString(R.string.dialog_title_connect_panic_app));
|
builder.setTitle(getString(R.string.dialog_title_connect_panic_app));
|
||||||
|
|
||||||
CharSequence app = getString(R.string.unknown_app);
|
CharSequence app = getString(R.string.unknown_app);
|
||||||
|
|||||||
@@ -57,10 +57,9 @@ public class PanicResponderActivity extends BriarActivity {
|
|||||||
LOG.info("Received Panic Trigger...");
|
LOG.info("Received Panic Trigger...");
|
||||||
|
|
||||||
if (PanicResponder.receivedTriggerFromConnectedApp(this)) {
|
if (PanicResponder.receivedTriggerFromConnectedApp(this)) {
|
||||||
LOG.info("Panic Trigger came from connected app.");
|
LOG.info("Panic Trigger came from connected app");
|
||||||
LOG.info("Performing destructive responses...");
|
|
||||||
|
|
||||||
// Performing destructive panic responses
|
// Performing panic responses
|
||||||
if (sharedPref.getBoolean(KEY_UNINSTALL, false)) {
|
if (sharedPref.getBoolean(KEY_UNINSTALL, false)) {
|
||||||
LOG.info("Purging all data...");
|
LOG.info("Purging all data...");
|
||||||
deleteAllData();
|
deleteAllData();
|
||||||
@@ -78,19 +77,6 @@ public class PanicResponderActivity extends BriarActivity {
|
|||||||
signOut(true);
|
signOut(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Performing non-destructive default panic response
|
|
||||||
else if (sharedPref.getBoolean(KEY_LOCK, true)) {
|
|
||||||
LOG.info("Signing out...");
|
|
||||||
signOut(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// received intent from non-trusted app
|
|
||||||
else {
|
|
||||||
intent = getIntent();
|
|
||||||
if (intent != null && Panic.isTriggerIntent(intent)) {
|
|
||||||
LOG.info("Signing out...");
|
|
||||||
signOut(true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ import org.briarproject.bramble.api.event.EventBus;
|
|||||||
import org.briarproject.bramble.api.event.EventListener;
|
import org.briarproject.bramble.api.event.EventListener;
|
||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.BluetoothConstants;
|
||||||
|
import org.briarproject.bramble.api.plugin.TorConstants;
|
||||||
import org.briarproject.bramble.api.settings.Settings;
|
import org.briarproject.bramble.api.settings.Settings;
|
||||||
import org.briarproject.bramble.api.settings.SettingsManager;
|
import org.briarproject.bramble.api.settings.SettingsManager;
|
||||||
import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
|
import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
|
||||||
@@ -45,7 +47,18 @@ import static android.media.RingtoneManager.TYPE_NOTIFICATION;
|
|||||||
import static android.provider.Settings.System.DEFAULT_NOTIFICATION_URI;
|
import static android.provider.Settings.System.DEFAULT_NOTIFICATION_URI;
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
|
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PREF_BT_ENABLE;
|
||||||
|
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK;
|
||||||
|
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_ALWAYS;
|
||||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_RINGTONE;
|
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_RINGTONE;
|
||||||
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_BLOG;
|
||||||
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_FORUM;
|
||||||
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_GROUP;
|
||||||
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_PRIVATE;
|
||||||
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_RINGTONE_NAME;
|
||||||
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_RINGTONE_URI;
|
||||||
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_SOUND;
|
||||||
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_VIBRATION;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
@@ -53,8 +66,8 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
implements EventListener, Preference.OnPreferenceChangeListener {
|
implements EventListener, Preference.OnPreferenceChangeListener {
|
||||||
|
|
||||||
public static final String SETTINGS_NAMESPACE = "android-ui";
|
public static final String SETTINGS_NAMESPACE = "android-ui";
|
||||||
public static final String PREF_NOTIFY_GROUP = "notifyGroupMessages";
|
public static final String BT_NAMESPACE = BluetoothConstants.ID.getString();
|
||||||
public static final String PREF_NOTIFY_BLOG = "notifyBlogPosts";
|
public static final String TOR_NAMESPACE = TorConstants.ID.getString();
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(SettingsFragment.class.getName());
|
Logger.getLogger(SettingsFragment.class.getName());
|
||||||
@@ -62,7 +75,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
private SettingsActivity listener;
|
private SettingsActivity listener;
|
||||||
private AndroidExecutor androidExecutor;
|
private AndroidExecutor androidExecutor;
|
||||||
private ListPreference enableBluetooth;
|
private ListPreference enableBluetooth;
|
||||||
private ListPreference torOverMobile;
|
private ListPreference torNetwork;
|
||||||
private CheckBoxPreference notifyPrivateMessages;
|
private CheckBoxPreference notifyPrivateMessages;
|
||||||
private CheckBoxPreference notifyGroupMessages;
|
private CheckBoxPreference notifyGroupMessages;
|
||||||
private CheckBoxPreference notifyForumPosts;
|
private CheckBoxPreference notifyForumPosts;
|
||||||
@@ -74,7 +87,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
private volatile SettingsManager settingsManager;
|
private volatile SettingsManager settingsManager;
|
||||||
private volatile EventBus eventBus;
|
private volatile EventBus eventBus;
|
||||||
private volatile Settings settings;
|
private volatile Settings settings;
|
||||||
private volatile boolean bluetoothSetting = false, torSetting = false;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Context context) {
|
public void onAttach(Context context) {
|
||||||
@@ -90,10 +102,8 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
public void onCreatePreferences(Bundle bundle, String s) {
|
public void onCreatePreferences(Bundle bundle, String s) {
|
||||||
addPreferencesFromResource(R.xml.settings);
|
addPreferencesFromResource(R.xml.settings);
|
||||||
|
|
||||||
enableBluetooth =
|
enableBluetooth = (ListPreference) findPreference("pref_key_bluetooth");
|
||||||
(ListPreference) findPreference("pref_key_bluetooth");
|
torNetwork = (ListPreference) findPreference("pref_key_tor_network");
|
||||||
torOverMobile =
|
|
||||||
(ListPreference) findPreference("pref_key_tor_mobile");
|
|
||||||
notifyPrivateMessages = (CheckBoxPreference) findPreference(
|
notifyPrivateMessages = (CheckBoxPreference) findPreference(
|
||||||
"pref_key_notify_private_messages");
|
"pref_key_notify_private_messages");
|
||||||
notifyGroupMessages = (CheckBoxPreference) findPreference(
|
notifyGroupMessages = (CheckBoxPreference) findPreference(
|
||||||
@@ -107,7 +117,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
notifySound = findPreference("pref_key_notify_sound");
|
notifySound = findPreference("pref_key_notify_sound");
|
||||||
|
|
||||||
enableBluetooth.setOnPreferenceChangeListener(this);
|
enableBluetooth.setOnPreferenceChangeListener(this);
|
||||||
torOverMobile.setOnPreferenceChangeListener(this);
|
torNetwork.setOnPreferenceChangeListener(this);
|
||||||
notifyPrivateMessages.setOnPreferenceChangeListener(this);
|
notifyPrivateMessages.setOnPreferenceChangeListener(this);
|
||||||
notifyGroupMessages.setOnPreferenceChangeListener(this);
|
notifyGroupMessages.setOnPreferenceChangeListener(this);
|
||||||
notifyForumPosts.setOnPreferenceChangeListener(this);
|
notifyForumPosts.setOnPreferenceChangeListener(this);
|
||||||
@@ -126,10 +136,10 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
i.putExtra(EXTRA_RINGTONE_DEFAULT_URI,
|
i.putExtra(EXTRA_RINGTONE_DEFAULT_URI,
|
||||||
DEFAULT_NOTIFICATION_URI);
|
DEFAULT_NOTIFICATION_URI);
|
||||||
i.putExtra(EXTRA_RINGTONE_SHOW_SILENT, true);
|
i.putExtra(EXTRA_RINGTONE_SHOW_SILENT, true);
|
||||||
if (settings.getBoolean("notifySound", true)) {
|
if (settings.getBoolean(PREF_NOTIFY_SOUND, true)) {
|
||||||
Uri uri;
|
Uri uri;
|
||||||
String ringtoneUri =
|
String ringtoneUri =
|
||||||
settings.get("notifyRingtoneUri");
|
settings.get(PREF_NOTIFY_RINGTONE_URI);
|
||||||
if (StringUtils.isNullOrEmpty(ringtoneUri))
|
if (StringUtils.isNullOrEmpty(ringtoneUri))
|
||||||
uri = DEFAULT_NOTIFICATION_URI;
|
uri = DEFAULT_NOTIFICATION_URI;
|
||||||
else uri = Uri.parse(ringtoneUri);
|
else uri = Uri.parse(ringtoneUri);
|
||||||
@@ -181,14 +191,18 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
try {
|
try {
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
settings = settingsManager.getSettings(SETTINGS_NAMESPACE);
|
settings = settingsManager.getSettings(SETTINGS_NAMESPACE);
|
||||||
Settings btSettings = settingsManager.getSettings("bt");
|
Settings btSettings =
|
||||||
Settings torSettings = settingsManager.getSettings("tor");
|
settingsManager.getSettings(BT_NAMESPACE);
|
||||||
|
Settings torSettings =
|
||||||
|
settingsManager.getSettings(TOR_NAMESPACE);
|
||||||
long duration = System.currentTimeMillis() - now;
|
long duration = System.currentTimeMillis() - now;
|
||||||
if (LOG.isLoggable(INFO))
|
if (LOG.isLoggable(INFO))
|
||||||
LOG.info("Loading settings took " + duration + " ms");
|
LOG.info("Loading settings took " + duration + " ms");
|
||||||
bluetoothSetting = btSettings.getBoolean("enable", false);
|
boolean btSetting =
|
||||||
torSetting = torSettings.getBoolean("torOverMobile", true);
|
btSettings.getBoolean(PREF_BT_ENABLE, false);
|
||||||
displaySettings();
|
int torSetting = torSettings.getInt(PREF_TOR_NETWORK,
|
||||||
|
PREF_TOR_NETWORK_ALWAYS);
|
||||||
|
displaySettings(btSetting, torSetting);
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
if (LOG.isLoggable(WARNING))
|
if (LOG.isLoggable(WARNING))
|
||||||
LOG.log(WARNING, e.toString(), e);
|
LOG.log(WARNING, e.toString(), e);
|
||||||
@@ -197,31 +211,33 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void displaySettings() {
|
private void displaySettings(final boolean btSetting,
|
||||||
|
final int torSetting) {
|
||||||
listener.runOnUiThreadUnlessDestroyed(new Runnable() {
|
listener.runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
enableBluetooth.setValue(Boolean.toString(bluetoothSetting));
|
enableBluetooth.setValue(Boolean.toString(btSetting));
|
||||||
torOverMobile.setValue(Boolean.toString(torSetting));
|
torNetwork.setValue(Integer.toString(torSetting));
|
||||||
|
|
||||||
notifyPrivateMessages.setChecked(settings.getBoolean(
|
notifyPrivateMessages.setChecked(settings.getBoolean(
|
||||||
"notifyPrivateMessages", true));
|
PREF_NOTIFY_PRIVATE, true));
|
||||||
|
|
||||||
notifyGroupMessages.setChecked(settings.getBoolean(
|
notifyGroupMessages.setChecked(settings.getBoolean(
|
||||||
PREF_NOTIFY_GROUP, true));
|
PREF_NOTIFY_GROUP, true));
|
||||||
|
|
||||||
notifyForumPosts.setChecked(settings.getBoolean(
|
notifyForumPosts.setChecked(settings.getBoolean(
|
||||||
"notifyForumPosts", true));
|
PREF_NOTIFY_FORUM, true));
|
||||||
|
|
||||||
notifyBlogPosts.setChecked(settings.getBoolean(
|
notifyBlogPosts.setChecked(settings.getBoolean(
|
||||||
PREF_NOTIFY_BLOG, true));
|
PREF_NOTIFY_BLOG, true));
|
||||||
|
|
||||||
notifyVibration.setChecked(settings.getBoolean(
|
notifyVibration.setChecked(settings.getBoolean(
|
||||||
"notifyVibration", true));
|
PREF_NOTIFY_VIBRATION, true));
|
||||||
|
|
||||||
String text;
|
String text;
|
||||||
if (settings.getBoolean("notifySound", true)) {
|
if (settings.getBoolean(PREF_NOTIFY_SOUND, true)) {
|
||||||
String ringtoneName = settings.get("notifyRingtoneName");
|
String ringtoneName =
|
||||||
|
settings.get(PREF_NOTIFY_RINGTONE_NAME);
|
||||||
if (StringUtils.isNullOrEmpty(ringtoneName)) {
|
if (StringUtils.isNullOrEmpty(ringtoneName)) {
|
||||||
text = getString(R.string.notify_sound_setting_default);
|
text = getString(R.string.notify_sound_setting_default);
|
||||||
} else {
|
} else {
|
||||||
@@ -248,15 +264,15 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
@Override
|
@Override
|
||||||
public boolean onPreferenceChange(Preference preference, Object o) {
|
public boolean onPreferenceChange(Preference preference, Object o) {
|
||||||
if (preference == enableBluetooth) {
|
if (preference == enableBluetooth) {
|
||||||
bluetoothSetting = Boolean.valueOf((String) o);
|
boolean btSetting = Boolean.valueOf((String) o);
|
||||||
enableOrDisableBluetooth(bluetoothSetting);
|
enableOrDisableBluetooth(btSetting);
|
||||||
storeBluetoothSettings();
|
storeBluetoothSettings(btSetting);
|
||||||
} else if (preference == torOverMobile) {
|
} else if (preference == torNetwork) {
|
||||||
torSetting = Boolean.valueOf((String) o);
|
int torSetting = Integer.valueOf((String) o);
|
||||||
storeTorSettings();
|
storeTorSettings(torSetting);
|
||||||
} else if (preference == notifyPrivateMessages) {
|
} else if (preference == notifyPrivateMessages) {
|
||||||
Settings s = new Settings();
|
Settings s = new Settings();
|
||||||
s.putBoolean("notifyPrivateMessages", (Boolean) o);
|
s.putBoolean(PREF_NOTIFY_PRIVATE, (Boolean) o);
|
||||||
storeSettings(s);
|
storeSettings(s);
|
||||||
} else if (preference == notifyGroupMessages) {
|
} else if (preference == notifyGroupMessages) {
|
||||||
Settings s = new Settings();
|
Settings s = new Settings();
|
||||||
@@ -264,7 +280,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
storeSettings(s);
|
storeSettings(s);
|
||||||
} else if (preference == notifyForumPosts) {
|
} else if (preference == notifyForumPosts) {
|
||||||
Settings s = new Settings();
|
Settings s = new Settings();
|
||||||
s.putBoolean("notifyForumPosts", (Boolean) o);
|
s.putBoolean(PREF_NOTIFY_FORUM, (Boolean) o);
|
||||||
storeSettings(s);
|
storeSettings(s);
|
||||||
} else if (preference == notifyBlogPosts) {
|
} else if (preference == notifyBlogPosts) {
|
||||||
Settings s = new Settings();
|
Settings s = new Settings();
|
||||||
@@ -272,7 +288,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
storeSettings(s);
|
storeSettings(s);
|
||||||
} else if (preference == notifyVibration) {
|
} else if (preference == notifyVibration) {
|
||||||
Settings s = new Settings();
|
Settings s = new Settings();
|
||||||
s.putBoolean("notifyVibration", (Boolean) o);
|
s.putBoolean(PREF_NOTIFY_VIBRATION, (Boolean) o);
|
||||||
storeSettings(s);
|
storeSettings(s);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -291,15 +307,15 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void storeTorSettings() {
|
private void storeTorSettings(final int torSetting) {
|
||||||
listener.runOnDbThread(new Runnable() {
|
listener.runOnDbThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
Settings s = new Settings();
|
Settings s = new Settings();
|
||||||
s.putBoolean("torOverMobile", torSetting);
|
s.putInt(PREF_TOR_NETWORK, torSetting);
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
settingsManager.mergeSettings(s, "tor");
|
settingsManager.mergeSettings(s, TOR_NAMESPACE);
|
||||||
long duration = System.currentTimeMillis() - now;
|
long duration = System.currentTimeMillis() - now;
|
||||||
if (LOG.isLoggable(INFO))
|
if (LOG.isLoggable(INFO))
|
||||||
LOG.info("Merging settings took " + duration + " ms");
|
LOG.info("Merging settings took " + duration + " ms");
|
||||||
@@ -311,15 +327,15 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void storeBluetoothSettings() {
|
private void storeBluetoothSettings(final boolean btSetting) {
|
||||||
listener.runOnDbThread(new Runnable() {
|
listener.runOnDbThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
Settings s = new Settings();
|
Settings s = new Settings();
|
||||||
s.putBoolean("enable", bluetoothSetting);
|
s.putBoolean(PREF_BT_ENABLE, btSetting);
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
settingsManager.mergeSettings(s, "bt");
|
settingsManager.mergeSettings(s, BT_NAMESPACE);
|
||||||
long duration = System.currentTimeMillis() - now;
|
long duration = System.currentTimeMillis() - now;
|
||||||
if (LOG.isLoggable(INFO))
|
if (LOG.isLoggable(INFO))
|
||||||
LOG.info("Merging settings took " + duration + " ms");
|
LOG.info("Merging settings took " + duration + " ms");
|
||||||
@@ -357,21 +373,21 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
Uri uri = data.getParcelableExtra(EXTRA_RINGTONE_PICKED_URI);
|
Uri uri = data.getParcelableExtra(EXTRA_RINGTONE_PICKED_URI);
|
||||||
if (uri == null) {
|
if (uri == null) {
|
||||||
// The user chose silence
|
// The user chose silence
|
||||||
s.putBoolean("notifySound", false);
|
s.putBoolean(PREF_NOTIFY_SOUND, false);
|
||||||
s.put("notifyRingtoneName", "");
|
s.put(PREF_NOTIFY_RINGTONE_NAME, "");
|
||||||
s.put("notifyRingtoneUri", "");
|
s.put(PREF_NOTIFY_RINGTONE_URI, "");
|
||||||
} else if (RingtoneManager.isDefault(uri)) {
|
} else if (RingtoneManager.isDefault(uri)) {
|
||||||
// The user chose the default
|
// The user chose the default
|
||||||
s.putBoolean("notifySound", true);
|
s.putBoolean(PREF_NOTIFY_SOUND, true);
|
||||||
s.put("notifyRingtoneName", "");
|
s.put(PREF_NOTIFY_RINGTONE_NAME, "");
|
||||||
s.put("notifyRingtoneUri", "");
|
s.put(PREF_NOTIFY_RINGTONE_URI, "");
|
||||||
} else {
|
} else {
|
||||||
// The user chose a ringtone other than the default
|
// The user chose a ringtone other than the default
|
||||||
Ringtone r = RingtoneManager.getRingtone(getContext(), uri);
|
Ringtone r = RingtoneManager.getRingtone(getContext(), uri);
|
||||||
String name = r.getTitle(getContext());
|
String name = r.getTitle(getContext());
|
||||||
s.putBoolean("notifySound", true);
|
s.putBoolean(PREF_NOTIFY_SOUND, true);
|
||||||
s.put("notifyRingtoneName", name);
|
s.put(PREF_NOTIFY_RINGTONE_NAME, name);
|
||||||
s.put("notifyRingtoneUri", uri.toString());
|
s.put(PREF_NOTIFY_RINGTONE_URI, uri.toString());
|
||||||
}
|
}
|
||||||
storeSettings(s);
|
storeSettings(s);
|
||||||
}
|
}
|
||||||
@@ -381,7 +397,8 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
public void eventOccurred(Event e) {
|
public void eventOccurred(Event e) {
|
||||||
if (e instanceof SettingsUpdatedEvent) {
|
if (e instanceof SettingsUpdatedEvent) {
|
||||||
String namespace = ((SettingsUpdatedEvent) e).getNamespace();
|
String namespace = ((SettingsUpdatedEvent) e).getNamespace();
|
||||||
if (namespace.equals("bt") || namespace.equals("tor")
|
if (namespace.equals(BT_NAMESPACE)
|
||||||
|
|| namespace.equals(TOR_NAMESPACE)
|
||||||
|| namespace.equals(SETTINGS_NAMESPACE)) {
|
|| namespace.equals(SETTINGS_NAMESPACE)) {
|
||||||
LOG.info("Settings updated");
|
LOG.info("Settings updated");
|
||||||
loadSettings();
|
loadSettings();
|
||||||
|
|||||||
@@ -30,8 +30,8 @@ public class SplashScreenActivity extends BaseActivity {
|
|||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(SplashScreenActivity.class.getName());
|
Logger.getLogger(SplashScreenActivity.class.getName());
|
||||||
|
|
||||||
// This build expires on 1 March 2017
|
// This build expires on 1 May 2017
|
||||||
private static final long EXPIRY_DATE = 1488322800 * 1000L;
|
private static final long EXPIRY_DATE = 1493593200 * 1000L;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
protected ConfigController configController;
|
protected ConfigController configController;
|
||||||
@@ -83,6 +83,10 @@ public class SplashScreenActivity extends BaseActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void showNewScreenFilterWarning() {
|
||||||
|
}
|
||||||
|
|
||||||
private void enableStrictMode() {
|
private void enableStrictMode() {
|
||||||
if (TESTING) {
|
if (TESTING) {
|
||||||
ThreadPolicy.Builder threadPolicy = new ThreadPolicy.Builder();
|
ThreadPolicy.Builder threadPolicy = new ThreadPolicy.Builder();
|
||||||
|
|||||||
@@ -9,6 +9,16 @@ import org.briarproject.bramble.api.sync.GroupId;
|
|||||||
*/
|
*/
|
||||||
public interface AndroidNotificationManager {
|
public interface AndroidNotificationManager {
|
||||||
|
|
||||||
|
String PREF_NOTIFY_PRIVATE = "notifyPrivateMessages";
|
||||||
|
String PREF_NOTIFY_GROUP = "notifyGroupMessages";
|
||||||
|
String PREF_NOTIFY_FORUM = "notifyForumPosts";
|
||||||
|
String PREF_NOTIFY_BLOG = "notifyBlogPosts";
|
||||||
|
|
||||||
|
String PREF_NOTIFY_SOUND = "notifySound";
|
||||||
|
String PREF_NOTIFY_RINGTONE_NAME = "notifyRingtoneName";
|
||||||
|
String PREF_NOTIFY_RINGTONE_URI = "notifyRingtoneUri";
|
||||||
|
String PREF_NOTIFY_VIBRATION = "notifyVibration";
|
||||||
|
|
||||||
void clearContactNotification(ContactId c);
|
void clearContactNotification(ContactId c);
|
||||||
|
|
||||||
void clearAllContactNotifications();
|
void clearAllContactNotifications();
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package org.briarproject.briar.api.android;
|
||||||
|
|
||||||
|
import android.support.annotation.UiThread;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public interface ScreenFilterMonitor {
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
Set<String> getApps();
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
void storeAppsAsShown(Collection<String> s, boolean persistent);
|
||||||
|
}
|
||||||
@@ -8,15 +8,17 @@ import android.graphics.Paint;
|
|||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.UiThread;
|
||||||
import android.text.Spannable;
|
import android.text.Spannable;
|
||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.activity.BaseActivity;
|
import org.briarproject.briar.android.BriarApplication;
|
||||||
import org.thoughtcrime.securesms.components.util.FutureTaskListener;
|
import org.thoughtcrime.securesms.components.util.FutureTaskListener;
|
||||||
import org.thoughtcrime.securesms.components.util.ListenableFutureTask;
|
import org.thoughtcrime.securesms.components.util.ListenableFutureTask;
|
||||||
import org.thoughtcrime.securesms.util.BitmapDecodingException;
|
import org.thoughtcrime.securesms.util.BitmapDecodingException;
|
||||||
@@ -33,17 +35,21 @@ import java.util.regex.Pattern;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static android.graphics.Paint.ANTI_ALIAS_FLAG;
|
||||||
|
import static android.graphics.Paint.FILTER_BITMAP_FLAG;
|
||||||
import static android.graphics.PixelFormat.TRANSLUCENT;
|
import static android.graphics.PixelFormat.TRANSLUCENT;
|
||||||
import static android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
|
import static android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
|
|
||||||
|
@MethodsNotNullByDefault
|
||||||
|
@ParametersNotNullByDefault
|
||||||
public class EmojiProvider {
|
public class EmojiProvider {
|
||||||
|
|
||||||
private static volatile EmojiProvider INSTANCE = null;
|
private static volatile EmojiProvider INSTANCE = null;
|
||||||
|
|
||||||
private static final Paint PAINT =
|
private static final Paint PAINT =
|
||||||
new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
|
new Paint(FILTER_BITMAP_FLAG | ANTI_ALIAS_FLAG);
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
AndroidExecutor androidExecutor;
|
AndroidExecutor androidExecutor;
|
||||||
@@ -64,7 +70,7 @@ public class EmojiProvider {
|
|||||||
private static final int EMOJI_PER_ROW = 32;
|
private static final int EMOJI_PER_ROW = 32;
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final float decodeScale, verticalPad;
|
private final float decodeScale;
|
||||||
private final List<EmojiPageModel> staticPages;
|
private final List<EmojiPageModel> staticPages;
|
||||||
|
|
||||||
static EmojiProvider getInstance(Context context) {
|
static EmojiProvider getInstance(Context context) {
|
||||||
@@ -73,8 +79,9 @@ public class EmojiProvider {
|
|||||||
if (INSTANCE == null) {
|
if (INSTANCE == null) {
|
||||||
LOG.info("Creating new instance of EmojiProvider");
|
LOG.info("Creating new instance of EmojiProvider");
|
||||||
INSTANCE = new EmojiProvider(context);
|
INSTANCE = new EmojiProvider(context);
|
||||||
((BaseActivity) context).getActivityComponent()
|
BriarApplication app =
|
||||||
.inject(INSTANCE);
|
(BriarApplication) context.getApplicationContext();
|
||||||
|
app.getApplicationComponent().inject(INSTANCE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,11 +93,10 @@ public class EmojiProvider {
|
|||||||
float drawerSize =
|
float drawerSize =
|
||||||
context.getResources().getDimension(R.dimen.emoji_drawer_size);
|
context.getResources().getDimension(R.dimen.emoji_drawer_size);
|
||||||
decodeScale = Math.min(1f, drawerSize / EMOJI_RAW_HEIGHT);
|
decodeScale = Math.min(1f, drawerSize / EMOJI_RAW_HEIGHT);
|
||||||
verticalPad = EMOJI_VERT_PAD * this.decodeScale;
|
|
||||||
staticPages = EmojiPages.getPages(context);
|
staticPages = EmojiPages.getPages(context);
|
||||||
for (EmojiPageModel page : staticPages) {
|
for (EmojiPageModel page : staticPages) {
|
||||||
if (page.hasSpriteMap()) {
|
if (page.hasSpriteMap()) {
|
||||||
final EmojiPageBitmap pageBitmap = new EmojiPageBitmap(page);
|
EmojiPageBitmap pageBitmap = new EmojiPageBitmap(page);
|
||||||
for (int i = 0; i < page.getEmoji().length; i++) {
|
for (int i = 0; i < page.getEmoji().length; i++) {
|
||||||
offsets.put(Character.codePointAt(page.getEmoji()[i], 0),
|
offsets.put(Character.codePointAt(page.getEmoji()[i], 0),
|
||||||
new DrawInfo(pageBitmap, i));
|
new DrawInfo(pageBitmap, i));
|
||||||
@@ -100,8 +106,8 @@ public class EmojiProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
Spannable emojify(@Nullable CharSequence text,
|
@UiThread
|
||||||
@NonNull TextView tv) {
|
Spannable emojify(@Nullable CharSequence text, TextView tv) {
|
||||||
if (text == null) return null;
|
if (text == null) return null;
|
||||||
Matcher matches = EMOJI_RANGE.matcher(text);
|
Matcher matches = EMOJI_RANGE.matcher(text);
|
||||||
SpannableStringBuilder builder = new SpannableStringBuilder(text);
|
SpannableStringBuilder builder = new SpannableStringBuilder(text);
|
||||||
@@ -118,12 +124,13 @@ public class EmojiProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@UiThread
|
||||||
Drawable getEmojiDrawable(int emojiCode) {
|
Drawable getEmojiDrawable(int emojiCode) {
|
||||||
return getEmojiDrawable(offsets.get(emojiCode));
|
return getEmojiDrawable(offsets.get(emojiCode));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private Drawable getEmojiDrawable(DrawInfo drawInfo) {
|
private Drawable getEmojiDrawable(@Nullable DrawInfo drawInfo) {
|
||||||
if (drawInfo == null) {
|
if (drawInfo == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -154,10 +161,10 @@ public class EmojiProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class EmojiDrawable extends Drawable {
|
static class EmojiDrawable extends Drawable {
|
||||||
|
|
||||||
private final DrawInfo info;
|
private final DrawInfo info;
|
||||||
private final float intrinsicWidth, intrinsicHeight;
|
private final float intrinsicWidth, intrinsicHeight, verticalPad;
|
||||||
|
|
||||||
private Bitmap bmp;
|
private Bitmap bmp;
|
||||||
|
|
||||||
@@ -165,6 +172,7 @@ public class EmojiProvider {
|
|||||||
this.info = info;
|
this.info = info;
|
||||||
intrinsicWidth = EMOJI_RAW_WIDTH * decodeScale;
|
intrinsicWidth = EMOJI_RAW_WIDTH * decodeScale;
|
||||||
intrinsicHeight = EMOJI_RAW_HEIGHT * decodeScale;
|
intrinsicHeight = EMOJI_RAW_HEIGHT * decodeScale;
|
||||||
|
verticalPad = EMOJI_VERT_PAD * decodeScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -178,7 +186,7 @@ public class EmojiProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(@NonNull Canvas canvas) {
|
public void draw(Canvas canvas) {
|
||||||
if (bmp == null) {
|
if (bmp == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -212,7 +220,7 @@ public class EmojiProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setColorFilter(ColorFilter cf) {
|
public void setColorFilter(@Nullable ColorFilter cf) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,49 +253,46 @@ public class EmojiProvider {
|
|||||||
this.model = model;
|
this.model = model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
private ListenableFutureTask<Bitmap> get() {
|
private ListenableFutureTask<Bitmap> get() {
|
||||||
if (bitmapReference != null && bitmapReference.get() != null) {
|
if (bitmapReference != null) {
|
||||||
return new ListenableFutureTask<>(bitmapReference.get());
|
Bitmap bitmap = bitmapReference.get();
|
||||||
} else if (task != null) {
|
if (bitmap != null) return new ListenableFutureTask<>(bitmap);
|
||||||
return task;
|
|
||||||
} else {
|
|
||||||
Callable<Bitmap> callable = new Callable<Bitmap>() {
|
|
||||||
@Override
|
|
||||||
@Nullable
|
|
||||||
public Bitmap call() throws Exception {
|
|
||||||
try {
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Loading page " + model.getSprite());
|
|
||||||
return loadPage();
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
LOG.log(WARNING, ioe.toString(), ioe);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
task = new ListenableFutureTask<>(callable);
|
|
||||||
new AsyncTask<Void, Void, Void>() {
|
|
||||||
@Override
|
|
||||||
protected Void doInBackground(Void... params) {
|
|
||||||
task.run();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(Void aVoid) {
|
|
||||||
task = null;
|
|
||||||
}
|
|
||||||
}.execute();
|
|
||||||
}
|
}
|
||||||
|
if (task != null) return task;
|
||||||
|
Callable<Bitmap> callable = new Callable<Bitmap>() {
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public Bitmap call() throws Exception {
|
||||||
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info("Loading page " + model.getSprite());
|
||||||
|
return loadPage();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
task = new ListenableFutureTask<>(callable);
|
||||||
|
new AsyncTask<Void, Void, Void>() {
|
||||||
|
@Override
|
||||||
|
protected Void doInBackground(Void... params) {
|
||||||
|
task.run();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Void aVoid) {
|
||||||
|
task = null;
|
||||||
|
}
|
||||||
|
}.execute();
|
||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Bitmap loadPage() throws IOException {
|
private Bitmap loadPage() throws IOException {
|
||||||
if (bitmapReference != null && bitmapReference.get() != null)
|
if (bitmapReference != null) {
|
||||||
return bitmapReference.get();
|
Bitmap bitmap = bitmapReference.get();
|
||||||
|
if (bitmap != null) return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final Bitmap bitmap = BitmapUtil.createScaledBitmap(context,
|
Bitmap bitmap = BitmapUtil.createScaledBitmap(context,
|
||||||
"file:///android_asset/" + model.getSprite(),
|
"file:///android_asset/" + model.getSprite(),
|
||||||
decodeScale);
|
decodeScale);
|
||||||
bitmapReference = new SoftReference<>(bitmap);
|
bitmapReference = new SoftReference<>(bitmap);
|
||||||
|
|||||||
@@ -2,19 +2,21 @@ package org.thoughtcrime.securesms.components.emoji;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.annotation.DrawableRes;
|
import android.support.annotation.DrawableRes;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.annotation.UiThread;
|
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.settings.Settings;
|
import org.briarproject.bramble.api.settings.Settings;
|
||||||
import org.briarproject.bramble.api.settings.SettingsManager;
|
import org.briarproject.bramble.api.settings.SettingsManager;
|
||||||
|
import org.briarproject.bramble.util.StringUtils;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.activity.BaseActivity;
|
import org.briarproject.briar.android.BriarApplication;
|
||||||
import org.briarproject.briar.android.controller.DbController;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@@ -23,7 +25,8 @@ import javax.inject.Inject;
|
|||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
|
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
|
||||||
|
|
||||||
@UiThread
|
@MethodsNotNullByDefault
|
||||||
|
@ParametersNotNullByDefault
|
||||||
public class RecentEmojiPageModel implements EmojiPageModel {
|
public class RecentEmojiPageModel implements EmojiPageModel {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
@@ -32,27 +35,27 @@ public class RecentEmojiPageModel implements EmojiPageModel {
|
|||||||
private static final String EMOJI_LRU_PREFERENCE = "pref_emoji_recent";
|
private static final String EMOJI_LRU_PREFERENCE = "pref_emoji_recent";
|
||||||
private static final int EMOJI_LRU_SIZE = 50;
|
private static final int EMOJI_LRU_SIZE = 50;
|
||||||
|
|
||||||
private final LinkedHashSet<String> recentlyUsed;
|
private final LinkedHashSet<String> recentlyUsed; // UI thread
|
||||||
private Settings settings;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
SettingsManager settingsManager;
|
SettingsManager settingsManager;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
DbController dbController;
|
@DatabaseExecutor
|
||||||
|
Executor dbExecutor;
|
||||||
|
|
||||||
RecentEmojiPageModel(Context context) {
|
RecentEmojiPageModel(Context context) {
|
||||||
if (!(context instanceof BaseActivity)) {
|
BriarApplication app =
|
||||||
throw new IllegalArgumentException(
|
(BriarApplication) context.getApplicationContext();
|
||||||
"Needs to be created from BaseActivity");
|
app.getApplicationComponent().inject(this);
|
||||||
}
|
|
||||||
((BaseActivity) context).getActivityComponent().inject(this);
|
|
||||||
recentlyUsed = getPersistedCache();
|
recentlyUsed = getPersistedCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
private LinkedHashSet<String> getPersistedCache() {
|
private LinkedHashSet<String> getPersistedCache() {
|
||||||
String serialized;
|
String serialized;
|
||||||
try {
|
try {
|
||||||
settings = settingsManager.getSettings(SETTINGS_NAMESPACE);
|
// FIXME: Don't make DB calls on the UI thread
|
||||||
|
Settings settings = settingsManager.getSettings(SETTINGS_NAMESPACE);
|
||||||
serialized = settings.get(EMOJI_LRU_PREFERENCE);
|
serialized = settings.get(EMOJI_LRU_PREFERENCE);
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
@@ -67,7 +70,6 @@ public class RecentEmojiPageModel implements EmojiPageModel {
|
|||||||
return R.drawable.ic_emoji_recent;
|
return R.drawable.ic_emoji_recent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getEmoji() {
|
public String[] getEmoji() {
|
||||||
return toReversePrimitiveArray(recentlyUsed);
|
return toReversePrimitiveArray(recentlyUsed);
|
||||||
@@ -92,31 +94,26 @@ public class RecentEmojiPageModel implements EmojiPageModel {
|
|||||||
iterator.next();
|
iterator.next();
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
}
|
}
|
||||||
save(recentlyUsed);
|
save(serialize(recentlyUsed));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String serialize(LinkedHashSet<String> emojis) {
|
private String serialize(LinkedHashSet<String> emojis) {
|
||||||
String result = "";
|
return StringUtils.join(emojis, ";");
|
||||||
for (String emoji : emojis) {
|
|
||||||
result += emoji + ";";
|
|
||||||
}
|
|
||||||
if (!emojis.isEmpty())
|
|
||||||
result = result.substring(0, result.length() - 1);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private LinkedHashSet<String> deserialize(@Nullable String str) {
|
private LinkedHashSet<String> deserialize(@Nullable String serialized) {
|
||||||
String[] list = str == null ? new String[] {} : str.split(";");
|
if (serialized == null) return new LinkedHashSet<>();
|
||||||
|
String[] list = serialized.split(";");
|
||||||
LinkedHashSet<String> result = new LinkedHashSet<>(list.length);
|
LinkedHashSet<String> result = new LinkedHashSet<>(list.length);
|
||||||
Collections.addAll(result, list);
|
Collections.addAll(result, list);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void save(final LinkedHashSet<String> recentlyUsed) {
|
private void save(final String serialized) {
|
||||||
dbController.runOnDbThread(new Runnable() {
|
dbExecutor.execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
String serialized = serialize(recentlyUsed);
|
Settings settings = new Settings();
|
||||||
settings.put(EMOJI_LRU_PREFERENCE, serialized);
|
settings.put(EMOJI_LRU_PREFERENCE, serialized);
|
||||||
try {
|
try {
|
||||||
settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE);
|
settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE);
|
||||||
@@ -128,8 +125,7 @@ public class RecentEmojiPageModel implements EmojiPageModel {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private String[] toReversePrimitiveArray(
|
private String[] toReversePrimitiveArray(LinkedHashSet<String> emojiSet) {
|
||||||
@NonNull LinkedHashSet<String> emojiSet) {
|
|
||||||
String[] emojis = new String[emojiSet.size()];
|
String[] emojis = new String[emojiSet.size()];
|
||||||
int i = emojiSet.size() - 1;
|
int i = emojiSet.size() - 1;
|
||||||
for (String emoji : emojiSet) {
|
for (String emoji : emojiSet) {
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.9 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.8 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.6 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.6 KiB |
40
briar-android/src/main/res/layout/alert_dialog_checkbox.xml
Normal file
40
briar-android/src/main/res/layout/alert_dialog_checkbox.xml
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:filterTouchesWhenObscured="false"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:fadeScrollbars="false"
|
||||||
|
android:paddingLeft="20dp"
|
||||||
|
android:paddingRight="20dp"
|
||||||
|
android:paddingStart="20dp"
|
||||||
|
android:paddingTop="20dp"
|
||||||
|
android:theme="@style/BriarTheme">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/alert_dialog_text"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingLeft="3dp"
|
||||||
|
android:paddingRight="6dp"
|
||||||
|
android:text="TextView"
|
||||||
|
android:textAppearance="@style/BriarTextBody"
|
||||||
|
android:theme="@+theme/BriarDialogTheme"/>
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/checkBox_screen_filter_reminder"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="15dp"
|
||||||
|
android:layout_marginStart="15dp"
|
||||||
|
android:layout_weight="0"
|
||||||
|
android:filterTouchesWhenObscured="false"
|
||||||
|
android:text="@string/checkbox_dont_show_again"
|
||||||
|
android:textAppearance="@style/BriarTextBody"/>
|
||||||
|
</LinearLayout>
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
android:layout_height="@dimen/blogs_avatar_normal_size"
|
android:layout_height="@dimen/blogs_avatar_normal_size"
|
||||||
android:layout_alignTop="@+id/authorName"
|
android:layout_alignTop="@+id/authorName"
|
||||||
android:layout_marginRight="@dimen/margin_medium"
|
android:layout_marginRight="@dimen/margin_medium"
|
||||||
tools:src="@drawable/ic_launcher"/>
|
tools:src="@mipmap/ic_launcher_round"/>
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/avatarIcon"
|
android:id="@+id/avatarIcon"
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
android:layout_width="30dp"
|
android:layout_width="30dp"
|
||||||
android:layout_height="30dp"
|
android:layout_height="30dp"
|
||||||
app:civ_border_color="@color/action_bar_text"
|
app:civ_border_color="@color/action_bar_text"
|
||||||
tools:src="@drawable/ic_launcher"/>
|
tools:src="@mipmap/ic_launcher_round"/>
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/contactStatus"
|
android:id="@+id/contactStatus"
|
||||||
|
|||||||
@@ -38,7 +38,7 @@
|
|||||||
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
|
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
|
||||||
android:layout_marginRight="@dimen/listitem_horizontal_margin"
|
android:layout_marginRight="@dimen/listitem_horizontal_margin"
|
||||||
android:layout_marginStart="@dimen/listitem_horizontal_margin"
|
android:layout_marginStart="@dimen/listitem_horizontal_margin"
|
||||||
tools:src="@drawable/ic_launcher"/>
|
tools:src="@mipmap/ic_launcher_round"/>
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||||
android:id="@+id/nameContact1"
|
android:id="@+id/nameContact1"
|
||||||
@@ -77,7 +77,7 @@
|
|||||||
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
|
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
|
||||||
android:layout_marginRight="@dimen/listitem_horizontal_margin"
|
android:layout_marginRight="@dimen/listitem_horizontal_margin"
|
||||||
android:layout_marginStart="@dimen/listitem_horizontal_margin"
|
android:layout_marginStart="@dimen/listitem_horizontal_margin"
|
||||||
tools:src="@drawable/ic_launcher"/>
|
tools:src="@mipmap/ic_launcher_round"/>
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||||
android:id="@+id/nameContact2"
|
android:id="@+id/nameContact2"
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
android:layout_width="@dimen/listitem_picture_size"
|
android:layout_width="@dimen/listitem_picture_size"
|
||||||
android:layout_height="@dimen/listitem_picture_size"
|
android:layout_height="@dimen/listitem_picture_size"
|
||||||
android:layout_gravity="bottom|left"
|
android:layout_gravity="bottom|left"
|
||||||
tools:src="@drawable/ic_launcher"/>
|
tools:src="@mipmap/ic_launcher_round"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/unreadCountView"
|
android:id="@+id/unreadCountView"
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
|
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
|
||||||
android:layout_marginStart="@dimen/listitem_horizontal_margin"
|
android:layout_marginStart="@dimen/listitem_horizontal_margin"
|
||||||
tools:src="@drawable/ic_launcher"/>
|
tools:src="@mipmap/ic_launcher_round"/>
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||||
android:id="@+id/nameView"
|
android:id="@+id/nameView"
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
|
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
|
||||||
android:layout_marginRight="@dimen/listitem_horizontal_margin"
|
android:layout_marginRight="@dimen/listitem_horizontal_margin"
|
||||||
tools:src="@drawable/ic_launcher"/>
|
tools:src="@mipmap/ic_launcher_round"/>
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||||
android:id="@+id/nameView"
|
android:id="@+id/nameView"
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
android:layout_alignParentLeft="true"
|
android:layout_alignParentLeft="true"
|
||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_centerVertical="true"
|
android:layout_centerVertical="true"
|
||||||
tools:src="@drawable/ic_launcher"/>
|
tools:src="@mipmap/ic_launcher_round"/>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|||||||
@@ -61,6 +61,7 @@
|
|||||||
<string name="hide">Ausblenden</string>
|
<string name="hide">Ausblenden</string>
|
||||||
<string name="ok">OK</string>
|
<string name="ok">OK</string>
|
||||||
<string name="cancel">Abbrechen</string>
|
<string name="cancel">Abbrechen</string>
|
||||||
|
<string name="got_it">Okay, verstanden.</string>
|
||||||
<string name="delete">Löschen</string>
|
<string name="delete">Löschen</string>
|
||||||
<string name="accept">Annehmen</string>
|
<string name="accept">Annehmen</string>
|
||||||
<string name="decline">Ablehnen</string>
|
<string name="decline">Ablehnen</string>
|
||||||
@@ -68,9 +69,12 @@
|
|||||||
<string name="online">Online</string>
|
<string name="online">Online</string>
|
||||||
<string name="offline">Offline</string>
|
<string name="offline">Offline</string>
|
||||||
<string name="send">Senden</string>
|
<string name="send">Senden</string>
|
||||||
|
<string name="allow">Erlauben</string>
|
||||||
|
<string name="open">Öffnen</string>
|
||||||
<string name="no_data">Keine Daten</string>
|
<string name="no_data">Keine Daten</string>
|
||||||
<string name="ellipsis">…</string>
|
<string name="ellipsis">…</string>
|
||||||
<string name="text_too_long">Der eingegebene Text ist leider zu lang</string>
|
<string name="text_too_long">Der eingegebene Text ist leider zu lang</string>
|
||||||
|
<string name="show_onboarding">Hilfe anzeigen</string>
|
||||||
<!--Contacts and Private Conversations-->
|
<!--Contacts and Private Conversations-->
|
||||||
<string name="no_contacts">Du scheinst hier neu zu sein und noch keine Kontakte zu haben.\n\nVerwende das \"+\"-Icon am oberen Bildschirmrand an und folge den Anweisungen um Freunde zu deiner Kontaktliste hinzuzufügen.\n\nDenke daran dass Du neue Kontakte nur dann hinzufügen kannst, wenn Du ihnen physisch begegnest. Das hindert andere daran, sich als Deine Person auszugeben oder Deine Nachrichten zu lesen.</string>
|
<string name="no_contacts">Du scheinst hier neu zu sein und noch keine Kontakte zu haben.\n\nVerwende das \"+\"-Icon am oberen Bildschirmrand an und folge den Anweisungen um Freunde zu deiner Kontaktliste hinzuzufügen.\n\nDenke daran dass Du neue Kontakte nur dann hinzufügen kannst, wenn Du ihnen physisch begegnest. Das hindert andere daran, sich als Deine Person auszugeben oder Deine Nachrichten zu lesen.</string>
|
||||||
<string name="date_no_private_messages">Keine Nachrichten.</string>
|
<string name="date_no_private_messages">Keine Nachrichten.</string>
|
||||||
@@ -83,6 +87,7 @@
|
|||||||
<!--Adding Contacts-->
|
<!--Adding Contacts-->
|
||||||
<string name="add_contact_title">Kontakt hinzufügen</string>
|
<string name="add_contact_title">Kontakt hinzufügen</string>
|
||||||
<string name="your_nickname">Wähle die zu verwendende Identität:</string>
|
<string name="your_nickname">Wähle die zu verwendende Identität:</string>
|
||||||
|
<string name="face_to_face">Um einen neuen Kontakt hinzufügen zu können, ist eine reale Begegnung erforderlich.\n\nDadurch wird eine betrügerische Impersonation und der unautorisierte Zugriff auf die Kommunikation verhindert.</string>
|
||||||
<string name="continue_button">Weiter</string>
|
<string name="continue_button">Weiter</string>
|
||||||
<string name="your_invitation_code">Dein Einladungs-Code ist</string>
|
<string name="your_invitation_code">Dein Einladungs-Code ist</string>
|
||||||
<string name="enter_invitation_code">Bitte gib den Einladungs-Code Deines Kontakts an:</string>
|
<string name="enter_invitation_code">Bitte gib den Einladungs-Code Deines Kontakts an:</string>
|
||||||
@@ -95,6 +100,7 @@
|
|||||||
<string name="your_confirmation_code">Dein Bestätigungscode ist</string>
|
<string name="your_confirmation_code">Dein Bestätigungscode ist</string>
|
||||||
<string name="enter_confirmation_code">Bitte gebe den Bestätigungscode Deines Kontakts ein:</string>
|
<string name="enter_confirmation_code">Bitte gebe den Bestätigungscode Deines Kontakts ein:</string>
|
||||||
<string name="waiting_for_contact">Warte auf Kontakt\u2026</string>
|
<string name="waiting_for_contact">Warte auf Kontakt\u2026</string>
|
||||||
|
<string name="waiting_for_contact_to_scan">Warte auf Scan und Verbindung mit dem Kontakt\u2026</string>
|
||||||
<string name="exchanging_contact_details">Kontaktdetails werden ausgetauscht\u2026</string>
|
<string name="exchanging_contact_details">Kontaktdetails werden ausgetauscht\u2026</string>
|
||||||
<string name="codes_do_not_match">Codes stimmen nicht überein</string>
|
<string name="codes_do_not_match">Codes stimmen nicht überein</string>
|
||||||
<string name="interfering">Möglicherweise stört jemand Deine Verbindung</string>
|
<string name="interfering">Möglicherweise stört jemand Deine Verbindung</string>
|
||||||
@@ -144,21 +150,38 @@
|
|||||||
<string name="groups_create_group_button">Gruppe erstellen</string>
|
<string name="groups_create_group_button">Gruppe erstellen</string>
|
||||||
<string name="groups_create_group_invitation_button">Einladung schicken</string>
|
<string name="groups_create_group_invitation_button">Einladung schicken</string>
|
||||||
<string name="groups_create_group_hint">Gebe Deiner privaten Gruppe einen Namen</string>
|
<string name="groups_create_group_hint">Gebe Deiner privaten Gruppe einen Namen</string>
|
||||||
|
<string name="groups_invitation_sent">Gruppeneinladung versendet</string>
|
||||||
<string name="groups_compose_message">Nachricht erstellen</string>
|
<string name="groups_compose_message">Nachricht erstellen</string>
|
||||||
<string name="groups_message_sent">Nachricht gesendet</string>
|
<string name="groups_message_sent">Nachricht gesendet</string>
|
||||||
<string name="groups_member_list">Mitglieder</string>
|
<string name="groups_member_list">Mitglieder</string>
|
||||||
<string name="groups_invite_members">Mitglieder einladen</string>
|
<string name="groups_invite_members">Mitglieder einladen</string>
|
||||||
|
<string name="groups_member_created_you">Du hast diese Gruppe erstellt</string>
|
||||||
|
<string name="groups_member_created">%s hat diese Gruppe erstellt</string>
|
||||||
<string name="groups_member_joined_you">Du bist der Gruppe beigetreten</string>
|
<string name="groups_member_joined_you">Du bist der Gruppe beigetreten</string>
|
||||||
<string name="groups_member_joined">%s ist der Gruppe beigetreten</string>
|
<string name="groups_member_joined">%s ist der Gruppe beigetreten</string>
|
||||||
<string name="groups_leave">Gruppe verlassen</string>
|
<string name="groups_leave">Gruppe verlassen</string>
|
||||||
|
<string name="groups_leave_dialog_title">Verlassen der Gruppe bestätigen</string>
|
||||||
<string name="groups_leave_dialog_message">Bist Du sicher, dass Du die Gruppe verlassen willst?</string>
|
<string name="groups_leave_dialog_message">Bist Du sicher, dass Du die Gruppe verlassen willst?</string>
|
||||||
<string name="groups_dissolve">Gruppe auflösen</string>
|
<string name="groups_dissolve">Gruppe auflösen</string>
|
||||||
|
<string name="groups_dissolve_dialog_title">Auflösung der Gruppe bestätigen</string>
|
||||||
|
<string name="groups_dissolve_dialog_message">Willst Du wirklich diese Gruppe auflösen?\n\nAlle anderen Teilnehmer verlieren die Möglichkeit der Kommunikation ohne möglicherweise die neuesten Nachrichten erhalten zu haben.</string>
|
||||||
|
<string name="groups_dissolve_button">Auflösen</string>
|
||||||
|
<string name="groups_dissolved_dialog_title">Gruppe wurde aufgelöst</string>
|
||||||
|
<string name="groups_dissolved_dialog_message">Der Ersteller dieser Gruppe hat dieselbe aufgelöst.\n\nEs können keine weiteren Nachrichten mehr in dieser Gruppe geschrieben werden und möglicherweise wurden noch nicht alle Nachrichten empfangen.</string>
|
||||||
<!--Private Group Invitations-->
|
<!--Private Group Invitations-->
|
||||||
<string name="groups_invitations_title">Gruppeneinladungen</string>
|
<string name="groups_invitations_title">Gruppeneinladungen</string>
|
||||||
|
<string name="groups_invitations_invitation_sent">%1$s wurde in die Gruppe \"%2$s\" eingeladen.</string>
|
||||||
<string name="groups_invitations_invitation_received">%1$s hat dich eingeladen der Gruppe \"%2$s\" beizutreten.</string>
|
<string name="groups_invitations_invitation_received">%1$s hat dich eingeladen der Gruppe \"%2$s\" beizutreten.</string>
|
||||||
<string name="groups_invitations_joined">Gruppe beigetreten</string>
|
<string name="groups_invitations_joined">Gruppe beigetreten</string>
|
||||||
<string name="groups_invitations_declined">Gruppeneinladung abgelehnt</string>
|
<string name="groups_invitations_declined">Gruppeneinladung abgelehnt</string>
|
||||||
|
<string name="groups_invitations_response_accepted_sent">Gruppeneinladung von %s angenommen.</string>
|
||||||
|
<string name="groups_invitations_response_declined_sent">Gruppeneinladung von %s abgelehnt.</string>
|
||||||
|
<string name="groups_invitations_response_accepted_received">%s hat die Einladung zur Gruppe angenommen.</string>
|
||||||
|
<string name="groups_invitations_response_declined_received">%s hat die Einladung zur Gruppe abgelehnt.</string>
|
||||||
<!--Private Groups Revealing Contacts-->
|
<!--Private Groups Revealing Contacts-->
|
||||||
|
<string name="groups_reveal_contacts">Kontakte teilen</string>
|
||||||
|
<string name="groups_reveal_dialog_message">Kontakte können mit allen derzeitigen und zukünftigen Mitgliedern dieser Gruppe geteilt werden.\n\nDas beschleunigt die Verbindung zu der Gruppe und macht sie zusätzlich zuverlässiger, da Kommunikation mit den Mitgliedern auch dann erfolgen kann, wenn der Ersteller der Gruppe offline ist.</string>
|
||||||
|
<string name="groups_reveal_visible">Verbindung zum Kontakt ist für die Gruppe sichtbar</string>
|
||||||
<!--Forums-->
|
<!--Forums-->
|
||||||
<string name="no_forums">Du hast noch keine Foren.\n\nWarum erstellst du nicht einfach selbst ein neues Forum, indem du auf das \"+\"-Icon am oberen Bildschirmrand tippst?\n\nDu kannst auch deine Kontakte auffordern, Foren mit dir zu teilen.</string>
|
<string name="no_forums">Du hast noch keine Foren.\n\nWarum erstellst du nicht einfach selbst ein neues Forum, indem du auf das \"+\"-Icon am oberen Bildschirmrand tippst?\n\nDu kannst auch deine Kontakte auffordern, Foren mit dir zu teilen.</string>
|
||||||
<string name="create_forum_title">Neues Forum</string>
|
<string name="create_forum_title">Neues Forum</string>
|
||||||
@@ -259,9 +282,6 @@
|
|||||||
<string name="bluetooth_setting">Über Bluetooth verbinden</string>
|
<string name="bluetooth_setting">Über Bluetooth verbinden</string>
|
||||||
<string name="bluetooth_setting_enabled">Sobald Kontakte in der Nähe sind</string>
|
<string name="bluetooth_setting_enabled">Sobald Kontakte in der Nähe sind</string>
|
||||||
<string name="bluetooth_setting_disabled">Nur beim Hinzufügen von Kontakten</string>
|
<string name="bluetooth_setting_disabled">Nur beim Hinzufügen von Kontakten</string>
|
||||||
<string name="tor_mobile_setting">Über Tor verbinden</string>
|
|
||||||
<string name="tor_mobile_setting_enabled">Wenn WLAN oder Mobildaten verwendet werden</string>
|
|
||||||
<string name="tor_mobile_setting_disabled">Nur wenn über WLAN verbunden</string>
|
|
||||||
<!--Settings Security and Panic-->
|
<!--Settings Security and Panic-->
|
||||||
<string name="security_settings_title">Sicherheit</string>
|
<string name="security_settings_title">Sicherheit</string>
|
||||||
<string name="change_password">Passwort ändern</string>
|
<string name="change_password">Passwort ändern</string>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user