mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 18:59:06 +01:00
Compare commits
101 Commits
beta-2017-
...
831_refact
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8bb55d2580 | ||
|
|
6de539a62d | ||
|
|
34704ec04d | ||
|
|
9fd6d46583 | ||
|
|
920f3581fa | ||
|
|
45e7af31fe | ||
|
|
67d5d8cdf1 | ||
|
|
9d8cadb7a9 | ||
|
|
6425c49d04 | ||
|
|
68d98b50f2 | ||
|
|
84986d393f | ||
|
|
115d488bc3 | ||
|
|
2eeb2213e3 | ||
|
|
1b48d661e8 | ||
|
|
49ba66dee9 | ||
|
|
46920f3bce | ||
|
|
4b955809f7 | ||
|
|
57d4d6546a | ||
|
|
9bfb58a764 | ||
|
|
0256ec0b8c | ||
|
|
b0b4a85d15 | ||
|
|
d40a058ef5 | ||
|
|
58b9efb24c | ||
|
|
17de785c12 | ||
|
|
c7ff1ba974 | ||
|
|
d17669f131 | ||
|
|
9755cd9ab4 | ||
|
|
6d2b18facc | ||
|
|
f8cf7034db | ||
|
|
a1e65c9fa7 | ||
|
|
499d2fe677 | ||
|
|
fe963edd9d | ||
|
|
96f006068f | ||
|
|
74f1fa5690 | ||
|
|
85c17b4cb0 | ||
|
|
6b3a1fd6d4 | ||
|
|
bcabcfce8c | ||
|
|
db0a3bf380 | ||
|
|
d5d9436e28 | ||
|
|
0827b067ec | ||
|
|
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>
|
||||||
@@ -19,8 +19,7 @@ import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
|||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.Backoff;
|
import org.briarproject.bramble.api.plugin.Backoff;
|
||||||
import org.briarproject.bramble.api.plugin.PluginException;
|
import org.briarproject.bramble.api.plugin.PluginException;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.duplex.AbstractBluetoothPlugin;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||||
@@ -67,6 +66,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;
|
||||||
@@ -74,7 +74,8 @@ import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
|
|||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
class DroidtoothPlugin implements DuplexPlugin {
|
class DroidtoothPlugin<C, S>
|
||||||
|
extends AbstractBluetoothPlugin<C, S>{
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(DroidtoothPlugin.class.getName());
|
Logger.getLogger(DroidtoothPlugin.class.getName());
|
||||||
@@ -83,16 +84,10 @@ class DroidtoothPlugin implements DuplexPlugin {
|
|||||||
private static final String DISCOVERY_FINISHED =
|
private static final String DISCOVERY_FINISHED =
|
||||||
"android.bluetooth.adapter.action.DISCOVERY_FINISHED";
|
"android.bluetooth.adapter.action.DISCOVERY_FINISHED";
|
||||||
|
|
||||||
private final Executor ioExecutor;
|
|
||||||
private final AndroidExecutor androidExecutor;
|
private final AndroidExecutor androidExecutor;
|
||||||
private final Context appContext;
|
private final Context appContext;
|
||||||
private final SecureRandom secureRandom;
|
|
||||||
private final Backoff backoff;
|
|
||||||
private final DuplexPluginCallback callback;
|
|
||||||
private final int maxLatency;
|
|
||||||
private final AtomicBoolean used = new AtomicBoolean(false);
|
private final AtomicBoolean used = new AtomicBoolean(false);
|
||||||
|
|
||||||
private volatile boolean running = false;
|
|
||||||
private volatile boolean wasEnabledByUs = false;
|
private volatile boolean wasEnabledByUs = false;
|
||||||
private volatile BluetoothStateReceiver receiver = null;
|
private volatile BluetoothStateReceiver receiver = null;
|
||||||
private volatile BluetoothServerSocket socket = null;
|
private volatile BluetoothServerSocket socket = null;
|
||||||
@@ -103,29 +98,15 @@ class DroidtoothPlugin implements DuplexPlugin {
|
|||||||
DroidtoothPlugin(Executor ioExecutor, AndroidExecutor androidExecutor,
|
DroidtoothPlugin(Executor ioExecutor, AndroidExecutor androidExecutor,
|
||||||
Context appContext, SecureRandom secureRandom, Backoff backoff,
|
Context appContext, SecureRandom secureRandom, Backoff backoff,
|
||||||
DuplexPluginCallback callback, int maxLatency) {
|
DuplexPluginCallback callback, int maxLatency) {
|
||||||
this.ioExecutor = ioExecutor;
|
|
||||||
|
super(ioExecutor, secureRandom, backoff, maxLatency, callback);
|
||||||
this.androidExecutor = androidExecutor;
|
this.androidExecutor = androidExecutor;
|
||||||
this.appContext = appContext;
|
this.appContext = appContext;
|
||||||
this.secureRandom = secureRandom;
|
|
||||||
this.backoff = backoff;
|
|
||||||
this.callback = callback;
|
|
||||||
this.maxLatency = maxLatency;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TransportId getId() {
|
protected void close(S ss) throws IOException {
|
||||||
return ID;
|
((BluetoothServerSocket)ss).close();
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMaxLatency() {
|
|
||||||
return maxLatency;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMaxIdleTime() {
|
|
||||||
// Bluetooth detects dead connections so we don't need keepalives
|
|
||||||
return Integer.MAX_VALUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -164,7 +145,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");
|
||||||
@@ -193,14 +174,14 @@ class DroidtoothPlugin implements DuplexPlugin {
|
|||||||
BluetoothServerSocket ss;
|
BluetoothServerSocket ss;
|
||||||
try {
|
try {
|
||||||
ss = adapter.listenUsingInsecureRfcommWithServiceRecord(
|
ss = adapter.listenUsingInsecureRfcommWithServiceRecord(
|
||||||
"RFCOMM", getUuid());
|
"RFCOMM", UUID.fromString(getUuid()));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
if (LOG.isLoggable(WARNING))
|
if (LOG.isLoggable(WARNING))
|
||||||
LOG.log(WARNING, e.toString(), e);
|
LOG.log(WARNING, e.toString(), e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!isRunning()) {
|
if (!isRunning()) {
|
||||||
tryToClose(ss);
|
tryToClose((S)ss);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LOG.info("Socket bound");
|
LOG.info("Socket bound");
|
||||||
@@ -212,29 +193,6 @@ class DroidtoothPlugin implements DuplexPlugin {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private UUID getUuid() {
|
|
||||||
String uuid = callback.getLocalProperties().get(PROP_UUID);
|
|
||||||
if (uuid == null) {
|
|
||||||
byte[] random = new byte[UUID_BYTES];
|
|
||||||
secureRandom.nextBytes(random);
|
|
||||||
uuid = UUID.nameUUIDFromBytes(random).toString();
|
|
||||||
TransportProperties p = new TransportProperties();
|
|
||||||
p.put(PROP_UUID, uuid);
|
|
||||||
callback.mergeLocalProperties(p);
|
|
||||||
}
|
|
||||||
return UUID.fromString(uuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void tryToClose(@Nullable BluetoothServerSocket ss) {
|
|
||||||
try {
|
|
||||||
if (ss != null) ss.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
} finally {
|
|
||||||
callback.transportDisabled();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void acceptContactConnections() {
|
private void acceptContactConnections() {
|
||||||
while (isRunning()) {
|
while (isRunning()) {
|
||||||
BluetoothSocket s;
|
BluetoothSocket s;
|
||||||
@@ -260,9 +218,9 @@ class DroidtoothPlugin implements DuplexPlugin {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop() {
|
public void stop() {
|
||||||
running = false;
|
this.running = false;
|
||||||
if (receiver != null) appContext.unregisterReceiver(receiver);
|
if (receiver != null) appContext.unregisterReceiver(receiver);
|
||||||
tryToClose(socket);
|
tryToClose((S)socket);
|
||||||
// Disable Bluetooth if we enabled it and it's still enabled
|
// Disable Bluetooth if we enabled it and it's still enabled
|
||||||
if (wasEnabledByUs && adapter.isEnabled()) {
|
if (wasEnabledByUs && adapter.isEnabled()) {
|
||||||
if (adapter.disable()) LOG.info("Disabling Bluetooth");
|
if (adapter.disable()) LOG.info("Disabling Bluetooth");
|
||||||
@@ -275,42 +233,20 @@ class DroidtoothPlugin implements DuplexPlugin {
|
|||||||
return running && adapter != null && adapter.isEnabled();
|
return running && adapter != null && adapter.isEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
protected Runnable returnPollRunnable(final String address, final String uuid,
|
||||||
public boolean shouldPoll() {
|
final ContactId c) {
|
||||||
return true;
|
return new Runnable() {
|
||||||
}
|
@Override
|
||||||
|
public void run() {
|
||||||
@Override
|
if (!running) return;
|
||||||
public int getPollingInterval() {
|
BluetoothSocket s = connect(address, uuid);
|
||||||
return backoff.getPollingInterval();
|
if (s != null) {
|
||||||
}
|
backoff.reset();
|
||||||
|
callback.outgoingConnectionCreated(c, wrapSocket(s));
|
||||||
@Override
|
|
||||||
public void poll(Collection<ContactId> connected) {
|
|
||||||
if (!isRunning()) return;
|
|
||||||
backoff.increment();
|
|
||||||
// Try to connect to known devices in parallel
|
|
||||||
Map<ContactId, TransportProperties> remote =
|
|
||||||
callback.getRemoteProperties();
|
|
||||||
for (Entry<ContactId, TransportProperties> e : remote.entrySet()) {
|
|
||||||
final ContactId c = e.getKey();
|
|
||||||
if (connected.contains(c)) continue;
|
|
||||||
final String address = e.getValue().get(PROP_ADDRESS);
|
|
||||||
if (StringUtils.isNullOrEmpty(address)) continue;
|
|
||||||
final String uuid = e.getValue().get(PROP_UUID);
|
|
||||||
if (StringUtils.isNullOrEmpty(uuid)) continue;
|
|
||||||
ioExecutor.execute(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (!running) return;
|
|
||||||
BluetoothSocket s = connect(address, uuid);
|
|
||||||
if (s != null) {
|
|
||||||
backoff.reset();
|
|
||||||
callback.outgoingConnectionCreated(c, wrapSocket(s));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -346,37 +282,17 @@ class DroidtoothPlugin implements DuplexPlugin {
|
|||||||
LOG.info("Failed to connect to " + scrubMacAddress(address)
|
LOG.info("Failed to connect to " + scrubMacAddress(address)
|
||||||
+ ": " + e);
|
+ ": " + e);
|
||||||
}
|
}
|
||||||
tryToClose(s);
|
tryToClose((S)s);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tryToClose(@Nullable Closeable c) {
|
|
||||||
try {
|
|
||||||
if (c != null) c.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public DuplexTransportConnection connectToAddress(String address, String uuid) {
|
||||||
public DuplexTransportConnection createConnection(ContactId c) {
|
|
||||||
if (!isRunning()) return null;
|
|
||||||
TransportProperties p = callback.getRemoteProperties().get(c);
|
|
||||||
if (p == null) return null;
|
|
||||||
String address = p.get(PROP_ADDRESS);
|
|
||||||
if (StringUtils.isNullOrEmpty(address)) return null;
|
|
||||||
String uuid = p.get(PROP_UUID);
|
|
||||||
if (StringUtils.isNullOrEmpty(uuid)) return null;
|
|
||||||
BluetoothSocket s = connect(address, uuid);
|
BluetoothSocket s = connect(address, uuid);
|
||||||
if (s == null) return null;
|
return s == null ? null : wrapSocket(s);
|
||||||
return new DroidtoothTransportConnection(this, s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supportsInvitations() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DuplexTransportConnection createInvitationConnection(PseudoRandom r,
|
public DuplexTransportConnection createInvitationConnection(PseudoRandom r,
|
||||||
@@ -425,7 +341,7 @@ class DroidtoothPlugin implements DuplexPlugin {
|
|||||||
return null;
|
return null;
|
||||||
} finally {
|
} finally {
|
||||||
// Closing the socket will terminate the listener task
|
// Closing the socket will terminate the listener task
|
||||||
tryToClose(ss);
|
tryToClose((S)ss);
|
||||||
closeSockets(futures, chosen);
|
closeSockets(futures, chosen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -457,11 +373,6 @@ class DroidtoothPlugin implements DuplexPlugin {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supportsKeyAgreement() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public KeyAgreementListener createKeyAgreementListener(byte[] commitment) {
|
public KeyAgreementListener createKeyAgreementListener(byte[] commitment) {
|
||||||
if (!isRunning()) return null;
|
if (!isRunning()) return null;
|
||||||
@@ -486,31 +397,6 @@ class DroidtoothPlugin implements DuplexPlugin {
|
|||||||
return new BluetoothKeyAgreementListener(descriptor, ss);
|
return new BluetoothKeyAgreementListener(descriptor, ss);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public DuplexTransportConnection createKeyAgreementConnection(
|
|
||||||
byte[] commitment, BdfList descriptor, long timeout) {
|
|
||||||
if (!isRunning()) return null;
|
|
||||||
String address;
|
|
||||||
try {
|
|
||||||
address = parseAddress(descriptor);
|
|
||||||
} catch (FormatException e) {
|
|
||||||
LOG.info("Invalid address in key agreement descriptor");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// No truncation necessary because COMMIT_LENGTH = 16
|
|
||||||
UUID uuid = UUID.nameUUIDFromBytes(commitment);
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Connecting to key agreement UUID " + uuid);
|
|
||||||
BluetoothSocket s = connect(address, uuid.toString());
|
|
||||||
if (s == null) return null;
|
|
||||||
return new DroidtoothTransportConnection(this, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String parseAddress(BdfList descriptor) throws FormatException {
|
|
||||||
byte[] mac = descriptor.getRaw(1);
|
|
||||||
if (mac.length != 6) throw new FormatException();
|
|
||||||
return StringUtils.macToString(mac);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class BluetoothStateReceiver extends BroadcastReceiver {
|
private class BluetoothStateReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
@@ -522,7 +408,7 @@ class DroidtoothPlugin implements DuplexPlugin {
|
|||||||
bind();
|
bind();
|
||||||
} else if (state == STATE_OFF) {
|
} else if (state == STATE_OFF) {
|
||||||
LOG.info("Bluetooth disabled");
|
LOG.info("Bluetooth disabled");
|
||||||
tryToClose(socket);
|
tryToClose((S)socket);
|
||||||
}
|
}
|
||||||
int scanMode = intent.getIntExtra(EXTRA_SCAN_MODE, 0);
|
int scanMode = intent.getIntExtra(EXTRA_SCAN_MODE, 0);
|
||||||
if (scanMode == SCAN_MODE_NONE) {
|
if (scanMode == SCAN_MODE_NONE) {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ import javax.annotation.concurrent.Immutable;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class Author {
|
public class Author {
|
||||||
|
|
||||||
public enum Status {ANONYMOUS, UNKNOWN, UNVERIFIED, VERIFIED, OURSELVES}
|
public enum Status {
|
||||||
|
NONE, ANONYMOUS, UNKNOWN, UNVERIFIED, VERIFIED, OURSELVES
|
||||||
|
}
|
||||||
|
|
||||||
private final AuthorId id;
|
private final AuthorId id;
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|||||||
@@ -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,12 @@ 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
|
||||||
|
int EXTRA_SOCKET_TIMEOUT = 30000; // 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,190 @@
|
|||||||
|
package org.briarproject.bramble.api.plugin.duplex;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.FormatException;
|
||||||
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.data.BdfList;
|
||||||
|
import org.briarproject.bramble.api.plugin.Backoff;
|
||||||
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
||||||
|
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||||
|
import org.briarproject.bramble.util.StringUtils;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.INFO;
|
||||||
|
import static java.util.logging.Level.WARNING;
|
||||||
|
import static org.briarproject.bramble.api.plugin.BluetoothConstants.ID;
|
||||||
|
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.UUID_BYTES;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Santiago Torres-Arias on 1/10/17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public abstract class AbstractBluetoothPlugin<C, S> implements DuplexPlugin {
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
Logger.getLogger("Halp");
|
||||||
|
|
||||||
|
protected final Executor ioExecutor;
|
||||||
|
protected final SecureRandom secureRandom;
|
||||||
|
protected final Backoff backoff;
|
||||||
|
protected final int maxLatency;
|
||||||
|
protected final DuplexPluginCallback callback;
|
||||||
|
protected final S ss = null;
|
||||||
|
|
||||||
|
protected volatile boolean running = false;
|
||||||
|
|
||||||
|
protected Runnable pollRunnable = null;
|
||||||
|
|
||||||
|
public AbstractBluetoothPlugin(Executor ioExecutor,
|
||||||
|
SecureRandom secureRandom,
|
||||||
|
Backoff backoff, int maxLatency,
|
||||||
|
DuplexPluginCallback callback) {
|
||||||
|
this.ioExecutor = ioExecutor;
|
||||||
|
this.secureRandom = secureRandom;
|
||||||
|
this.backoff = backoff;
|
||||||
|
this.maxLatency = maxLatency;
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsInvitations() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsKeyAgreement() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRunning() {
|
||||||
|
return running;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldPoll() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPollingInterval() {
|
||||||
|
return backoff.getPollingInterval();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getUuid() {
|
||||||
|
String uuid = callback.getLocalProperties().get(PROP_UUID);
|
||||||
|
if (uuid == null) {
|
||||||
|
byte[] random = new byte[UUID_BYTES];
|
||||||
|
secureRandom.nextBytes(random);
|
||||||
|
uuid = UUID.nameUUIDFromBytes(random).toString();
|
||||||
|
TransportProperties p = new TransportProperties();
|
||||||
|
p.put(PROP_UUID, uuid);
|
||||||
|
callback.mergeLocalProperties(p);
|
||||||
|
}
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TransportId getId() {
|
||||||
|
return ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxLatency() {
|
||||||
|
return maxLatency;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxIdleTime() {
|
||||||
|
// Bluetooth detects dead connections so we don't need keepalives
|
||||||
|
return Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String parseAddress(BdfList descriptor) throws FormatException {
|
||||||
|
byte[] mac = descriptor.getRaw(1);
|
||||||
|
if (mac.length != 6) throw new FormatException();
|
||||||
|
return StringUtils.macToString(mac);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void tryToClose(@Nullable S ss) {
|
||||||
|
try {
|
||||||
|
if (ss != null) close(ss);
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
|
} finally {
|
||||||
|
callback.transportDisabled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void close(S ss) throws IOException;
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
running = false;
|
||||||
|
tryToClose(ss);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void poll(final Collection<ContactId> connected) {
|
||||||
|
if (!running) return;
|
||||||
|
backoff.increment();
|
||||||
|
// Try to connect to known devices in parallel
|
||||||
|
Map<ContactId, TransportProperties> remote =
|
||||||
|
callback.getRemoteProperties();
|
||||||
|
for (Map.Entry<ContactId, TransportProperties> e : remote.entrySet()) {
|
||||||
|
final ContactId c = e.getKey();
|
||||||
|
if (connected.contains(c)) continue;
|
||||||
|
final String address = e.getValue().get(PROP_ADDRESS);
|
||||||
|
if (StringUtils.isNullOrEmpty(address)) continue;
|
||||||
|
final String uuid = e.getValue().get(PROP_UUID);
|
||||||
|
if (StringUtils.isNullOrEmpty(uuid)) continue;
|
||||||
|
ioExecutor.execute(returnPollRunnable(address,uuid, c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract Runnable returnPollRunnable(String address, String uuid,
|
||||||
|
ContactId c);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DuplexTransportConnection createConnection(ContactId c) {
|
||||||
|
if (!isRunning()) return null;
|
||||||
|
TransportProperties p = callback.getRemoteProperties().get(c);
|
||||||
|
if (p == null) return null;
|
||||||
|
String address = p.get(PROP_ADDRESS);
|
||||||
|
if (StringUtils.isNullOrEmpty(address)) return null;
|
||||||
|
String uuid = p.get(PROP_UUID);
|
||||||
|
if (StringUtils.isNullOrEmpty(uuid)) return null;
|
||||||
|
return connectToAddress(address, uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract DuplexTransportConnection connectToAddress(String address, String uuid);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DuplexTransportConnection createKeyAgreementConnection(
|
||||||
|
byte[] commitment, BdfList descriptor, long timeout) {
|
||||||
|
if (!isRunning()) return null;
|
||||||
|
String address;
|
||||||
|
try {
|
||||||
|
address = parseAddress(descriptor);
|
||||||
|
} catch (FormatException e) {
|
||||||
|
LOG.info("Invalid address in key agreement descriptor");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// No truncation necessary because COMMIT_LENGTH = 16
|
||||||
|
String uuid = UUID.nameUUIDFromBytes(commitment).toString();
|
||||||
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info("Connecting to key agreement UUID " + uuid);
|
||||||
|
return connectToAddress(address, uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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) {
|
||||||
@@ -661,7 +668,9 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
acked.add(m);
|
acked.add(m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
transaction.attach(new MessagesAckedEvent(c, acked));
|
if (acked.size() > 0) {
|
||||||
|
transaction.attach(new MessagesAckedEvent(c, acked));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -68,8 +68,8 @@ import static org.briarproject.bramble.db.ExponentialBackoff.calculateExpiry;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
abstract class JdbcDatabase implements Database<Connection> {
|
abstract class JdbcDatabase implements Database<Connection> {
|
||||||
|
|
||||||
private static final int SCHEMA_VERSION = 29;
|
private static final int SCHEMA_VERSION = 30;
|
||||||
private static final int MIN_SCHEMA_VERSION = 29;
|
private static final int MIN_SCHEMA_VERSION = 30;
|
||||||
|
|
||||||
private static final String CREATE_SETTINGS =
|
private static final String CREATE_SETTINGS =
|
||||||
"CREATE TABLE settings"
|
"CREATE TABLE settings"
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import dagger.Module;
|
|||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.plugin.TorConstants.CONNECT_TO_PROXY_TIMEOUT;
|
import static org.briarproject.bramble.api.plugin.TorConstants.CONNECT_TO_PROXY_TIMEOUT;
|
||||||
|
import static org.briarproject.bramble.api.plugin.TorConstants.EXTRA_SOCKET_TIMEOUT;
|
||||||
import static org.briarproject.bramble.api.plugin.TorConstants.SOCKS_PORT;
|
import static org.briarproject.bramble.api.plugin.TorConstants.SOCKS_PORT;
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
@@ -17,6 +18,7 @@ public class SocksModule {
|
|||||||
SocketFactory provideTorSocketFactory() {
|
SocketFactory provideTorSocketFactory() {
|
||||||
InetSocketAddress proxy = new InetSocketAddress("127.0.0.1",
|
InetSocketAddress proxy = new InetSocketAddress("127.0.0.1",
|
||||||
SOCKS_PORT);
|
SOCKS_PORT);
|
||||||
return new SocksSocketFactory(proxy, CONNECT_TO_PROXY_TIMEOUT);
|
return new SocksSocketFactory(proxy, CONNECT_TO_PROXY_TIMEOUT,
|
||||||
|
EXTRA_SOCKET_TIMEOUT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,18 +6,36 @@ 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 final SocketAddress proxy;
|
private static final String[] ERRORS = {
|
||||||
private final int connectToProxyTimeout;
|
"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"
|
||||||
|
};
|
||||||
|
|
||||||
SocksSocket(SocketAddress proxy, int connectToProxyTimeout) {
|
private static final byte[] UNSPECIFIED_ADDRESS = new byte[4];
|
||||||
|
|
||||||
|
private final SocketAddress proxy;
|
||||||
|
private final int connectToProxyTimeout, extraSocketTimeout;
|
||||||
|
|
||||||
|
SocksSocket(SocketAddress proxy, int connectToProxyTimeout,
|
||||||
|
int extraSocketTimeout) {
|
||||||
this.proxy = proxy;
|
this.proxy = proxy;
|
||||||
this.connectToProxyTimeout = connectToProxyTimeout;
|
this.connectToProxyTimeout = connectToProxyTimeout;
|
||||||
|
this.extraSocketTimeout = extraSocketTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -28,6 +46,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();
|
||||||
@@ -41,16 +64,16 @@ class SocksSocket extends Socket {
|
|||||||
sendMethodRequest(out);
|
sendMethodRequest(out);
|
||||||
receiveMethodResponse(in);
|
receiveMethodResponse(in);
|
||||||
|
|
||||||
// Use the supplied timeout temporarily
|
// Use the supplied timeout temporarily, plus any configured extra
|
||||||
int oldTimeout = getSoTimeout();
|
int oldTimeout = getSoTimeout();
|
||||||
setSoTimeout(timeout);
|
setSoTimeout(timeout + extraSocketTimeout);
|
||||||
|
|
||||||
// Connect to the endpoint via the proxy
|
// Connect to the endpoint via the proxy
|
||||||
sendConnectRequest(out, host, port);
|
sendConnectRequest(out, host, port);
|
||||||
receiveConnectResponse(in);
|
receiveConnectResponse(in);
|
||||||
|
|
||||||
// Restore the old timeout
|
// Restore the old timeout, plus any configured extra
|
||||||
setSoTimeout(oldTimeout);
|
setSoTimeout(oldTimeout + extraSocketTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendMethodRequest(OutputStream out) throws IOException {
|
private void sendMethodRequest(OutputStream out) throws IOException {
|
||||||
@@ -93,13 +116,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);
|
||||||
|
|||||||
@@ -11,16 +11,18 @@ import javax.net.SocketFactory;
|
|||||||
class SocksSocketFactory extends SocketFactory {
|
class SocksSocketFactory extends SocketFactory {
|
||||||
|
|
||||||
private final SocketAddress proxy;
|
private final SocketAddress proxy;
|
||||||
private final int connectToProxyTimeout;
|
private final int connectToProxyTimeout, extraSocketTimeout;
|
||||||
|
|
||||||
SocksSocketFactory(SocketAddress proxy, int connectToProxyTimeout) {
|
SocksSocketFactory(SocketAddress proxy, int connectToProxyTimeout,
|
||||||
|
int extraSocketTimeout) {
|
||||||
this.proxy = proxy;
|
this.proxy = proxy;
|
||||||
this.connectToProxyTimeout = connectToProxyTimeout;
|
this.connectToProxyTimeout = connectToProxyTimeout;
|
||||||
|
this.extraSocketTimeout = extraSocketTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Socket createSocket() {
|
public Socket createSocket() {
|
||||||
return new SocksSocket(proxy, connectToProxyTimeout);
|
return new SocksSocket(proxy, connectToProxyTimeout, extraSocketTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -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,16 @@
|
|||||||
|
package org.briarproject.bramble.test;
|
||||||
|
|
||||||
|
import javax.net.SocketFactory;
|
||||||
|
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.Provides;
|
||||||
|
|
||||||
|
@Module
|
||||||
|
public class TestSocksModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
SocketFactory provideSocketFactory() {
|
||||||
|
return SocketFactory.getDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -10,14 +10,14 @@ import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
|||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.Backoff;
|
import org.briarproject.bramble.api.plugin.Backoff;
|
||||||
import org.briarproject.bramble.api.plugin.PluginException;
|
import org.briarproject.bramble.api.plugin.PluginException;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.duplex.AbstractBluetoothPlugin;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||||
import org.briarproject.bramble.util.OsUtils;
|
import org.briarproject.bramble.util.OsUtils;
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
import org.briarproject.bramble.util.StringUtils;
|
||||||
|
|
||||||
|
import java.io.IOError;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
@@ -57,46 +57,22 @@ import static org.briarproject.bramble.api.plugin.BluetoothConstants.UUID_BYTES;
|
|||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
class BluetoothPlugin implements DuplexPlugin {
|
class BluetoothPlugin<C, S> extends AbstractBluetoothPlugin<C, S> {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(BluetoothPlugin.class.getName());
|
Logger.getLogger(BluetoothPlugin.class.getName());
|
||||||
|
|
||||||
private final Executor ioExecutor;
|
|
||||||
private final SecureRandom secureRandom;
|
|
||||||
private final Backoff backoff;
|
|
||||||
private final DuplexPluginCallback callback;
|
|
||||||
private final int maxLatency;
|
|
||||||
private final Semaphore discoverySemaphore = new Semaphore(1);
|
private final Semaphore discoverySemaphore = new Semaphore(1);
|
||||||
private final AtomicBoolean used = new AtomicBoolean(false);
|
private final AtomicBoolean used = new AtomicBoolean(false);
|
||||||
|
|
||||||
private volatile boolean running = false;
|
|
||||||
private volatile StreamConnectionNotifier socket = null;
|
private volatile StreamConnectionNotifier socket = null;
|
||||||
private volatile LocalDevice localDevice = null;
|
private volatile LocalDevice localDevice = null;
|
||||||
|
|
||||||
BluetoothPlugin(Executor ioExecutor, SecureRandom secureRandom,
|
BluetoothPlugin(Executor ioExecutor,
|
||||||
Backoff backoff, DuplexPluginCallback callback, int maxLatency) {
|
SecureRandom secureRandom,
|
||||||
this.ioExecutor = ioExecutor;
|
Backoff backoff, int maxLatency,
|
||||||
this.secureRandom = secureRandom;
|
DuplexPluginCallback callback) {
|
||||||
this.backoff = backoff;
|
super(ioExecutor, secureRandom, backoff, maxLatency, callback);
|
||||||
this.callback = callback;
|
|
||||||
this.maxLatency = maxLatency;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TransportId getId() {
|
|
||||||
return ID;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMaxLatency() {
|
|
||||||
return maxLatency;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMaxIdleTime() {
|
|
||||||
// Bluetooth detects dead connections so we don't need keepalives
|
|
||||||
return Integer.MAX_VALUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -139,7 +115,7 @@ class BluetoothPlugin implements DuplexPlugin {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!running) {
|
if (!running) {
|
||||||
tryToClose(ss);
|
tryToClose((S)ss);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
socket = ss;
|
socket = ss;
|
||||||
@@ -153,29 +129,16 @@ class BluetoothPlugin implements DuplexPlugin {
|
|||||||
private String makeUrl(String address, String uuid) {
|
private String makeUrl(String address, String uuid) {
|
||||||
return "btspp://" + address + ":" + uuid + ";name=RFCOMM";
|
return "btspp://" + address + ":" + uuid + ";name=RFCOMM";
|
||||||
}
|
}
|
||||||
|
//
|
||||||
private String getUuid() {
|
// private void tryToClose(@Nullable StreamConnectionNotifier ss) {
|
||||||
String uuid = callback.getLocalProperties().get(PROP_UUID);
|
// try {
|
||||||
if (uuid == null) {
|
// if (ss != null) ss.close();
|
||||||
byte[] random = new byte[UUID_BYTES];
|
// } catch (IOException e) {
|
||||||
secureRandom.nextBytes(random);
|
// if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
uuid = UUID.nameUUIDFromBytes(random).toString();
|
// } finally {
|
||||||
TransportProperties p = new TransportProperties();
|
// callback.transportDisabled();
|
||||||
p.put(PROP_UUID, uuid);
|
// }
|
||||||
callback.mergeLocalProperties(p);
|
// }
|
||||||
}
|
|
||||||
return uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void tryToClose(@Nullable StreamConnectionNotifier ss) {
|
|
||||||
try {
|
|
||||||
if (ss != null) ss.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
} finally {
|
|
||||||
callback.transportDisabled();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void acceptContactConnections(StreamConnectionNotifier ss) {
|
private void acceptContactConnections(StreamConnectionNotifier ss) {
|
||||||
while (true) {
|
while (true) {
|
||||||
@@ -196,54 +159,33 @@ class BluetoothPlugin implements DuplexPlugin {
|
|||||||
private DuplexTransportConnection wrapSocket(StreamConnection s) {
|
private DuplexTransportConnection wrapSocket(StreamConnection s) {
|
||||||
return new BluetoothTransportConnection(this, s);
|
return new BluetoothTransportConnection(this, s);
|
||||||
}
|
}
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public void stop() {
|
||||||
|
// running = false;
|
||||||
|
// tryToClose(socket);
|
||||||
|
// }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop() {
|
protected void close(S ss) throws IOException {
|
||||||
running = false;
|
((StreamConnection)ss).close();
|
||||||
tryToClose(socket);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isRunning() {
|
public Runnable returnPollRunnable(final String address, final String uuid,
|
||||||
return running;
|
final ContactId c) {
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
return new Runnable() {
|
||||||
public boolean shouldPoll() {
|
@Override
|
||||||
return true;
|
public void run() {
|
||||||
}
|
if (!running) return;
|
||||||
|
StreamConnection s = connect(makeUrl(address, uuid));
|
||||||
@Override
|
if (s != null) {
|
||||||
public int getPollingInterval() {
|
backoff.reset();
|
||||||
return backoff.getPollingInterval();
|
callback.outgoingConnectionCreated(c, wrapSocket(s));
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void poll(final Collection<ContactId> connected) {
|
|
||||||
if (!running) return;
|
|
||||||
backoff.increment();
|
|
||||||
// Try to connect to known devices in parallel
|
|
||||||
Map<ContactId, TransportProperties> remote =
|
|
||||||
callback.getRemoteProperties();
|
|
||||||
for (Entry<ContactId, TransportProperties> e : remote.entrySet()) {
|
|
||||||
final ContactId c = e.getKey();
|
|
||||||
if (connected.contains(c)) continue;
|
|
||||||
final String address = e.getValue().get(PROP_ADDRESS);
|
|
||||||
if (StringUtils.isNullOrEmpty(address)) continue;
|
|
||||||
final String uuid = e.getValue().get(PROP_UUID);
|
|
||||||
if (StringUtils.isNullOrEmpty(uuid)) continue;
|
|
||||||
ioExecutor.execute(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (!running) return;
|
|
||||||
StreamConnection s = connect(makeUrl(address, uuid));
|
|
||||||
if (s != null) {
|
|
||||||
backoff.reset();
|
|
||||||
callback.outgoingConnectionCreated(c, wrapSocket(s));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private StreamConnection connect(String url) {
|
private StreamConnection connect(String url) {
|
||||||
@@ -259,25 +201,13 @@ class BluetoothPlugin implements DuplexPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DuplexTransportConnection createConnection(ContactId c) {
|
protected DuplexTransportConnection connectToAddress(String address, String uuid) {
|
||||||
if (!running) return null;
|
String url = makeUrl(address, uuid);
|
||||||
TransportProperties p = callback.getRemoteProperties().get(c);
|
|
||||||
if (p == null) return null;
|
|
||||||
String address = p.get(PROP_ADDRESS);
|
|
||||||
if (StringUtils.isNullOrEmpty(address)) return null;
|
|
||||||
String uuid = p.get(PROP_UUID);
|
|
||||||
if (StringUtils.isNullOrEmpty(uuid)) return null;
|
|
||||||
String url = makeUrl(address, uuid);
|
|
||||||
StreamConnection s = connect(url);
|
StreamConnection s = connect(url);
|
||||||
if (s == null) return null;
|
if (s == null) return null;
|
||||||
return new BluetoothTransportConnection(this, s);
|
return new BluetoothTransportConnection(this, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supportsInvitations() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DuplexTransportConnection createInvitationConnection(PseudoRandom r,
|
public DuplexTransportConnection createInvitationConnection(PseudoRandom r,
|
||||||
long timeout, boolean alice) {
|
long timeout, boolean alice) {
|
||||||
@@ -297,7 +227,7 @@ class BluetoothPlugin implements DuplexPlugin {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (!running) {
|
if (!running) {
|
||||||
tryToClose(ss);
|
tryToClose((S)ss);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// Create the background tasks
|
// Create the background tasks
|
||||||
@@ -330,7 +260,7 @@ class BluetoothPlugin implements DuplexPlugin {
|
|||||||
return null;
|
return null;
|
||||||
} finally {
|
} finally {
|
||||||
// Closing the socket will terminate the listener task
|
// Closing the socket will terminate the listener task
|
||||||
tryToClose(ss);
|
tryToClose((S)ss);
|
||||||
closeSockets(futures, chosen);
|
closeSockets(futures, chosen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -362,11 +292,6 @@ class BluetoothPlugin implements DuplexPlugin {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supportsKeyAgreement() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public KeyAgreementListener createKeyAgreementListener(byte[] commitment) {
|
public KeyAgreementListener createKeyAgreementListener(byte[] commitment) {
|
||||||
if (!running) return null;
|
if (!running) return null;
|
||||||
@@ -385,7 +310,7 @@ class BluetoothPlugin implements DuplexPlugin {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (!running) {
|
if (!running) {
|
||||||
tryToClose(ss);
|
tryToClose((S)ss);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
BdfList descriptor = new BdfList();
|
BdfList descriptor = new BdfList();
|
||||||
@@ -395,33 +320,6 @@ class BluetoothPlugin implements DuplexPlugin {
|
|||||||
return new BluetoothKeyAgreementListener(descriptor, ss);
|
return new BluetoothKeyAgreementListener(descriptor, ss);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public DuplexTransportConnection createKeyAgreementConnection(
|
|
||||||
byte[] commitment, BdfList descriptor, long timeout) {
|
|
||||||
if (!isRunning()) return null;
|
|
||||||
String address;
|
|
||||||
try {
|
|
||||||
address = parseAddress(descriptor);
|
|
||||||
} catch (FormatException e) {
|
|
||||||
LOG.info("Invalid address in key agreement descriptor");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// No truncation necessary because COMMIT_LENGTH = 16
|
|
||||||
String uuid = UUID.nameUUIDFromBytes(commitment).toString();
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Connecting to key agreement UUID " + uuid);
|
|
||||||
String url = makeUrl(address, uuid);
|
|
||||||
StreamConnection s = connect(url);
|
|
||||||
if (s == null) return null;
|
|
||||||
return new BluetoothTransportConnection(this, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String parseAddress(BdfList descriptor) throws FormatException {
|
|
||||||
byte[] mac = descriptor.getRaw(1);
|
|
||||||
if (mac.length != 6) throw new FormatException();
|
|
||||||
return StringUtils.macToString(mac);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void makeDeviceDiscoverable() {
|
private void makeDeviceDiscoverable() {
|
||||||
// Try to make the device discoverable (requires root on Linux)
|
// Try to make the device discoverable (requires root on Linux)
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -62,7 +59,6 @@ import static android.app.Notification.DEFAULT_SOUND;
|
|||||||
import static android.app.Notification.DEFAULT_VIBRATE;
|
import static android.app.Notification.DEFAULT_VIBRATE;
|
||||||
import static android.content.Context.NOTIFICATION_SERVICE;
|
import static android.content.Context.NOTIFICATION_SERVICE;
|
||||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||||
import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
|
|
||||||
import static android.support.v4.app.NotificationCompat.CATEGORY_MESSAGE;
|
import static android.support.v4.app.NotificationCompat.CATEGORY_MESSAGE;
|
||||||
import static android.support.v4.app.NotificationCompat.CATEGORY_SOCIAL;
|
import static android.support.v4.app.NotificationCompat.CATEGORY_SOCIAL;
|
||||||
import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
|
import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
|
||||||
@@ -73,8 +69,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 +86,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 +101,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 +138,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 +151,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
clearForumPostNotification();
|
clearForumPostNotification();
|
||||||
clearBlogPostNotification();
|
clearBlogPostNotification();
|
||||||
clearIntroductionSuccessNotification();
|
clearIntroductionSuccessNotification();
|
||||||
appContext.unregisterReceiver(receiver);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -325,34 +286,30 @@ 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);
|
||||||
ContactId c = contactCounts.keySet().iterator().next();
|
ContactId c = contactCounts.keySet().iterator().next();
|
||||||
i.putExtra(CONTACT_ID, c.getInt());
|
i.putExtra(CONTACT_ID, c.getInt());
|
||||||
i.setData(Uri.parse(CONTACT_URI + "/" + c.getInt()));
|
i.setData(Uri.parse(CONTACT_URI + "/" + c.getInt()));
|
||||||
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
|
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
TaskStackBuilder t = TaskStackBuilder.create(appContext);
|
TaskStackBuilder t = TaskStackBuilder.create(appContext);
|
||||||
t.addParentStack(ConversationActivity.class);
|
t.addParentStack(ConversationActivity.class);
|
||||||
t.addNextIntent(i);
|
t.addNextIntent(i);
|
||||||
@@ -361,7 +318,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
// 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);
|
||||||
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
|
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
i.setData(Uri.parse(CONTACT_URI));
|
i.setData(Uri.parse(CONTACT_URI));
|
||||||
TaskStackBuilder t = TaskStackBuilder.create(appContext);
|
TaskStackBuilder t = TaskStackBuilder.create(appContext);
|
||||||
t.addParentStack(NavDrawerActivity.class);
|
t.addParentStack(NavDrawerActivity.class);
|
||||||
@@ -381,11 +338,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 +395,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);
|
||||||
@@ -461,7 +414,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
i.putExtra(GROUP_ID, g.getBytes());
|
i.putExtra(GROUP_ID, g.getBytes());
|
||||||
String idHex = StringUtils.toHexString(g.getBytes());
|
String idHex = StringUtils.toHexString(g.getBytes());
|
||||||
i.setData(Uri.parse(GROUP_URI + "/" + idHex));
|
i.setData(Uri.parse(GROUP_URI + "/" + idHex));
|
||||||
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
|
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
TaskStackBuilder t = TaskStackBuilder.create(appContext);
|
TaskStackBuilder t = TaskStackBuilder.create(appContext);
|
||||||
t.addParentStack(GroupActivity.class);
|
t.addParentStack(GroupActivity.class);
|
||||||
t.addNextIntent(i);
|
t.addNextIntent(i);
|
||||||
@@ -470,7 +423,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
// Touching the notification shows the group list
|
// Touching the notification shows the group list
|
||||||
Intent i = new Intent(appContext, NavDrawerActivity.class);
|
Intent i = new Intent(appContext, NavDrawerActivity.class);
|
||||||
i.putExtra(INTENT_GROUPS, true);
|
i.putExtra(INTENT_GROUPS, true);
|
||||||
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
|
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
i.setData(Uri.parse(GROUP_URI));
|
i.setData(Uri.parse(GROUP_URI));
|
||||||
TaskStackBuilder t = TaskStackBuilder.create(appContext);
|
TaskStackBuilder t = TaskStackBuilder.create(appContext);
|
||||||
t.addParentStack(NavDrawerActivity.class);
|
t.addParentStack(NavDrawerActivity.class);
|
||||||
@@ -530,26 +483,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);
|
||||||
@@ -557,7 +506,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
i.putExtra(GROUP_ID, g.getBytes());
|
i.putExtra(GROUP_ID, g.getBytes());
|
||||||
String idHex = StringUtils.toHexString(g.getBytes());
|
String idHex = StringUtils.toHexString(g.getBytes());
|
||||||
i.setData(Uri.parse(FORUM_URI + "/" + idHex));
|
i.setData(Uri.parse(FORUM_URI + "/" + idHex));
|
||||||
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
|
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
TaskStackBuilder t = TaskStackBuilder.create(appContext);
|
TaskStackBuilder t = TaskStackBuilder.create(appContext);
|
||||||
t.addParentStack(ForumActivity.class);
|
t.addParentStack(ForumActivity.class);
|
||||||
t.addNextIntent(i);
|
t.addNextIntent(i);
|
||||||
@@ -566,7 +515,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
// Touching the notification shows the forum list
|
// Touching the notification shows the forum list
|
||||||
Intent i = new Intent(appContext, NavDrawerActivity.class);
|
Intent i = new Intent(appContext, NavDrawerActivity.class);
|
||||||
i.putExtra(INTENT_FORUMS, true);
|
i.putExtra(INTENT_FORUMS, true);
|
||||||
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
|
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
i.setData(Uri.parse(FORUM_URI));
|
i.setData(Uri.parse(FORUM_URI));
|
||||||
TaskStackBuilder t = TaskStackBuilder.create(appContext);
|
TaskStackBuilder t = TaskStackBuilder.create(appContext);
|
||||||
t.addParentStack(NavDrawerActivity.class);
|
t.addParentStack(NavDrawerActivity.class);
|
||||||
@@ -630,26 +579,22 @@ 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);
|
||||||
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
|
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
i.setData(Uri.parse(BLOG_URI));
|
i.setData(Uri.parse(BLOG_URI));
|
||||||
TaskStackBuilder t = TaskStackBuilder.create(appContext);
|
TaskStackBuilder t = TaskStackBuilder.create(appContext);
|
||||||
t.addParentStack(NavDrawerActivity.class);
|
t.addParentStack(NavDrawerActivity.class);
|
||||||
@@ -696,21 +641,16 @@ 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);
|
||||||
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
|
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
i.setData(Uri.parse(CONTACT_URI));
|
i.setData(Uri.parse(CONTACT_URI));
|
||||||
TaskStackBuilder t = TaskStackBuilder.create(appContext);
|
TaskStackBuilder t = TaskStackBuilder.create(appContext);
|
||||||
t.addParentStack(NavDrawerActivity.class);
|
t.addParentStack(NavDrawerActivity.class);
|
||||||
@@ -847,27 +787,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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import javax.inject.Inject;
|
|||||||
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
|
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
|
||||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||||
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_SINGLE_TOP;
|
|
||||||
import static android.support.v4.app.NotificationCompat.CATEGORY_SERVICE;
|
import static android.support.v4.app.NotificationCompat.CATEGORY_SERVICE;
|
||||||
import static android.support.v4.app.NotificationCompat.PRIORITY_MIN;
|
import static android.support.v4.app.NotificationCompat.PRIORITY_MIN;
|
||||||
import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
|
import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
|
||||||
@@ -83,8 +82,7 @@ public class BriarService extends Service {
|
|||||||
b.setWhen(0); // Don't show the time
|
b.setWhen(0); // Don't show the time
|
||||||
b.setOngoing(true);
|
b.setOngoing(true);
|
||||||
Intent i = new Intent(this, NavDrawerActivity.class);
|
Intent i = new Intent(this, NavDrawerActivity.class);
|
||||||
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP |
|
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
FLAG_ACTIVITY_SINGLE_TOP);
|
|
||||||
b.setContentIntent(PendingIntent.getActivity(this, 0, i, 0));
|
b.setContentIntent(PendingIntent.getActivity(this, 0, i, 0));
|
||||||
if (Build.VERSION.SDK_INT >= 21) {
|
if (Build.VERSION.SDK_INT >= 21) {
|
||||||
b.setCategory(CATEGORY_SERVICE);
|
b.setCategory(CATEGORY_SERVICE);
|
||||||
|
|||||||
@@ -0,0 +1,282 @@
|
|||||||
|
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.PackageInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
|
import android.content.pm.Signature;
|
||||||
|
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.bramble.util.StringUtils;
|
||||||
|
import org.briarproject.briar.api.android.ScreenFilterMonitor;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
import java.security.cert.CertificateFactory;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
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.Logger;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
|
||||||
|
import static android.content.Intent.ACTION_PACKAGE_ADDED;
|
||||||
|
import static android.content.Intent.EXTRA_REPLACING;
|
||||||
|
import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
|
||||||
|
import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
|
||||||
|
import static android.content.pm.PackageManager.GET_PERMISSIONS;
|
||||||
|
import static android.content.pm.PackageManager.GET_SIGNATURES;
|
||||||
|
import static java.util.logging.Level.WARNING;
|
||||||
|
|
||||||
|
@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";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ignore Play Services if it uses this package name and public key - it's
|
||||||
|
* effectively a system app, but not flagged as such on older systems
|
||||||
|
*/
|
||||||
|
private static final String PLAY_SERVICES_PACKAGE =
|
||||||
|
"com.google.android.gms";
|
||||||
|
private static final String PLAY_SERVICES_PUBLIC_KEY =
|
||||||
|
"30820120300D06092A864886F70D01010105000382010D0030820108" +
|
||||||
|
"0282010100AB562E00D83BA208AE0A966F124E29DA11F2AB56D08F58" +
|
||||||
|
"E2CCA91303E9B754D372F640A71B1DCB130967624E4656A7776A9219" +
|
||||||
|
"3DB2E5BFB724A91E77188B0E6A47A43B33D9609B77183145CCDF7B2E" +
|
||||||
|
"586674C9E1565B1F4C6A5955BFF251A63DABF9C55C27222252E875E4" +
|
||||||
|
"F8154A645F897168C0B1BFC612EABF785769BB34AA7984DC7E2EA276" +
|
||||||
|
"4CAE8307D8C17154D7EE5F64A51A44A602C249054157DC02CD5F5C0E" +
|
||||||
|
"55FBEF8519FBE327F0B1511692C5A06F19D18385F5C4DBC2D6B93F68" +
|
||||||
|
"CC2979C70E18AB93866B3BD5DB8999552A0E3B4C99DF58FB918BEDC1" +
|
||||||
|
"82BA35E003C1B4B10DD244A8EE24FFFD333872AB5221985EDAB0FC0D" +
|
||||||
|
"0B145B6AA192858E79020103";
|
||||||
|
|
||||||
|
private final Context appContext;
|
||||||
|
private final AndroidExecutor androidExecutor;
|
||||||
|
private final PackageManager pm;
|
||||||
|
private final SharedPreferences prefs;
|
||||||
|
private final AtomicBoolean used = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
// The following must only be accessed on the UI thread
|
||||||
|
private final Set<String> apps = new HashSet<>();
|
||||||
|
private final Set<String> shownApps;
|
||||||
|
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(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() {
|
||||||
|
// Result 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(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(GET_PERMISSIONS);
|
||||||
|
for (PackageInfo packageInfo : packageInfos) {
|
||||||
|
if (isOverlayApp(packageInfo)) {
|
||||||
|
String name = pkgToString(packageInfo);
|
||||||
|
if (name != null) {
|
||||||
|
screenFilterApps.add(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return screenFilterApps;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if a package 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, GET_PERMISSIONS);
|
||||||
|
if (isOverlayApp(pkgInfo)) {
|
||||||
|
return pkgToString(pkgInfo);
|
||||||
|
}
|
||||||
|
} catch (NameNotFoundException e) {
|
||||||
|
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetches 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 package is a user app using the permission.
|
||||||
|
private boolean isOverlayApp(PackageInfo packageInfo) {
|
||||||
|
int mask = FLAG_SYSTEM | FLAG_UPDATED_SYSTEM_APP;
|
||||||
|
// Ignore system apps
|
||||||
|
if ((packageInfo.applicationInfo.flags & mask) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Ignore Play Services, it's effectively a system app
|
||||||
|
if (isPlayServices(packageInfo.packageName)) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isPlayServices(String pkg) {
|
||||||
|
if (!PLAY_SERVICES_PACKAGE.equals(pkg)) return false;
|
||||||
|
try {
|
||||||
|
PackageInfo sigs = pm.getPackageInfo(pkg, GET_SIGNATURES);
|
||||||
|
// The genuine Play Services app should have a single signature
|
||||||
|
Signature[] signatures = sigs.signatures;
|
||||||
|
if (signatures == null || signatures.length != 1) return false;
|
||||||
|
// Extract the public key from the signature
|
||||||
|
CertificateFactory certFactory =
|
||||||
|
CertificateFactory.getInstance("X509");
|
||||||
|
byte[] signatureBytes = signatures[0].toByteArray();
|
||||||
|
InputStream in = new ByteArrayInputStream(signatureBytes);
|
||||||
|
X509Certificate cert =
|
||||||
|
(X509Certificate) certFactory.generateCertificate(in);
|
||||||
|
byte[] publicKeyBytes = cert.getPublicKey().getEncoded();
|
||||||
|
String publicKey = StringUtils.toHexString(publicKeyBytes);
|
||||||
|
return PLAY_SERVICES_PUBLIC_KEY.equals(publicKey);
|
||||||
|
} catch (NameNotFoundException | CertificateException e) {
|
||||||
|
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
|
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
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ import javax.inject.Inject;
|
|||||||
|
|
||||||
import static android.app.Activity.RESULT_OK;
|
import static android.app.Activity.RESULT_OK;
|
||||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||||
import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
|
|
||||||
import static android.widget.Toast.LENGTH_SHORT;
|
import static android.widget.Toast.LENGTH_SHORT;
|
||||||
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
|
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
|
||||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_SHARE_BLOG;
|
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_SHARE_BLOG;
|
||||||
@@ -149,14 +148,14 @@ public class BlogFragment extends BaseFragment
|
|||||||
return true;
|
return true;
|
||||||
case R.id.action_blog_share:
|
case R.id.action_blog_share:
|
||||||
Intent i2 = new Intent(getActivity(), ShareBlogActivity.class);
|
Intent i2 = new Intent(getActivity(), ShareBlogActivity.class);
|
||||||
i2.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
|
i2.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
i2.putExtra(GROUP_ID, groupId.getBytes());
|
i2.putExtra(GROUP_ID, groupId.getBytes());
|
||||||
startActivityForResult(i2, REQUEST_SHARE_BLOG);
|
startActivityForResult(i2, REQUEST_SHARE_BLOG);
|
||||||
return true;
|
return true;
|
||||||
case R.id.action_blog_sharing_status:
|
case R.id.action_blog_sharing_status:
|
||||||
Intent i3 = new Intent(getActivity(),
|
Intent i3 = new Intent(getActivity(),
|
||||||
BlogSharingStatusActivity.class);
|
BlogSharingStatusActivity.class);
|
||||||
i3.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
|
i3.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
i3.putExtra(GROUP_ID, groupId.getBytes());
|
i3.putExtra(GROUP_ID, groupId.getBytes());
|
||||||
startActivity(i3);
|
startActivity(i3);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -48,6 +48,10 @@ public class BlogPostItem implements Comparable<BlogPostItem> {
|
|||||||
return body;
|
return body;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isRssFeed() {
|
||||||
|
return header.isRssFeed();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isRead() {
|
public boolean isRead() {
|
||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,7 +108,8 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
|
|||||||
author.setAuthor(a);
|
author.setAuthor(a);
|
||||||
author.setAuthorStatus(post.getAuthorStatus());
|
author.setAuthorStatus(post.getAuthorStatus());
|
||||||
author.setDate(post.getTimestamp());
|
author.setDate(post.getTimestamp());
|
||||||
author.setPersona(AuthorView.NORMAL);
|
author.setPersona(
|
||||||
|
item.isRssFeed() ? AuthorView.RSS_FEED : AuthorView.NORMAL);
|
||||||
// TODO make author clickable more often #624
|
// TODO make author clickable more often #624
|
||||||
if (item.getHeader().getType() == POST) {
|
if (item.getHeader().getType() == POST) {
|
||||||
author.setBlogLink(post.getGroupId());
|
author.setBlogLink(post.getGroupId());
|
||||||
@@ -168,7 +169,9 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
|
|||||||
reblogger.setVisibility(VISIBLE);
|
reblogger.setVisibility(VISIBLE);
|
||||||
reblogger.setPersona(AuthorView.REBLOGGER);
|
reblogger.setPersona(AuthorView.REBLOGGER);
|
||||||
|
|
||||||
author.setPersona(AuthorView.COMMENTER);
|
author.setPersona(item.getHeader().getRootPost().isRssFeed() ?
|
||||||
|
AuthorView.RSS_FEED_REBLOGGED :
|
||||||
|
AuthorView.COMMENTER);
|
||||||
|
|
||||||
// comments
|
// comments
|
||||||
for (BlogCommentHeader c : item.getComments()) {
|
for (BlogCommentHeader c : item.getComments()) {
|
||||||
|
|||||||
@@ -179,7 +179,6 @@ public class FeedFragment extends BaseFragment implements
|
|||||||
case R.id.action_rss_feeds_import:
|
case R.id.action_rss_feeds_import:
|
||||||
Intent i2 =
|
Intent i2 =
|
||||||
new Intent(getActivity(), RssFeedImportActivity.class);
|
new Intent(getActivity(), RssFeedImportActivity.class);
|
||||||
i2.putExtra(GROUP_ID, personalBlog.getId().getBytes());
|
|
||||||
startActivity(i2);
|
startActivity(i2);
|
||||||
return true;
|
return true;
|
||||||
case R.id.action_rss_feeds_manage:
|
case R.id.action_rss_feeds_manage:
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import android.view.LayoutInflater;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.OnClickListener;
|
import android.view.View.OnClickListener;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageButton;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
@@ -39,12 +39,7 @@ class RssFeedAdapter extends BriarAdapter<Feed, RssFeedAdapter.FeedViewHolder> {
|
|||||||
if (item == null) return;
|
if (item == null) return;
|
||||||
|
|
||||||
// Feed Title
|
// Feed Title
|
||||||
if (item.getTitle() != null) {
|
ui.title.setText(item.getTitle());
|
||||||
ui.title.setText(item.getTitle());
|
|
||||||
ui.title.setVisibility(VISIBLE);
|
|
||||||
} else {
|
|
||||||
ui.title.setVisibility(GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete Button
|
// Delete Button
|
||||||
ui.delete.setOnClickListener(new OnClickListener() {
|
ui.delete.setOnClickListener(new OnClickListener() {
|
||||||
@@ -75,6 +70,14 @@ class RssFeedAdapter extends BriarAdapter<Feed, RssFeedAdapter.FeedViewHolder> {
|
|||||||
} else {
|
} else {
|
||||||
ui.description.setVisibility(GONE);
|
ui.description.setVisibility(GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Open feed's blog when clicked
|
||||||
|
ui.layout.setOnClickListener(new OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
listener.onFeedClick(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -99,8 +102,9 @@ class RssFeedAdapter extends BriarAdapter<Feed, RssFeedAdapter.FeedViewHolder> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static class FeedViewHolder extends RecyclerView.ViewHolder {
|
static class FeedViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
private final View layout;
|
||||||
private final TextView title;
|
private final TextView title;
|
||||||
private final ImageView delete;
|
private final ImageButton delete;
|
||||||
private final TextView imported;
|
private final TextView imported;
|
||||||
private final TextView updated;
|
private final TextView updated;
|
||||||
private final TextView author;
|
private final TextView author;
|
||||||
@@ -110,8 +114,9 @@ class RssFeedAdapter extends BriarAdapter<Feed, RssFeedAdapter.FeedViewHolder> {
|
|||||||
private FeedViewHolder(View v) {
|
private FeedViewHolder(View v) {
|
||||||
super(v);
|
super(v);
|
||||||
|
|
||||||
|
layout = v;
|
||||||
title = (TextView) v.findViewById(R.id.titleView);
|
title = (TextView) v.findViewById(R.id.titleView);
|
||||||
delete = (ImageView) v.findViewById(R.id.deleteButton);
|
delete = (ImageButton) v.findViewById(R.id.deleteButton);
|
||||||
imported = (TextView) v.findViewById(R.id.importedView);
|
imported = (TextView) v.findViewById(R.id.importedView);
|
||||||
updated = (TextView) v.findViewById(R.id.updatedView);
|
updated = (TextView) v.findViewById(R.id.updatedView);
|
||||||
author = (TextView) v.findViewById(R.id.authorView);
|
author = (TextView) v.findViewById(R.id.authorView);
|
||||||
@@ -121,6 +126,7 @@ class RssFeedAdapter extends BriarAdapter<Feed, RssFeedAdapter.FeedViewHolder> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface RssFeedListener {
|
interface RssFeedListener {
|
||||||
|
void onFeedClick(Feed feed);
|
||||||
void onDeleteClick(Feed feed);
|
void onDeleteClick(Feed feed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package org.briarproject.briar.android.blog;
|
package org.briarproject.briar.android.blog;
|
||||||
|
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v7.app.AlertDialog;
|
import android.support.v7.app.AlertDialog;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
@@ -15,7 +14,6 @@ import android.widget.ProgressBar;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.sync.GroupId;
|
|
||||||
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.BriarActivity;
|
import org.briarproject.briar.android.activity.BriarActivity;
|
||||||
@@ -44,9 +42,6 @@ public class RssFeedImportActivity extends BriarActivity {
|
|||||||
@IoExecutor
|
@IoExecutor
|
||||||
Executor ioExecutor;
|
Executor ioExecutor;
|
||||||
|
|
||||||
// Fields that are accessed from background threads must be volatile
|
|
||||||
private volatile GroupId groupId = null;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@SuppressWarnings("WeakerAccess")
|
@SuppressWarnings("WeakerAccess")
|
||||||
volatile FeedManager feedManager;
|
volatile FeedManager feedManager;
|
||||||
@@ -55,12 +50,6 @@ public class RssFeedImportActivity extends BriarActivity {
|
|||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
// GroupId from Intent
|
|
||||||
Intent i = getIntent();
|
|
||||||
byte[] b = i.getByteArrayExtra(GROUP_ID);
|
|
||||||
if (b == null) throw new IllegalStateException("No Group in intent.");
|
|
||||||
groupId = new GroupId(b);
|
|
||||||
|
|
||||||
setContentView(R.layout.activity_rss_feed_import);
|
setContentView(R.layout.activity_rss_feed_import);
|
||||||
|
|
||||||
urlInput = (EditText) findViewById(R.id.urlInput);
|
urlInput = (EditText) findViewById(R.id.urlInput);
|
||||||
@@ -128,7 +117,7 @@ public class RssFeedImportActivity extends BriarActivity {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
feedManager.addFeed(url, groupId);
|
feedManager.addFeed(url);
|
||||||
feedImported();
|
feedImported();
|
||||||
} catch (DbException | IOException e) {
|
} catch (DbException | IOException e) {
|
||||||
if (LOG.isLoggable(WARNING))
|
if (LOG.isLoggable(WARNING))
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
package org.briarproject.briar.android.blog;
|
package org.briarproject.briar.android.blog;
|
||||||
|
|
||||||
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.design.widget.Snackbar;
|
import android.support.design.widget.Snackbar;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.sync.GroupId;
|
|
||||||
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.BriarActivity;
|
import org.briarproject.briar.android.activity.BriarActivity;
|
||||||
@@ -23,6 +24,7 @@ import java.util.logging.Logger;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||||
import static android.support.design.widget.Snackbar.LENGTH_LONG;
|
import static android.support.design.widget.Snackbar.LENGTH_LONG;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
|
|
||||||
@@ -34,7 +36,6 @@ public class RssFeedManageActivity extends BriarActivity
|
|||||||
|
|
||||||
private BriarRecyclerView list;
|
private BriarRecyclerView list;
|
||||||
private RssFeedAdapter adapter;
|
private RssFeedAdapter adapter;
|
||||||
private GroupId groupId;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@SuppressWarnings("WeakerAccess")
|
@SuppressWarnings("WeakerAccess")
|
||||||
@@ -44,12 +45,6 @@ public class RssFeedManageActivity extends BriarActivity
|
|||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
// GroupId from Intent
|
|
||||||
Intent i = getIntent();
|
|
||||||
byte[] b = i.getByteArrayExtra(GROUP_ID);
|
|
||||||
if (b == null) throw new IllegalStateException("No Group in intent.");
|
|
||||||
groupId = new GroupId(b);
|
|
||||||
|
|
||||||
setContentView(R.layout.activity_rss_feed_manage);
|
setContentView(R.layout.activity_rss_feed_manage);
|
||||||
|
|
||||||
adapter = new RssFeedAdapter(this, this);
|
adapter = new RssFeedAdapter(this, this);
|
||||||
@@ -87,7 +82,6 @@ public class RssFeedManageActivity extends BriarActivity
|
|||||||
return true;
|
return true;
|
||||||
case R.id.action_rss_feeds_import:
|
case R.id.action_rss_feeds_import:
|
||||||
Intent i = new Intent(this, RssFeedImportActivity.class);
|
Intent i = new Intent(this, RssFeedImportActivity.class);
|
||||||
i.putExtra(GROUP_ID, groupId.getBytes());
|
|
||||||
startActivity(i);
|
startActivity(i);
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
@@ -100,21 +94,32 @@ public class RssFeedManageActivity extends BriarActivity
|
|||||||
component.inject(this);
|
component.inject(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFeedClick(Feed feed) {
|
||||||
|
Intent i = new Intent(this, BlogActivity.class);
|
||||||
|
i.putExtra(GROUP_ID, feed.getBlogId().getBytes());
|
||||||
|
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
|
startActivity(i);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDeleteClick(final Feed feed) {
|
public void onDeleteClick(final Feed feed) {
|
||||||
runOnDbThread(new Runnable() {
|
DialogInterface.OnClickListener okListener =
|
||||||
@Override
|
new DialogInterface.OnClickListener() {
|
||||||
public void run() {
|
@Override
|
||||||
try {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
feedManager.removeFeed(feed.getUrl());
|
deleteFeed(feed);
|
||||||
onFeedDeleted(feed);
|
}
|
||||||
} catch (DbException e) {
|
};
|
||||||
if (LOG.isLoggable(WARNING))
|
AlertDialog.Builder builder = new AlertDialog.Builder(this,
|
||||||
LOG.log(WARNING, e.toString(), e);
|
R.style.BriarDialogTheme);
|
||||||
onDeleteError();
|
builder.setTitle(getString(R.string.blogs_rss_remove_feed));
|
||||||
}
|
builder.setMessage(
|
||||||
}
|
getString(R.string.blogs_rss_remove_feed_dialog_message));
|
||||||
});
|
builder.setPositiveButton(R.string.cancel, null);
|
||||||
|
builder.setNegativeButton(R.string.blogs_rss_remove_feed_ok,
|
||||||
|
okListener);
|
||||||
|
builder.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadFeeds() {
|
private void loadFeeds() {
|
||||||
@@ -149,6 +154,22 @@ public class RssFeedManageActivity extends BriarActivity
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void deleteFeed(final Feed feed) {
|
||||||
|
runOnDbThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
feedManager.removeFeed(feed);
|
||||||
|
onFeedDeleted(feed);
|
||||||
|
} catch (DbException e) {
|
||||||
|
if (LOG.isLoggable(WARNING))
|
||||||
|
LOG.log(WARNING, e.toString(), e);
|
||||||
|
onDeleteError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void onLoadError() {
|
private void onLoadError() {
|
||||||
runOnUiThreadUnlessDestroyed(new Runnable() {
|
runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -946,6 +946,7 @@ public class ConversationActivity extends BriarActivity
|
|||||||
@Override
|
@Override
|
||||||
public void respondToRequest(final ConversationRequestItem item,
|
public void respondToRequest(final ConversationRequestItem item,
|
||||||
final boolean accept) {
|
final boolean accept) {
|
||||||
|
item.setAnswered(true);
|
||||||
int position = adapter.findItemPosition(item);
|
int position = adapter.findItemPosition(item);
|
||||||
if (position != INVALID_POSITION) {
|
if (position != INVALID_POSITION) {
|
||||||
adapter.notifyItemChanged(position, item);
|
adapter.notifyItemChanged(position, item);
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ class ConversationRequestItem extends ConversationNoticeInItem {
|
|||||||
private final GroupId requestedGroupId;
|
private final GroupId requestedGroupId;
|
||||||
private final RequestType requestType;
|
private final RequestType requestType;
|
||||||
private final SessionId sessionId;
|
private final SessionId sessionId;
|
||||||
private final boolean answered, canBeOpened;
|
private final boolean canBeOpened;
|
||||||
|
private boolean answered;
|
||||||
|
|
||||||
ConversationRequestItem(MessageId id, GroupId groupId,
|
ConversationRequestItem(MessageId id, GroupId groupId,
|
||||||
RequestType requestType, SessionId sessionId, String text,
|
RequestType requestType, SessionId sessionId, String text,
|
||||||
@@ -53,6 +54,10 @@ class ConversationRequestItem extends ConversationNoticeInItem {
|
|||||||
return answered;
|
return answered;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setAnswered(boolean answered) {
|
||||||
|
this.answered = answered;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean canBeOpened() {
|
public boolean canBeOpened() {
|
||||||
return canBeOpened;
|
return canBeOpened;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,8 @@ class ConversationRequestViewHolder extends ConversationNoticeInViewHolder {
|
|||||||
acceptButton.setOnClickListener(new OnClickListener() {
|
acceptButton.setOnClickListener(new OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
|
acceptButton.setEnabled(false);
|
||||||
|
declineButton.setEnabled(false);
|
||||||
listener.respondToRequest(item, true);
|
listener.respondToRequest(item, true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -58,6 +60,8 @@ class ConversationRequestViewHolder extends ConversationNoticeInViewHolder {
|
|||||||
declineButton.setOnClickListener(new OnClickListener() {
|
declineButton.setOnClickListener(new OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
|
acceptButton.setEnabled(false);
|
||||||
|
declineButton.setEnabled(false);
|
||||||
listener.respondToRequest(item, false);
|
listener.respondToRequest(item, false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ import javax.annotation.Nullable;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||||
import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
|
|
||||||
import static android.widget.Toast.LENGTH_SHORT;
|
import static android.widget.Toast.LENGTH_SHORT;
|
||||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_SHARE_FORUM;
|
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_SHARE_FORUM;
|
||||||
import static org.briarproject.briar.api.forum.ForumConstants.MAX_FORUM_POST_BODY_LENGTH;
|
import static org.briarproject.briar.api.forum.ForumConstants.MAX_FORUM_POST_BODY_LENGTH;
|
||||||
@@ -117,18 +116,15 @@ public class ForumActivity extends
|
|||||||
public boolean onOptionsItemSelected(final MenuItem item) {
|
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||||
// Handle presses on the action bar items
|
// Handle presses on the action bar items
|
||||||
switch (item.getItemId()) {
|
switch (item.getItemId()) {
|
||||||
case R.id.action_forum_compose_post:
|
|
||||||
showTextInput(null);
|
|
||||||
return true;
|
|
||||||
case R.id.action_forum_share:
|
case R.id.action_forum_share:
|
||||||
Intent i2 = new Intent(this, ShareForumActivity.class);
|
Intent i2 = new Intent(this, ShareForumActivity.class);
|
||||||
i2.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
|
i2.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
i2.putExtra(GROUP_ID, groupId.getBytes());
|
i2.putExtra(GROUP_ID, groupId.getBytes());
|
||||||
startActivityForResult(i2, REQUEST_SHARE_FORUM);
|
startActivityForResult(i2, REQUEST_SHARE_FORUM);
|
||||||
return true;
|
return true;
|
||||||
case R.id.action_forum_sharing_status:
|
case R.id.action_forum_sharing_status:
|
||||||
Intent i3 = new Intent(this, ForumSharingStatusActivity.class);
|
Intent i3 = new Intent(this, ForumSharingStatusActivity.class);
|
||||||
i3.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
|
i3.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
i3.putExtra(GROUP_ID, groupId.getBytes());
|
i3.putExtra(GROUP_ID, groupId.getBytes());
|
||||||
startActivity(i3);
|
startActivity(i3);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,4 +107,4 @@ public class PanicResponderActivity extends BriarActivity {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user