mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-16 20:59:54 +01:00
Compare commits
49 Commits
beta-1.4.8
...
poll-own-h
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66d907d1b2 | ||
|
|
7d336c98e4 | ||
|
|
963e510c3b | ||
|
|
213d2f1da4 | ||
|
|
3038b92dbc | ||
|
|
7cd3c2890b | ||
|
|
a38933df66 | ||
|
|
4993873ae2 | ||
|
|
02b805ce42 | ||
|
|
1a6ba16a59 | ||
|
|
654a05df8a | ||
|
|
ffe1876337 | ||
|
|
98963955b1 | ||
|
|
d83efce002 | ||
|
|
efb1b8c1ad | ||
|
|
3f36db8b3a | ||
|
|
a2f4e70a48 | ||
|
|
01e72eff40 | ||
|
|
dbcea3e1d1 | ||
|
|
6288577daa | ||
|
|
5d363496bd | ||
|
|
75b5c92495 | ||
|
|
bcc98cc4c9 | ||
|
|
2d605089bc | ||
|
|
01f8be1b66 | ||
|
|
eac6d0aa40 | ||
|
|
713be403eb | ||
|
|
2fd948b81d | ||
|
|
62af5e858c | ||
|
|
2201585a34 | ||
|
|
97d11cc602 | ||
|
|
79f41064e4 | ||
|
|
9aacd9d3d8 | ||
|
|
78f4dee43d | ||
|
|
2b4a1cf54b | ||
|
|
bb71de1a78 | ||
|
|
08bf13e44f | ||
|
|
cc7de2c70a | ||
|
|
0f4aa8027a | ||
|
|
b161a5e115 | ||
|
|
e112f69c4e | ||
|
|
4623d03c93 | ||
|
|
b128220be3 | ||
|
|
6aa24af94c | ||
|
|
de63a50662 | ||
|
|
5517ac14ed | ||
|
|
2672d82a40 | ||
|
|
63c0210047 | ||
|
|
47085722da |
@@ -118,11 +118,3 @@ mailbox integration test:
|
|||||||
- cd "$CI_PROJECT_DIR"
|
- cd "$CI_PROJECT_DIR"
|
||||||
- bramble-core/src/test/bash/wait-for-mailbox.sh
|
- bramble-core/src/test/bash/wait-for-mailbox.sh
|
||||||
- OPTIONAL_TESTS=org.briarproject.bramble.mailbox.MailboxIntegrationTest ./gradlew --info bramble-core:test --tests MailboxIntegrationTest
|
- OPTIONAL_TESTS=org.briarproject.bramble.mailbox.MailboxIntegrationTest ./gradlew --info bramble-core:test --tests MailboxIntegrationTest
|
||||||
|
|
||||||
pre_release_tests:
|
|
||||||
extends: .optional_tests
|
|
||||||
script:
|
|
||||||
- OPTIONAL_TESTS=org.briarproject.bramble.plugin.tor.BridgeTest ./gradlew --info bramble-java:test --tests BridgeTest
|
|
||||||
timeout: 3h
|
|
||||||
only:
|
|
||||||
- tags
|
|
||||||
|
|||||||
@@ -86,8 +86,8 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
|
|||||||
BluetoothConnectionFactory<BluetoothSocket> connectionFactory =
|
BluetoothConnectionFactory<BluetoothSocket> connectionFactory =
|
||||||
new AndroidBluetoothConnectionFactory(connectionLimiter,
|
new AndroidBluetoothConnectionFactory(connectionLimiter,
|
||||||
wakeLockManager, timeoutMonitor);
|
wakeLockManager, timeoutMonitor);
|
||||||
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
Backoff backoff = backoffFactory.createBackoff(eventBus, ID,
|
||||||
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
MIN_POLLING_INTERVAL, MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
||||||
AndroidBluetoothPlugin plugin = new AndroidBluetoothPlugin(
|
AndroidBluetoothPlugin plugin = new AndroidBluetoothPlugin(
|
||||||
connectionLimiter, connectionFactory, ioExecutor,
|
connectionLimiter, connectionFactory, ioExecutor,
|
||||||
wakefulIoExecutor, secureRandom, androidExecutor, app,
|
wakefulIoExecutor, secureRandom, androidExecutor, app,
|
||||||
|
|||||||
@@ -61,8 +61,8 @@ public class AndroidLanTcpPluginFactory implements DuplexPluginFactory {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DuplexPlugin createPlugin(PluginCallback callback) {
|
public DuplexPlugin createPlugin(PluginCallback callback) {
|
||||||
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
Backoff backoff = backoffFactory.createBackoff(eventBus, ID,
|
||||||
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
MIN_POLLING_INTERVAL, MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
||||||
AndroidLanTcpPlugin plugin = new AndroidLanTcpPlugin(ioExecutor,
|
AndroidLanTcpPlugin plugin = new AndroidLanTcpPlugin(ioExecutor,
|
||||||
wakefulIoExecutor, app, backoff, callback,
|
wakefulIoExecutor, app, backoff, callback,
|
||||||
MAX_LATENCY, MAX_IDLE_TIME, CONNECTION_TIMEOUT);
|
MAX_LATENCY, MAX_IDLE_TIME, CONNECTION_TIMEOUT);
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import org.briarproject.bramble.api.battery.BatteryManager;
|
|||||||
import org.briarproject.bramble.api.network.NetworkManager;
|
import org.briarproject.bramble.api.network.NetworkManager;
|
||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.Backoff;
|
|
||||||
import org.briarproject.bramble.api.plugin.PluginCallback;
|
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||||
import org.briarproject.bramble.api.system.AndroidWakeLock;
|
import org.briarproject.bramble.api.system.AndroidWakeLock;
|
||||||
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
|
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
|
||||||
@@ -64,7 +63,6 @@ class AndroidTorPlugin extends TorPlugin {
|
|||||||
CircumventionProvider circumventionProvider,
|
CircumventionProvider circumventionProvider,
|
||||||
BatteryManager batteryManager,
|
BatteryManager batteryManager,
|
||||||
AndroidWakeLockManager wakeLockManager,
|
AndroidWakeLockManager wakeLockManager,
|
||||||
Backoff backoff,
|
|
||||||
TorRendezvousCrypto torRendezvousCrypto,
|
TorRendezvousCrypto torRendezvousCrypto,
|
||||||
PluginCallback callback,
|
PluginCallback callback,
|
||||||
String architecture,
|
String architecture,
|
||||||
@@ -75,7 +73,7 @@ class AndroidTorPlugin extends TorPlugin {
|
|||||||
int torControlPort) {
|
int torControlPort) {
|
||||||
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
||||||
torSocketFactory, clock, resourceProvider,
|
torSocketFactory, clock, resourceProvider,
|
||||||
circumventionProvider, batteryManager, backoff,
|
circumventionProvider, batteryManager,
|
||||||
torRendezvousCrypto, callback, architecture, maxLatency,
|
torRendezvousCrypto, callback, architecture, maxLatency,
|
||||||
maxIdleTime, torDirectory, torSocksPort, torControlPort);
|
maxIdleTime, torDirectory, torSocksPort, torControlPort);
|
||||||
this.app = app;
|
this.app = app;
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ import org.briarproject.bramble.api.event.EventBus;
|
|||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.network.NetworkManager;
|
import org.briarproject.bramble.api.network.NetworkManager;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.Backoff;
|
|
||||||
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
|
||||||
import org.briarproject.bramble.api.plugin.PluginCallback;
|
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||||
import org.briarproject.bramble.api.plugin.TorControlPort;
|
import org.briarproject.bramble.api.plugin.TorControlPort;
|
||||||
import org.briarproject.bramble.api.plugin.TorDirectory;
|
import org.briarproject.bramble.api.plugin.TorDirectory;
|
||||||
@@ -44,7 +42,6 @@ public class AndroidTorPluginFactory extends TorPluginFactory {
|
|||||||
LocationUtils locationUtils,
|
LocationUtils locationUtils,
|
||||||
EventBus eventBus,
|
EventBus eventBus,
|
||||||
SocketFactory torSocketFactory,
|
SocketFactory torSocketFactory,
|
||||||
BackoffFactory backoffFactory,
|
|
||||||
ResourceProvider resourceProvider,
|
ResourceProvider resourceProvider,
|
||||||
CircumventionProvider circumventionProvider,
|
CircumventionProvider circumventionProvider,
|
||||||
BatteryManager batteryManager,
|
BatteryManager batteryManager,
|
||||||
@@ -56,7 +53,7 @@ public class AndroidTorPluginFactory extends TorPluginFactory {
|
|||||||
Application app,
|
Application app,
|
||||||
AndroidWakeLockManager wakeLockManager) {
|
AndroidWakeLockManager wakeLockManager) {
|
||||||
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
||||||
eventBus, torSocketFactory, backoffFactory, resourceProvider,
|
eventBus, torSocketFactory, resourceProvider,
|
||||||
circumventionProvider, batteryManager, clock, crypto,
|
circumventionProvider, batteryManager, clock, crypto,
|
||||||
torDirectory, torSocksPort, torControlPort);
|
torDirectory, torSocksPort, torControlPort);
|
||||||
this.app = app;
|
this.app = app;
|
||||||
@@ -76,14 +73,14 @@ public class AndroidTorPluginFactory extends TorPluginFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
TorPlugin createPluginInstance(Backoff backoff,
|
TorPlugin createPluginInstance(TorRendezvousCrypto torRendezvousCrypto,
|
||||||
TorRendezvousCrypto torRendezvousCrypto, PluginCallback callback,
|
PluginCallback callback,
|
||||||
String architecture) {
|
String architecture) {
|
||||||
return new AndroidTorPlugin(ioExecutor,
|
return new AndroidTorPlugin(ioExecutor,
|
||||||
wakefulIoExecutor, app, networkManager, locationUtils,
|
wakefulIoExecutor, app, networkManager, locationUtils,
|
||||||
torSocketFactory, clock, resourceProvider,
|
torSocketFactory, clock, resourceProvider,
|
||||||
circumventionProvider, batteryManager, wakeLockManager,
|
circumventionProvider, batteryManager, wakeLockManager,
|
||||||
backoff, torRendezvousCrypto, callback, architecture,
|
torRendezvousCrypto, callback, architecture,
|
||||||
MAX_LATENCY, MAX_IDLE_TIME, torDirectory, torSocksPort,
|
MAX_LATENCY, MAX_IDLE_TIME, torDirectory, torSocksPort,
|
||||||
torControlPort);
|
torControlPort);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,17 @@ public interface ConnectionManager {
|
|||||||
*/
|
*/
|
||||||
void manageIncomingConnection(TransportId t, TransportConnectionReader r);
|
void manageIncomingConnection(TransportId t, TransportConnectionReader r);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages an incoming connection from a contact via a mailbox.
|
||||||
|
* <p>
|
||||||
|
* This method does not mark the tag as recognised until after the data
|
||||||
|
* has been read from the {@link TransportConnectionReader}, at which
|
||||||
|
* point the {@link TagController} is called to decide whether the tag
|
||||||
|
* should be marked as recognised.
|
||||||
|
*/
|
||||||
|
void manageIncomingConnection(TransportId t, TransportConnectionReader r,
|
||||||
|
TagController c);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages an incoming connection from a contact over a duplex transport.
|
* Manages an incoming connection from a contact over a duplex transport.
|
||||||
*/
|
*/
|
||||||
@@ -46,4 +57,21 @@ public interface ConnectionManager {
|
|||||||
*/
|
*/
|
||||||
void manageOutgoingConnection(PendingContactId p, TransportId t,
|
void manageOutgoingConnection(PendingContactId p, TransportId t,
|
||||||
DuplexTransportConnection d);
|
DuplexTransportConnection d);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface for controlling whether a tag should be marked as
|
||||||
|
* recognised.
|
||||||
|
*/
|
||||||
|
interface TagController {
|
||||||
|
/**
|
||||||
|
* This method is only called if a tag was read from the corresponding
|
||||||
|
* {@link TransportConnectionReader} and recognised.
|
||||||
|
*
|
||||||
|
* @param exception True if an exception was thrown while reading from
|
||||||
|
* the {@link TransportConnectionReader}, after successfully reading
|
||||||
|
* and recognising the tag.
|
||||||
|
* @return True if the tag should be marked as recognised.
|
||||||
|
*/
|
||||||
|
boolean shouldMarkTagAsRecognised(boolean exception);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
package org.briarproject.bramble.api.mailbox;
|
package org.briarproject.bramble.api.mailbox;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static java.util.Collections.singletonList;
|
||||||
import static java.util.concurrent.TimeUnit.HOURS;
|
import static java.util.concurrent.TimeUnit.HOURS;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_FRAME_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.MAX_PAYLOAD_LENGTH;
|
||||||
@@ -8,6 +13,32 @@ import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENG
|
|||||||
|
|
||||||
public interface MailboxConstants {
|
public interface MailboxConstants {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The transport ID of the mailbox plugin.
|
||||||
|
*/
|
||||||
|
TransportId ID = new TransportId("org.briarproject.bramble.mailbox");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mailbox API versions that we support as a client. This is reported to our
|
||||||
|
* contacts by {@link MailboxUpdateManager}.
|
||||||
|
*/
|
||||||
|
List<MailboxVersion> CLIENT_SUPPORTS = singletonList(
|
||||||
|
new MailboxVersion(1, 0));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The constant returned by
|
||||||
|
* {@link MailboxHelper#getHighestCommonMajorVersion(List, List)}
|
||||||
|
* when the server is too old to support our major version.
|
||||||
|
*/
|
||||||
|
int API_SERVER_TOO_OLD = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The constant returned by
|
||||||
|
* {@link MailboxHelper#getHighestCommonMajorVersion(List, List)}
|
||||||
|
* when we as a client are too old to support the server's major version.
|
||||||
|
*/
|
||||||
|
int API_CLIENT_TOO_OLD = -2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum length of a file that can be uploaded to or downloaded from
|
* The maximum length of a file that can be uploaded to or downloaded from
|
||||||
* a mailbox.
|
* a mailbox.
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package org.briarproject.bramble.api.mailbox;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
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 {@link File directory} where the Mailbox plugin
|
||||||
|
* should store its state.
|
||||||
|
*/
|
||||||
|
@Qualifier
|
||||||
|
@Target({FIELD, METHOD, PARAMETER})
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
public @interface MailboxDirectory {
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package org.briarproject.bramble.api.mailbox;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxConstants.API_CLIENT_TOO_OLD;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxConstants.API_SERVER_TOO_OLD;
|
||||||
|
|
||||||
|
class MailboxHelper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the highest major version that both client and server support
|
||||||
|
* or {@link MailboxConstants#API_SERVER_TOO_OLD} if the server is too old
|
||||||
|
* or {@link MailboxConstants#API_CLIENT_TOO_OLD} if the client is too old.
|
||||||
|
*/
|
||||||
|
static int getHighestCommonMajorVersion(
|
||||||
|
List<MailboxVersion> client, List<MailboxVersion> server) {
|
||||||
|
TreeSet<Integer> clientVersions = new TreeSet<>();
|
||||||
|
for (MailboxVersion version : client) {
|
||||||
|
clientVersions.add(version.getMajor());
|
||||||
|
}
|
||||||
|
TreeSet<Integer> serverVersions = new TreeSet<>();
|
||||||
|
for (MailboxVersion version : server) {
|
||||||
|
serverVersions.add(version.getMajor());
|
||||||
|
}
|
||||||
|
for (int clientVersion : clientVersions.descendingSet()) {
|
||||||
|
if (serverVersions.contains(clientVersion)) return clientVersion;
|
||||||
|
}
|
||||||
|
if (clientVersions.last() < serverVersions.last()) {
|
||||||
|
return API_CLIENT_TOO_OLD;
|
||||||
|
}
|
||||||
|
return API_SERVER_TOO_OLD;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2,10 +2,14 @@ package org.briarproject.bramble.api.mailbox;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxConstants.CLIENT_SUPPORTS;
|
||||||
import static org.briarproject.bramble.api.mailbox.MailboxConstants.PROBLEM_MS_SINCE_LAST_SUCCESS;
|
import static org.briarproject.bramble.api.mailbox.MailboxConstants.PROBLEM_MS_SINCE_LAST_SUCCESS;
|
||||||
import static org.briarproject.bramble.api.mailbox.MailboxConstants.PROBLEM_NUM_CONNECTION_FAILURES;
|
import static org.briarproject.bramble.api.mailbox.MailboxConstants.PROBLEM_NUM_CONNECTION_FAILURES;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxHelper.getHighestCommonMajorVersion;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -13,12 +17,15 @@ public class MailboxStatus {
|
|||||||
|
|
||||||
private final long lastAttempt, lastSuccess;
|
private final long lastAttempt, lastSuccess;
|
||||||
private final int attemptsSinceSuccess;
|
private final int attemptsSinceSuccess;
|
||||||
|
private final List<MailboxVersion> serverSupports;
|
||||||
|
|
||||||
public MailboxStatus(long lastAttempt, long lastSuccess,
|
public MailboxStatus(long lastAttempt, long lastSuccess,
|
||||||
int attemptsSinceSuccess) {
|
int attemptsSinceSuccess,
|
||||||
|
List<MailboxVersion> serverSupports) {
|
||||||
this.lastAttempt = lastAttempt;
|
this.lastAttempt = lastAttempt;
|
||||||
this.lastSuccess = lastSuccess;
|
this.lastSuccess = lastSuccess;
|
||||||
this.attemptsSinceSuccess = attemptsSinceSuccess;
|
this.attemptsSinceSuccess = attemptsSinceSuccess;
|
||||||
|
this.serverSupports = serverSupports;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -67,4 +74,13 @@ public class MailboxStatus {
|
|||||||
return attemptsSinceSuccess >= PROBLEM_NUM_CONNECTION_FAILURES &&
|
return attemptsSinceSuccess >= PROBLEM_NUM_CONNECTION_FAILURES &&
|
||||||
(now - lastSuccess) >= PROBLEM_MS_SINCE_LAST_SUCCESS;
|
(now - lastSuccess) >= PROBLEM_MS_SINCE_LAST_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return a positive integer if the mailbox is compatible. Same result as
|
||||||
|
* {@link MailboxHelper#getHighestCommonMajorVersion(List, List)}.
|
||||||
|
*/
|
||||||
|
public int getMailboxCompatibility() {
|
||||||
|
return getHighestCommonMajorVersion(CLIENT_SUPPORTS, serverSupports);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package org.briarproject.bramble.api.plugin;
|
package org.briarproject.bramble.api.plugin;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
|
|
||||||
public interface BackoffFactory {
|
public interface BackoffFactory {
|
||||||
|
|
||||||
Backoff createBackoff(int minInterval, int maxInterval,
|
Backoff createBackoff(EventBus eventBus, TransportId transportId,
|
||||||
double base);
|
int minInterval, int maxInterval, double base);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,4 +56,9 @@ public interface PluginCallback extends ConnectionHandler {
|
|||||||
* This method can safely be called while holding a lock.
|
* This method can safely be called while holding a lock.
|
||||||
*/
|
*/
|
||||||
void pluginStateChanged(State state);
|
void pluginStateChanged(State state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Informs the callback that the plugin's polling interval has decreased.
|
||||||
|
*/
|
||||||
|
void pollingIntervalDecreased();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package org.briarproject.bramble.api.plugin.event;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.event.Event;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event that is broadcast when a plugin's polling interval decreases.
|
||||||
|
*/
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
public class PollingIntervalDecreasedEvent extends Event {
|
||||||
|
|
||||||
|
private final TransportId transportId;
|
||||||
|
|
||||||
|
public PollingIntervalDecreasedEvent(TransportId transportId) {
|
||||||
|
this.transportId = transportId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TransportId getTransportId() {
|
||||||
|
return transportId;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package org.briarproject.bramble.api.mailbox;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxConstants.API_CLIENT_TOO_OLD;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxConstants.API_SERVER_TOO_OLD;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxHelper.getHighestCommonMajorVersion;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class MailboxHelperTest {
|
||||||
|
|
||||||
|
private final Random random = new Random();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetHighestCommonMajorVersion() {
|
||||||
|
assertEquals(2, getHighestCommonMajorVersion(v(2), v(2)));
|
||||||
|
assertEquals(2, getHighestCommonMajorVersion(v(1, 2), v(2, 3, 4)));
|
||||||
|
assertEquals(2, getHighestCommonMajorVersion(v(2, 3, 4), v(2)));
|
||||||
|
assertEquals(2, getHighestCommonMajorVersion(v(2), v(2, 3, 4)));
|
||||||
|
|
||||||
|
assertEquals(API_CLIENT_TOO_OLD,
|
||||||
|
getHighestCommonMajorVersion(v(2), v(3, 4)));
|
||||||
|
assertEquals(API_CLIENT_TOO_OLD,
|
||||||
|
getHighestCommonMajorVersion(v(2), v(1, 3)));
|
||||||
|
assertEquals(API_SERVER_TOO_OLD,
|
||||||
|
getHighestCommonMajorVersion(v(3, 4, 5), v(2)));
|
||||||
|
assertEquals(API_SERVER_TOO_OLD,
|
||||||
|
getHighestCommonMajorVersion(v(1, 3), v(2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<MailboxVersion> v(int... ints) {
|
||||||
|
List<MailboxVersion> versions = new ArrayList<>(ints.length);
|
||||||
|
for (int v : ints) {
|
||||||
|
// minor versions should not matter
|
||||||
|
versions.add(new MailboxVersion(v, random.nextInt(42)));
|
||||||
|
}
|
||||||
|
return versions;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -40,6 +40,7 @@ import java.io.File;
|
|||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -50,6 +51,7 @@ import java.util.Random;
|
|||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES;
|
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES;
|
||||||
@@ -335,4 +337,13 @@ public class TestUtils {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isCryptoStrengthUnlimited() {
|
||||||
|
try {
|
||||||
|
return Cipher.getMaxAllowedKeyLength("AES/CBC/PKCS5Padding")
|
||||||
|
== Integer.MAX_VALUE;
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ abstract class Connection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] readTag(InputStream in) throws IOException {
|
byte[] readTag(InputStream in) throws IOException {
|
||||||
byte[] tag = new byte[TAG_LENGTH];
|
byte[] tag = new byte[TAG_LENGTH];
|
||||||
read(in, tag);
|
read(in, tag);
|
||||||
return tag;
|
return tag;
|
||||||
|
|||||||
@@ -67,7 +67,15 @@ class ConnectionManagerImpl implements ConnectionManager {
|
|||||||
TransportConnectionReader r) {
|
TransportConnectionReader r) {
|
||||||
ioExecutor.execute(new IncomingSimplexSyncConnection(keyManager,
|
ioExecutor.execute(new IncomingSimplexSyncConnection(keyManager,
|
||||||
connectionRegistry, streamReaderFactory, streamWriterFactory,
|
connectionRegistry, streamReaderFactory, streamWriterFactory,
|
||||||
syncSessionFactory, transportPropertyManager, t, r));
|
syncSessionFactory, transportPropertyManager, t, r, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void manageIncomingConnection(TransportId t,
|
||||||
|
TransportConnectionReader r, TagController c) {
|
||||||
|
ioExecutor.execute(new IncomingSimplexSyncConnection(keyManager,
|
||||||
|
connectionRegistry, streamReaderFactory, streamWriterFactory,
|
||||||
|
syncSessionFactory, transportPropertyManager, t, r, c));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package org.briarproject.bramble.connection;
|
package org.briarproject.bramble.connection;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.connection.ConnectionManager.TagController;
|
||||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
@@ -15,6 +17,8 @@ import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
|
||||||
@@ -23,6 +27,8 @@ class IncomingSimplexSyncConnection extends SyncConnection implements Runnable {
|
|||||||
|
|
||||||
private final TransportId transportId;
|
private final TransportId transportId;
|
||||||
private final TransportConnectionReader reader;
|
private final TransportConnectionReader reader;
|
||||||
|
@Nullable
|
||||||
|
private final TagController tagController;
|
||||||
|
|
||||||
IncomingSimplexSyncConnection(KeyManager keyManager,
|
IncomingSimplexSyncConnection(KeyManager keyManager,
|
||||||
ConnectionRegistry connectionRegistry,
|
ConnectionRegistry connectionRegistry,
|
||||||
@@ -30,33 +36,50 @@ class IncomingSimplexSyncConnection extends SyncConnection implements Runnable {
|
|||||||
StreamWriterFactory streamWriterFactory,
|
StreamWriterFactory streamWriterFactory,
|
||||||
SyncSessionFactory syncSessionFactory,
|
SyncSessionFactory syncSessionFactory,
|
||||||
TransportPropertyManager transportPropertyManager,
|
TransportPropertyManager transportPropertyManager,
|
||||||
TransportId transportId, TransportConnectionReader reader) {
|
TransportId transportId,
|
||||||
|
TransportConnectionReader reader,
|
||||||
|
@Nullable TagController tagController) {
|
||||||
super(keyManager, connectionRegistry, streamReaderFactory,
|
super(keyManager, connectionRegistry, streamReaderFactory,
|
||||||
streamWriterFactory, syncSessionFactory,
|
streamWriterFactory, syncSessionFactory,
|
||||||
transportPropertyManager);
|
transportPropertyManager);
|
||||||
this.transportId = transportId;
|
this.transportId = transportId;
|
||||||
this.reader = reader;
|
this.reader = reader;
|
||||||
|
this.tagController = tagController;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
// Read and recognise the tag
|
// Read and recognise the tag
|
||||||
StreamContext ctx = recogniseTag(reader, transportId);
|
byte[] tag;
|
||||||
|
StreamContext ctx;
|
||||||
|
try {
|
||||||
|
tag = readTag(reader.getInputStream());
|
||||||
|
// If we have a tag controller, defer marking the tag as recognised
|
||||||
|
if (tagController == null) {
|
||||||
|
ctx = keyManager.getStreamContext(transportId, tag);
|
||||||
|
} else {
|
||||||
|
ctx = keyManager.getStreamContextOnly(transportId, tag);
|
||||||
|
}
|
||||||
|
} catch (IOException | DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (ctx == null) {
|
if (ctx == null) {
|
||||||
LOG.info("Unrecognised tag");
|
LOG.info("Unrecognised tag");
|
||||||
onError(false);
|
onError();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ContactId contactId = ctx.getContactId();
|
ContactId contactId = ctx.getContactId();
|
||||||
if (contactId == null) {
|
if (contactId == null) {
|
||||||
LOG.warning("Received rendezvous stream, expected contact");
|
LOG.warning("Received rendezvous stream, expected contact");
|
||||||
onError(true);
|
onError(tag);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (ctx.isHandshakeMode()) {
|
if (ctx.isHandshakeMode()) {
|
||||||
// TODO: Support handshake mode for contacts
|
// TODO: Support handshake mode for contacts
|
||||||
LOG.warning("Received handshake tag, expected rotation mode");
|
LOG.warning("Received handshake tag, expected rotation mode");
|
||||||
onError(true);
|
onError(tag);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -65,15 +88,33 @@ class IncomingSimplexSyncConnection extends SyncConnection implements Runnable {
|
|||||||
LOG.info("Ignoring priority for simplex connection");
|
LOG.info("Ignoring priority for simplex connection");
|
||||||
// Create and run the incoming session
|
// Create and run the incoming session
|
||||||
createIncomingSession(ctx, reader, handler).run();
|
createIncomingSession(ctx, reader, handler).run();
|
||||||
|
// Success
|
||||||
|
markTagAsRecognisedIfRequired(false, tag);
|
||||||
reader.dispose(false, true);
|
reader.dispose(false, true);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
onError(true);
|
onError(tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onError(boolean recognised) {
|
private void onError() {
|
||||||
disposeOnError(reader, recognised);
|
disposeOnError(reader, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onError(byte[] tag) {
|
||||||
|
markTagAsRecognisedIfRequired(true, tag);
|
||||||
|
disposeOnError(reader, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void markTagAsRecognisedIfRequired(boolean exception, byte[] tag) {
|
||||||
|
if (tagController != null &&
|
||||||
|
tagController.shouldMarkTagAsRecognised(exception)) {
|
||||||
|
try {
|
||||||
|
keyManager.markTagAsRecognised(transportId, tag);
|
||||||
|
} catch (DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -190,6 +190,10 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
if (state == STOPPING) {
|
||||||
|
LOG.info("Already stopped");
|
||||||
|
return;
|
||||||
|
}
|
||||||
LOG.info("Stopping services");
|
LOG.info("Stopping services");
|
||||||
state = STOPPING;
|
state = STOPPING;
|
||||||
eventBus.broadcast(new LifecycleEvent(STOPPING));
|
eventBus.broadcast(new LifecycleEvent(STOPPING));
|
||||||
|
|||||||
@@ -22,10 +22,21 @@ interface ConnectivityChecker {
|
|||||||
* the check succeeds. If a check is already running then the observer is
|
* the check succeeds. If a check is already running then the observer is
|
||||||
* called when the check succeeds. If a connectivity check has recently
|
* called when the check succeeds. If a connectivity check has recently
|
||||||
* succeeded then the observer is called immediately.
|
* succeeded then the observer is called immediately.
|
||||||
|
* <p>
|
||||||
|
* Observers are removed after being called, or when the checker is
|
||||||
|
* {@link #destroy() destroyed}.
|
||||||
*/
|
*/
|
||||||
void checkConnectivity(MailboxProperties properties,
|
void checkConnectivity(MailboxProperties properties,
|
||||||
ConnectivityObserver o);
|
ConnectivityObserver o);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an observer that was added via
|
||||||
|
* {@link #checkConnectivity(MailboxProperties, ConnectivityObserver)}. If
|
||||||
|
* there are no remaining observers and a connectivity check is running
|
||||||
|
* then the check will be cancelled.
|
||||||
|
*/
|
||||||
|
void removeObserver(ConnectivityObserver o);
|
||||||
|
|
||||||
interface ConnectivityObserver {
|
interface ConnectivityObserver {
|
||||||
void onConnectivityCheckSucceeded();
|
void onConnectivityCheckSucceeded();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,8 +80,7 @@ abstract class ConnectivityCheckerImpl implements ConnectivityChecker {
|
|||||||
> CONNECTIVITY_CHECK_FRESHNESS_MS) {
|
> CONNECTIVITY_CHECK_FRESHNESS_MS) {
|
||||||
// The last connectivity check is stale, start a new one
|
// The last connectivity check is stale, start a new one
|
||||||
connectivityObservers.add(o);
|
connectivityObservers.add(o);
|
||||||
ApiCall task =
|
ApiCall task = createConnectivityCheckTask(properties);
|
||||||
createConnectivityCheckTask(properties);
|
|
||||||
connectivityCheck = mailboxApiCaller.retryWithBackoff(task);
|
connectivityCheck = mailboxApiCaller.retryWithBackoff(task);
|
||||||
} else {
|
} else {
|
||||||
// The last connectivity check is fresh
|
// The last connectivity check is fresh
|
||||||
@@ -108,4 +107,16 @@ abstract class ConnectivityCheckerImpl implements ConnectivityChecker {
|
|||||||
o.onConnectivityCheckSucceeded();
|
o.onConnectivityCheckSucceeded();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeObserver(ConnectivityObserver o) {
|
||||||
|
synchronized (lock) {
|
||||||
|
if (destroyed) return;
|
||||||
|
connectivityObservers.remove(o);
|
||||||
|
if (connectivityObservers.isEmpty() && connectivityCheck != null) {
|
||||||
|
connectivityCheck.cancel();
|
||||||
|
connectivityCheck = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.mailbox.MailboxApi.ApiException;
|
import org.briarproject.bramble.mailbox.MailboxApi.ApiException;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
@@ -24,16 +22,11 @@ class ContactMailboxConnectivityChecker extends ConnectivityCheckerImpl {
|
|||||||
@Override
|
@Override
|
||||||
ApiCall createConnectivityCheckTask(MailboxProperties properties) {
|
ApiCall createConnectivityCheckTask(MailboxProperties properties) {
|
||||||
if (properties.isOwner()) throw new IllegalArgumentException();
|
if (properties.isOwner()) throw new IllegalArgumentException();
|
||||||
return new SimpleApiCall() {
|
return new SimpleApiCall(() -> {
|
||||||
@Override
|
if (!mailboxApi.checkStatus(properties)) throw new ApiException();
|
||||||
void tryToCallApi() throws IOException, ApiException {
|
// Call the observers and cache the result
|
||||||
if (!mailboxApi.checkStatus(properties)) {
|
onConnectivityCheckSucceeded(clock.currentTimeMillis());
|
||||||
throw new ApiException();
|
});
|
||||||
}
|
|
||||||
// Call the observers and cache the result
|
|
||||||
onConnectivityCheckSucceeded(clock.currentTimeMillis());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,241 @@
|
|||||||
|
package org.briarproject.bramble.mailbox;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.Cancellable;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.mailbox.ConnectivityChecker.ConnectivityObserver;
|
||||||
|
import org.briarproject.bramble.mailbox.MailboxApi.ApiException;
|
||||||
|
import org.briarproject.bramble.mailbox.MailboxApi.MailboxFile;
|
||||||
|
import org.briarproject.bramble.mailbox.MailboxApi.TolerableFailureException;
|
||||||
|
import org.briarproject.bramble.mailbox.TorReachabilityMonitor.TorReachabilityObserver;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.annotation.concurrent.GuardedBy;
|
||||||
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.INFO;
|
||||||
|
import static java.util.logging.Logger.getLogger;
|
||||||
|
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
||||||
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
|
||||||
|
@ThreadSafe
|
||||||
|
@NotNullByDefault
|
||||||
|
class ContactMailboxDownloadWorker implements MailboxWorker,
|
||||||
|
ConnectivityObserver, TorReachabilityObserver {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When the worker is started it waits for a connectivity check, then
|
||||||
|
* starts its first download cycle: checking the inbox, downloading and
|
||||||
|
* deleting any files, and checking again until the inbox is empty.
|
||||||
|
* <p>
|
||||||
|
* The worker then waits for our Tor hidden service to be reachable before
|
||||||
|
* starting its second download cycle. This ensures that if a contact
|
||||||
|
* tried and failed to connect to our hidden service before it was
|
||||||
|
* reachable, and therefore uploaded a file to the mailbox instead, we'll
|
||||||
|
* find the file in the second download cycle.
|
||||||
|
*/
|
||||||
|
private enum State {
|
||||||
|
CREATED,
|
||||||
|
CONNECTIVITY_CHECK,
|
||||||
|
DOWNLOAD_CYCLE_1,
|
||||||
|
WAITING_FOR_TOR,
|
||||||
|
DOWNLOAD_CYCLE_2,
|
||||||
|
FINISHED,
|
||||||
|
DESTROYED
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
getLogger(ContactMailboxDownloadWorker.class.getName());
|
||||||
|
|
||||||
|
private final ConnectivityChecker connectivityChecker;
|
||||||
|
private final TorReachabilityMonitor torReachabilityMonitor;
|
||||||
|
private final MailboxApiCaller mailboxApiCaller;
|
||||||
|
private final MailboxApi mailboxApi;
|
||||||
|
private final MailboxFileManager mailboxFileManager;
|
||||||
|
private final MailboxProperties mailboxProperties;
|
||||||
|
private final Object lock = new Object();
|
||||||
|
|
||||||
|
@GuardedBy("lock")
|
||||||
|
private State state = State.CREATED;
|
||||||
|
|
||||||
|
@GuardedBy("lock")
|
||||||
|
@Nullable
|
||||||
|
private Cancellable apiCall = null;
|
||||||
|
|
||||||
|
ContactMailboxDownloadWorker(
|
||||||
|
ConnectivityChecker connectivityChecker,
|
||||||
|
TorReachabilityMonitor torReachabilityMonitor,
|
||||||
|
MailboxApiCaller mailboxApiCaller,
|
||||||
|
MailboxApi mailboxApi,
|
||||||
|
MailboxFileManager mailboxFileManager,
|
||||||
|
MailboxProperties mailboxProperties) {
|
||||||
|
if (mailboxProperties.isOwner()) throw new IllegalArgumentException();
|
||||||
|
this.connectivityChecker = connectivityChecker;
|
||||||
|
this.torReachabilityMonitor = torReachabilityMonitor;
|
||||||
|
this.mailboxApiCaller = mailboxApiCaller;
|
||||||
|
this.mailboxApi = mailboxApi;
|
||||||
|
this.mailboxFileManager = mailboxFileManager;
|
||||||
|
this.mailboxProperties = mailboxProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start() {
|
||||||
|
LOG.info("Started");
|
||||||
|
synchronized (lock) {
|
||||||
|
// Don't allow the worker to be reused
|
||||||
|
if (state != State.CREATED) throw new IllegalStateException();
|
||||||
|
state = State.CONNECTIVITY_CHECK;
|
||||||
|
}
|
||||||
|
// Avoid leaking observer in case destroy() is called concurrently
|
||||||
|
// before observer is added
|
||||||
|
connectivityChecker.checkConnectivity(mailboxProperties, this);
|
||||||
|
boolean destroyed;
|
||||||
|
synchronized (lock) {
|
||||||
|
destroyed = state == State.DESTROYED;
|
||||||
|
}
|
||||||
|
if (destroyed) connectivityChecker.removeObserver(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() {
|
||||||
|
LOG.info("Destroyed");
|
||||||
|
Cancellable apiCall;
|
||||||
|
synchronized (lock) {
|
||||||
|
state = State.DESTROYED;
|
||||||
|
apiCall = this.apiCall;
|
||||||
|
this.apiCall = null;
|
||||||
|
}
|
||||||
|
if (apiCall != null) apiCall.cancel();
|
||||||
|
connectivityChecker.removeObserver(this);
|
||||||
|
torReachabilityMonitor.removeObserver(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConnectivityCheckSucceeded() {
|
||||||
|
LOG.info("Connectivity check succeeded");
|
||||||
|
synchronized (lock) {
|
||||||
|
if (state != State.CONNECTIVITY_CHECK) return;
|
||||||
|
state = State.DOWNLOAD_CYCLE_1;
|
||||||
|
// Start first download cycle
|
||||||
|
apiCall = mailboxApiCaller.retryWithBackoff(
|
||||||
|
new SimpleApiCall(this::apiCallListInbox));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void apiCallListInbox() throws IOException, ApiException {
|
||||||
|
synchronized (lock) {
|
||||||
|
if (state == State.DESTROYED) return;
|
||||||
|
}
|
||||||
|
LOG.info("Listing inbox");
|
||||||
|
List<MailboxFile> files = mailboxApi.getFiles(mailboxProperties,
|
||||||
|
requireNonNull(mailboxProperties.getInboxId()));
|
||||||
|
if (files.isEmpty()) onDownloadCycleFinished();
|
||||||
|
else downloadNextFile(new LinkedList<>(files));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onDownloadCycleFinished() {
|
||||||
|
boolean addObserver = false;
|
||||||
|
synchronized (lock) {
|
||||||
|
if (state == State.DOWNLOAD_CYCLE_1) {
|
||||||
|
LOG.info("First download cycle finished");
|
||||||
|
state = State.WAITING_FOR_TOR;
|
||||||
|
addObserver = true;
|
||||||
|
} else if (state == State.DOWNLOAD_CYCLE_2) {
|
||||||
|
LOG.info("Second download cycle finished");
|
||||||
|
state = State.FINISHED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (addObserver) {
|
||||||
|
// Avoid leaking observer in case destroy() is called concurrently
|
||||||
|
// before observer is added
|
||||||
|
torReachabilityMonitor.addOneShotObserver(this);
|
||||||
|
boolean destroyed;
|
||||||
|
synchronized (lock) {
|
||||||
|
destroyed = state == State.DESTROYED;
|
||||||
|
}
|
||||||
|
if (destroyed) torReachabilityMonitor.removeObserver(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void downloadNextFile(Queue<MailboxFile> queue) {
|
||||||
|
synchronized (lock) {
|
||||||
|
if (state == State.DESTROYED) return;
|
||||||
|
MailboxFile file = queue.remove();
|
||||||
|
apiCall = mailboxApiCaller.retryWithBackoff(
|
||||||
|
new SimpleApiCall(() -> apiCallDownloadFile(file, queue)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void apiCallDownloadFile(MailboxFile file,
|
||||||
|
Queue<MailboxFile> queue) throws IOException, ApiException {
|
||||||
|
synchronized (lock) {
|
||||||
|
if (state == State.DESTROYED) return;
|
||||||
|
}
|
||||||
|
LOG.info("Downloading file");
|
||||||
|
File tempFile = mailboxFileManager.createTempFileForDownload();
|
||||||
|
try {
|
||||||
|
mailboxApi.getFile(mailboxProperties,
|
||||||
|
requireNonNull(mailboxProperties.getInboxId()),
|
||||||
|
file.name, tempFile);
|
||||||
|
} catch (IOException | ApiException e) {
|
||||||
|
if (!tempFile.delete()) {
|
||||||
|
LOG.warning("Failed to delete temporary file");
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
mailboxFileManager.handleDownloadedFile(tempFile);
|
||||||
|
deleteFile(file, queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteFile(MailboxFile file, Queue<MailboxFile> queue) {
|
||||||
|
synchronized (lock) {
|
||||||
|
if (state == State.DESTROYED) return;
|
||||||
|
apiCall = mailboxApiCaller.retryWithBackoff(
|
||||||
|
new SimpleApiCall(() -> apiCallDeleteFile(file, queue)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void apiCallDeleteFile(MailboxFile file, Queue<MailboxFile> queue)
|
||||||
|
throws IOException, ApiException {
|
||||||
|
synchronized (lock) {
|
||||||
|
if (state == State.DESTROYED) return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
mailboxApi.deleteFile(mailboxProperties,
|
||||||
|
requireNonNull(mailboxProperties.getInboxId()), file.name);
|
||||||
|
} catch (TolerableFailureException e) {
|
||||||
|
// Catch this so we can continue to the next file
|
||||||
|
logException(LOG, INFO, e);
|
||||||
|
}
|
||||||
|
if (queue.isEmpty()) {
|
||||||
|
// List the inbox again to check for files that may have arrived
|
||||||
|
// while we were downloading
|
||||||
|
synchronized (lock) {
|
||||||
|
if (state == State.DESTROYED) return;
|
||||||
|
apiCall = mailboxApiCaller.retryWithBackoff(
|
||||||
|
new SimpleApiCall(this::apiCallListInbox));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
downloadNextFile(queue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTorReachable() {
|
||||||
|
LOG.info("Our Tor hidden service is reachable");
|
||||||
|
synchronized (lock) {
|
||||||
|
if (state != State.WAITING_FOR_TOR) return;
|
||||||
|
state = State.DOWNLOAD_CYCLE_2;
|
||||||
|
// Start second download cycle
|
||||||
|
apiCall = mailboxApiCaller.retryWithBackoff(
|
||||||
|
new SimpleApiCall(this::apiCallListInbox));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,7 +7,6 @@ import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
|
|||||||
import org.briarproject.bramble.api.mailbox.MailboxFileId;
|
import org.briarproject.bramble.api.mailbox.MailboxFileId;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxFolderId;
|
import org.briarproject.bramble.api.mailbox.MailboxFolderId;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxUpdateManager;
|
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxVersion;
|
import org.briarproject.bramble.api.mailbox.MailboxVersion;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
@@ -19,18 +18,9 @@ import java.util.List;
|
|||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
import static java.util.Collections.singletonList;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
interface MailboxApi {
|
interface MailboxApi {
|
||||||
|
|
||||||
/**
|
|
||||||
* Mailbox API versions that we support as a client. This is reported to our
|
|
||||||
* contacts by {@link MailboxUpdateManager}.
|
|
||||||
*/
|
|
||||||
List<MailboxVersion> CLIENT_SUPPORTS = singletonList(
|
|
||||||
new MailboxVersion(1, 0));
|
|
||||||
|
|
||||||
List<MailboxVersion> getServerSupports(MailboxProperties properties)
|
List<MailboxVersion> getServerSupports(MailboxProperties properties)
|
||||||
throws IOException, ApiException;
|
throws IOException, ApiException;
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ interface MailboxApiCaller {
|
|||||||
* Asynchronously calls the given API call on the {@link IoExecutor},
|
* Asynchronously calls the given API call on the {@link IoExecutor},
|
||||||
* automatically retrying at increasing intervals until the API call
|
* automatically retrying at increasing intervals until the API call
|
||||||
* returns false or retries are cancelled.
|
* returns false or retries are cancelled.
|
||||||
|
* <p>
|
||||||
|
* This method is safe to call while holding a lock.
|
||||||
*
|
*
|
||||||
* @return A {@link Cancellable} that can be used to cancel any future
|
* @return A {@link Cancellable} that can be used to cancel any future
|
||||||
* retries.
|
* retries.
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package org.briarproject.bramble.mailbox;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
|
||||||
|
@ThreadSafe
|
||||||
|
@NotNullByDefault
|
||||||
|
interface MailboxFileManager {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an empty file for storing a download.
|
||||||
|
*/
|
||||||
|
File createTempFileForDownload() throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles a file that has been downloaded. The file should be created
|
||||||
|
* with {@link #createTempFileForDownload()}.
|
||||||
|
*/
|
||||||
|
void handleDownloadedFile(File f);
|
||||||
|
}
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
package org.briarproject.bramble.mailbox;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.connection.ConnectionManager;
|
||||||
|
import org.briarproject.bramble.api.event.Event;
|
||||||
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
|
import org.briarproject.bramble.api.event.EventListener;
|
||||||
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxDirectory;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.PluginManager;
|
||||||
|
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.TransportActiveEvent;
|
||||||
|
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
|
||||||
|
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.WARNING;
|
||||||
|
import static java.util.logging.Logger.getLogger;
|
||||||
|
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.RUNNING;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxConstants.ID;
|
||||||
|
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
||||||
|
import static org.briarproject.bramble.api.plugin.file.FileConstants.PROP_PATH;
|
||||||
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
|
||||||
|
@ThreadSafe
|
||||||
|
@NotNullByDefault
|
||||||
|
class MailboxFileManagerImpl implements MailboxFileManager, EventListener {
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
getLogger(MailboxFileManagerImpl.class.getName());
|
||||||
|
|
||||||
|
// Package access for testing
|
||||||
|
static final String DOWNLOAD_DIR_NAME = "downloads";
|
||||||
|
|
||||||
|
private final Executor ioExecutor;
|
||||||
|
private final PluginManager pluginManager;
|
||||||
|
private final ConnectionManager connectionManager;
|
||||||
|
private final LifecycleManager lifecycleManager;
|
||||||
|
private final File mailboxDir;
|
||||||
|
private final EventBus eventBus;
|
||||||
|
private final CountDownLatch orphanLatch = new CountDownLatch(1);
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
MailboxFileManagerImpl(@IoExecutor Executor ioExecutor,
|
||||||
|
PluginManager pluginManager,
|
||||||
|
ConnectionManager connectionManager,
|
||||||
|
LifecycleManager lifecycleManager,
|
||||||
|
@MailboxDirectory File mailboxDir,
|
||||||
|
EventBus eventBus) {
|
||||||
|
this.ioExecutor = ioExecutor;
|
||||||
|
this.pluginManager = pluginManager;
|
||||||
|
this.connectionManager = connectionManager;
|
||||||
|
this.lifecycleManager = lifecycleManager;
|
||||||
|
this.mailboxDir = mailboxDir;
|
||||||
|
this.eventBus = eventBus;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public File createTempFileForDownload() throws IOException {
|
||||||
|
// Wait for orphaned files to be handled before creating new files
|
||||||
|
try {
|
||||||
|
orphanLatch.await();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
File downloadDir = createDirectoryIfNeeded(DOWNLOAD_DIR_NAME);
|
||||||
|
return File.createTempFile("mailbox", ".tmp", downloadDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
private File createDirectoryIfNeeded(String name) throws IOException {
|
||||||
|
File dir = new File(mailboxDir, name);
|
||||||
|
//noinspection ResultOfMethodCallIgnored
|
||||||
|
dir.mkdirs();
|
||||||
|
if (!dir.isDirectory()) {
|
||||||
|
throw new IOException("Failed to create directory '" + name + "'");
|
||||||
|
}
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleDownloadedFile(File f) {
|
||||||
|
// We shouldn't reach this point until the plugin has been started
|
||||||
|
SimplexPlugin plugin =
|
||||||
|
(SimplexPlugin) requireNonNull(pluginManager.getPlugin(ID));
|
||||||
|
TransportProperties p = new TransportProperties();
|
||||||
|
p.put(PROP_PATH, f.getAbsolutePath());
|
||||||
|
TransportConnectionReader reader = plugin.createReader(p);
|
||||||
|
if (reader == null) {
|
||||||
|
LOG.warning("Failed to create reader for downloaded file");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TransportConnectionReader decorated = new MailboxFileReader(reader, f);
|
||||||
|
LOG.info("Reading downloaded file");
|
||||||
|
connectionManager.manageIncomingConnection(ID, decorated,
|
||||||
|
exception -> isHandlingComplete(exception, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isHandlingComplete(boolean exception, boolean recognised) {
|
||||||
|
// If we've successfully read the file then we're done
|
||||||
|
if (!exception && recognised) return true;
|
||||||
|
// If the app is shutting down we may get spurious IO exceptions
|
||||||
|
// due to executors being shut down. Leave the file in the download
|
||||||
|
// directory and we'll try to read it again at the next startup
|
||||||
|
return !lifecycleManager.getLifecycleState().isAfter(RUNNING);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void eventOccurred(Event e) {
|
||||||
|
if (e instanceof TransportActiveEvent) {
|
||||||
|
TransportActiveEvent t = (TransportActiveEvent) e;
|
||||||
|
if (t.getTransportId().equals(ID)) {
|
||||||
|
ioExecutor.execute(this::handleOrphanedFiles);
|
||||||
|
eventBus.removeListener(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called at startup, as soon as the plugin is started, to
|
||||||
|
* handle any files that were left in the download directory at the last
|
||||||
|
* shutdown.
|
||||||
|
*/
|
||||||
|
@IoExecutor
|
||||||
|
private void handleOrphanedFiles() {
|
||||||
|
try {
|
||||||
|
File downloadDir = createDirectoryIfNeeded(DOWNLOAD_DIR_NAME);
|
||||||
|
File[] orphans = downloadDir.listFiles();
|
||||||
|
// Now that we've got the list of orphans, new files can be created
|
||||||
|
orphanLatch.countDown();
|
||||||
|
if (orphans != null) for (File f : orphans) handleDownloadedFile(f);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MailboxFileReader implements TransportConnectionReader {
|
||||||
|
|
||||||
|
private final TransportConnectionReader delegate;
|
||||||
|
private final File file;
|
||||||
|
|
||||||
|
private MailboxFileReader(TransportConnectionReader delegate,
|
||||||
|
File file) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
this.file = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getInputStream() throws IOException {
|
||||||
|
return delegate.getInputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose(boolean exception, boolean recognised)
|
||||||
|
throws IOException {
|
||||||
|
delegate.dispose(exception, recognised);
|
||||||
|
if (isHandlingComplete(exception, recognised)) {
|
||||||
|
LOG.info("Deleting downloaded file");
|
||||||
|
if (!file.delete()) {
|
||||||
|
LOG.warning("Failed to delete downloaded file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import org.briarproject.bramble.api.FeatureFlags;
|
|||||||
import org.briarproject.bramble.api.client.ClientHelper;
|
import org.briarproject.bramble.api.client.ClientHelper;
|
||||||
import org.briarproject.bramble.api.contact.ContactManager;
|
import org.briarproject.bramble.api.contact.ContactManager;
|
||||||
import org.briarproject.bramble.api.data.MetadataEncoder;
|
import org.briarproject.bramble.api.data.MetadataEncoder;
|
||||||
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxManager;
|
import org.briarproject.bramble.api.mailbox.MailboxManager;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
||||||
@@ -21,10 +22,10 @@ import javax.inject.Singleton;
|
|||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxConstants.CLIENT_SUPPORTS;
|
||||||
import static org.briarproject.bramble.api.mailbox.MailboxUpdateManager.CLIENT_ID;
|
import static org.briarproject.bramble.api.mailbox.MailboxUpdateManager.CLIENT_ID;
|
||||||
import static org.briarproject.bramble.api.mailbox.MailboxUpdateManager.MAJOR_VERSION;
|
import static org.briarproject.bramble.api.mailbox.MailboxUpdateManager.MAJOR_VERSION;
|
||||||
import static org.briarproject.bramble.api.mailbox.MailboxUpdateManager.MINOR_VERSION;
|
import static org.briarproject.bramble.api.mailbox.MailboxUpdateManager.MINOR_VERSION;
|
||||||
import static org.briarproject.bramble.mailbox.MailboxApi.CLIENT_SUPPORTS;
|
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
public class MailboxModule {
|
public class MailboxModule {
|
||||||
@@ -34,6 +35,8 @@ public class MailboxModule {
|
|||||||
MailboxUpdateValidator mailboxUpdateValidator;
|
MailboxUpdateValidator mailboxUpdateValidator;
|
||||||
@Inject
|
@Inject
|
||||||
MailboxUpdateManager mailboxUpdateManager;
|
MailboxUpdateManager mailboxUpdateManager;
|
||||||
|
@Inject
|
||||||
|
MailboxFileManager mailboxFileManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@@ -101,4 +104,14 @@ public class MailboxModule {
|
|||||||
}
|
}
|
||||||
return mailboxUpdateManager;
|
return mailboxUpdateManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
MailboxFileManager provideMailboxFileManager(FeatureFlags featureFlags,
|
||||||
|
EventBus eventBus, MailboxFileManagerImpl mailboxFileManager) {
|
||||||
|
if (featureFlags.shouldEnableMailbox()) {
|
||||||
|
eventBus.addListener(mailboxFileManager);
|
||||||
|
}
|
||||||
|
return mailboxFileManager;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import javax.annotation.Nullable;
|
|||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static java.util.Collections.emptyList;
|
||||||
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@@ -60,15 +61,7 @@ class MailboxSettingsManagerImpl implements MailboxSettingsManager {
|
|||||||
String onion = s.get(SETTINGS_KEY_ONION);
|
String onion = s.get(SETTINGS_KEY_ONION);
|
||||||
String token = s.get(SETTINGS_KEY_TOKEN);
|
String token = s.get(SETTINGS_KEY_TOKEN);
|
||||||
if (isNullOrEmpty(onion) || isNullOrEmpty(token)) return null;
|
if (isNullOrEmpty(onion) || isNullOrEmpty(token)) return null;
|
||||||
int[] ints = s.getIntArray(SETTINGS_KEY_SERVER_SUPPORTS);
|
List<MailboxVersion> serverSupports = parseServerSupports(s);
|
||||||
// We know we were paired, so we must have proper serverSupports
|
|
||||||
if (ints == null || ints.length == 0 || ints.length % 2 != 0) {
|
|
||||||
throw new DbException();
|
|
||||||
}
|
|
||||||
List<MailboxVersion> serverSupports = new ArrayList<>();
|
|
||||||
for (int i = 0; i < ints.length - 1; i += 2) {
|
|
||||||
serverSupports.add(new MailboxVersion(ints[i], ints[i + 1]));
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
MailboxAuthToken tokenId = MailboxAuthToken.fromString(token);
|
MailboxAuthToken tokenId = MailboxAuthToken.fromString(token);
|
||||||
return new MailboxProperties(onion, tokenId, serverSupports);
|
return new MailboxProperties(onion, tokenId, serverSupports);
|
||||||
@@ -115,18 +108,28 @@ class MailboxSettingsManagerImpl implements MailboxSettingsManager {
|
|||||||
long lastAttempt = s.getLong(SETTINGS_KEY_LAST_ATTEMPT, -1);
|
long lastAttempt = s.getLong(SETTINGS_KEY_LAST_ATTEMPT, -1);
|
||||||
long lastSuccess = s.getLong(SETTINGS_KEY_LAST_SUCCESS, -1);
|
long lastSuccess = s.getLong(SETTINGS_KEY_LAST_SUCCESS, -1);
|
||||||
int attempts = s.getInt(SETTINGS_KEY_ATTEMPTS, 0);
|
int attempts = s.getInt(SETTINGS_KEY_ATTEMPTS, 0);
|
||||||
return new MailboxStatus(lastAttempt, lastSuccess, attempts);
|
List<MailboxVersion> serverSupports;
|
||||||
|
try {
|
||||||
|
serverSupports = parseServerSupports(s);
|
||||||
|
} catch (DbException e) {
|
||||||
|
serverSupports = emptyList();
|
||||||
|
}
|
||||||
|
return new MailboxStatus(lastAttempt, lastSuccess, attempts,
|
||||||
|
serverSupports);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void recordSuccessfulConnection(Transaction txn, long now)
|
public void recordSuccessfulConnection(Transaction txn, long now)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
|
Settings oldSettings =
|
||||||
|
settingsManager.getSettings(txn, SETTINGS_NAMESPACE);
|
||||||
Settings s = new Settings();
|
Settings s = new Settings();
|
||||||
s.putLong(SETTINGS_KEY_LAST_ATTEMPT, now);
|
s.putLong(SETTINGS_KEY_LAST_ATTEMPT, now);
|
||||||
s.putLong(SETTINGS_KEY_LAST_SUCCESS, now);
|
s.putLong(SETTINGS_KEY_LAST_SUCCESS, now);
|
||||||
s.putInt(SETTINGS_KEY_ATTEMPTS, 0);
|
s.putInt(SETTINGS_KEY_ATTEMPTS, 0);
|
||||||
settingsManager.mergeSettings(txn, s, SETTINGS_NAMESPACE);
|
settingsManager.mergeSettings(txn, s, SETTINGS_NAMESPACE);
|
||||||
MailboxStatus status = new MailboxStatus(now, now, 0);
|
List<MailboxVersion> serverSupports = parseServerSupports(oldSettings);
|
||||||
|
MailboxStatus status = new MailboxStatus(now, now, 0, serverSupports);
|
||||||
txn.attach(new OwnMailboxConnectionStatusEvent(status));
|
txn.attach(new OwnMailboxConnectionStatusEvent(status));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,7 +144,9 @@ class MailboxSettingsManagerImpl implements MailboxSettingsManager {
|
|||||||
newSettings.putLong(SETTINGS_KEY_LAST_ATTEMPT, now);
|
newSettings.putLong(SETTINGS_KEY_LAST_ATTEMPT, now);
|
||||||
newSettings.putInt(SETTINGS_KEY_ATTEMPTS, newAttempts);
|
newSettings.putInt(SETTINGS_KEY_ATTEMPTS, newAttempts);
|
||||||
settingsManager.mergeSettings(txn, newSettings, SETTINGS_NAMESPACE);
|
settingsManager.mergeSettings(txn, newSettings, SETTINGS_NAMESPACE);
|
||||||
MailboxStatus status = new MailboxStatus(now, lastSuccess, newAttempts);
|
List<MailboxVersion> serverSupports = parseServerSupports(oldSettings);
|
||||||
|
MailboxStatus status = new MailboxStatus(now, lastSuccess, newAttempts,
|
||||||
|
serverSupports);
|
||||||
txn.attach(new OwnMailboxConnectionStatusEvent(status));
|
txn.attach(new OwnMailboxConnectionStatusEvent(status));
|
||||||
if (status.hasProblem(now)) txn.attach(new MailboxProblemEvent());
|
if (status.hasProblem(now)) txn.attach(new MailboxProblemEvent());
|
||||||
}
|
}
|
||||||
@@ -165,4 +170,19 @@ class MailboxSettingsManagerImpl implements MailboxSettingsManager {
|
|||||||
if (isNullOrEmpty(filename)) return null;
|
if (isNullOrEmpty(filename)) return null;
|
||||||
return filename;
|
return filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<MailboxVersion> parseServerSupports(Settings s)
|
||||||
|
throws DbException {
|
||||||
|
if (!s.containsKey(SETTINGS_KEY_SERVER_SUPPORTS)) return emptyList();
|
||||||
|
int[] ints = s.getIntArray(SETTINGS_KEY_SERVER_SUPPORTS);
|
||||||
|
// We know we were paired, so we must have proper serverSupports
|
||||||
|
if (ints == null || ints.length == 0 || ints.length % 2 != 0) {
|
||||||
|
throw new DbException();
|
||||||
|
}
|
||||||
|
List<MailboxVersion> serverSupports = new ArrayList<>();
|
||||||
|
for (int i = 0; i < ints.length - 1; i += 2) {
|
||||||
|
serverSupports.add(new MailboxVersion(ints[i], ints[i + 1]));
|
||||||
|
}
|
||||||
|
return serverSupports;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package org.briarproject.bramble.mailbox;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A worker that downloads files from a contact's mailbox.
|
||||||
|
*/
|
||||||
|
@ThreadSafe
|
||||||
|
@NotNullByDefault
|
||||||
|
interface MailboxWorker {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asynchronously starts the worker.
|
||||||
|
*/
|
||||||
|
void start();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys the worker and cancels any pending tasks or retries.
|
||||||
|
*/
|
||||||
|
void destroy();
|
||||||
|
}
|
||||||
@@ -16,17 +16,20 @@ import static org.briarproject.bramble.util.LogUtils.logException;
|
|||||||
* Convenience class for making simple API calls that don't return values.
|
* Convenience class for making simple API calls that don't return values.
|
||||||
*/
|
*/
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public abstract class SimpleApiCall implements ApiCall {
|
class SimpleApiCall implements ApiCall {
|
||||||
|
|
||||||
private static final Logger LOG = getLogger(SimpleApiCall.class.getName());
|
private static final Logger LOG = getLogger(SimpleApiCall.class.getName());
|
||||||
|
|
||||||
abstract void tryToCallApi()
|
private final Attempt attempt;
|
||||||
throws IOException, ApiException, TolerableFailureException;
|
|
||||||
|
SimpleApiCall(Attempt attempt) {
|
||||||
|
this.attempt = attempt;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean callApi() {
|
public boolean callApi() {
|
||||||
try {
|
try {
|
||||||
tryToCallApi();
|
attempt.tryToCallApi();
|
||||||
return false; // Succeeded, don't retry
|
return false; // Succeeded, don't retry
|
||||||
} catch (IOException | ApiException e) {
|
} catch (IOException | ApiException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
@@ -36,4 +39,17 @@ public abstract class SimpleApiCall implements ApiCall {
|
|||||||
return false; // Failed tolerably, don't retry
|
return false; // Failed tolerably, don't retry
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Attempt {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a single attempt to call an API endpoint. If this method
|
||||||
|
* throws an {@link IOException} or an {@link ApiException}, the call
|
||||||
|
* will be retried. If it throws a {@link TolerableFailureException}
|
||||||
|
* or returns without throwing an exception, the call will not be
|
||||||
|
* retried.
|
||||||
|
*/
|
||||||
|
void tryToCallApi()
|
||||||
|
throws IOException, ApiException, TolerableFailureException;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package org.briarproject.bramble.mailbox;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.Plugin;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
|
||||||
|
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||||
|
|
||||||
|
@ThreadSafe
|
||||||
|
@NotNullByDefault
|
||||||
|
interface TorReachabilityMonitor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How long the Tor plugin needs to be continuously
|
||||||
|
* {@link Plugin.State#ACTIVE active} before we assume our contacts can
|
||||||
|
* reach our hidden service.
|
||||||
|
*/
|
||||||
|
long REACHABILITY_PERIOD_MS = MINUTES.toMillis(10);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the monitor.
|
||||||
|
*/
|
||||||
|
void start();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys the monitor.
|
||||||
|
*/
|
||||||
|
void destroy();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an observer that will be called when our Tor hidden service becomes
|
||||||
|
* reachable. If our hidden service is already reachable, the observer is
|
||||||
|
* called immediately.
|
||||||
|
* <p>
|
||||||
|
* Observers are removed after being called, or when the monitor is
|
||||||
|
* {@link #destroy() destroyed}.
|
||||||
|
*/
|
||||||
|
void addOneShotObserver(TorReachabilityObserver o);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an observer that was added via
|
||||||
|
* {@link #addOneShotObserver(TorReachabilityObserver)}.
|
||||||
|
*/
|
||||||
|
void removeObserver(TorReachabilityObserver o);
|
||||||
|
|
||||||
|
interface TorReachabilityObserver {
|
||||||
|
|
||||||
|
void onTorReachable();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
package org.briarproject.bramble.mailbox;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.Cancellable;
|
||||||
|
import org.briarproject.bramble.api.event.Event;
|
||||||
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
|
import org.briarproject.bramble.api.event.EventListener;
|
||||||
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.Plugin;
|
||||||
|
import org.briarproject.bramble.api.plugin.PluginManager;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.TransportActiveEvent;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent;
|
||||||
|
import org.briarproject.bramble.api.system.TaskScheduler;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.annotation.concurrent.GuardedBy;
|
||||||
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||||
|
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
|
||||||
|
import static org.briarproject.bramble.api.plugin.TorConstants.ID;
|
||||||
|
|
||||||
|
@ThreadSafe
|
||||||
|
@NotNullByDefault
|
||||||
|
class TorReachabilityMonitorImpl
|
||||||
|
implements TorReachabilityMonitor, EventListener {
|
||||||
|
|
||||||
|
private final Executor ioExecutor;
|
||||||
|
private final TaskScheduler taskScheduler;
|
||||||
|
private final PluginManager pluginManager;
|
||||||
|
private final EventBus eventBus;
|
||||||
|
private final Object lock = new Object();
|
||||||
|
|
||||||
|
@GuardedBy("lock")
|
||||||
|
private boolean reachable = false, destroyed = false;
|
||||||
|
|
||||||
|
@GuardedBy("lock")
|
||||||
|
private final List<TorReachabilityObserver> observers = new ArrayList<>();
|
||||||
|
|
||||||
|
@GuardedBy("lock")
|
||||||
|
@Nullable
|
||||||
|
private Cancellable task = null;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
TorReachabilityMonitorImpl(
|
||||||
|
@IoExecutor Executor ioExecutor,
|
||||||
|
TaskScheduler taskScheduler,
|
||||||
|
PluginManager pluginManager,
|
||||||
|
EventBus eventBus) {
|
||||||
|
this.ioExecutor = ioExecutor;
|
||||||
|
this.taskScheduler = taskScheduler;
|
||||||
|
this.pluginManager = pluginManager;
|
||||||
|
this.eventBus = eventBus;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start() {
|
||||||
|
eventBus.addListener(this);
|
||||||
|
Plugin plugin = pluginManager.getPlugin(ID);
|
||||||
|
if (plugin != null && plugin.getState() == ACTIVE) onTorActive();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() {
|
||||||
|
eventBus.removeListener(this);
|
||||||
|
synchronized (lock) {
|
||||||
|
destroyed = true;
|
||||||
|
if (task != null) task.cancel();
|
||||||
|
task = null;
|
||||||
|
observers.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addOneShotObserver(TorReachabilityObserver o) {
|
||||||
|
boolean callNow = false;
|
||||||
|
synchronized (lock) {
|
||||||
|
if (destroyed) return;
|
||||||
|
if (reachable) callNow = true;
|
||||||
|
else observers.add(o);
|
||||||
|
}
|
||||||
|
if (callNow) o.onTorReachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeObserver(TorReachabilityObserver o) {
|
||||||
|
synchronized (lock) {
|
||||||
|
if (destroyed) return;
|
||||||
|
observers.remove(o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void eventOccurred(Event e) {
|
||||||
|
if (e instanceof TransportActiveEvent) {
|
||||||
|
TransportActiveEvent t = (TransportActiveEvent) e;
|
||||||
|
if (t.getTransportId().equals(ID)) onTorActive();
|
||||||
|
} else if (e instanceof TransportInactiveEvent) {
|
||||||
|
TransportInactiveEvent t = (TransportInactiveEvent) e;
|
||||||
|
if (t.getTransportId().equals(ID)) onTorInactive();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onTorActive() {
|
||||||
|
synchronized (lock) {
|
||||||
|
if (destroyed || task != null) return;
|
||||||
|
task = taskScheduler.schedule(this::onTorReachable, ioExecutor,
|
||||||
|
REACHABILITY_PERIOD_MS, MILLISECONDS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onTorInactive() {
|
||||||
|
synchronized (lock) {
|
||||||
|
reachable = false;
|
||||||
|
if (task != null) task.cancel();
|
||||||
|
task = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IoExecutor
|
||||||
|
private void onTorReachable() {
|
||||||
|
List<TorReachabilityObserver> observers;
|
||||||
|
synchronized (lock) {
|
||||||
|
if (destroyed) return;
|
||||||
|
reachable = true;
|
||||||
|
observers = new ArrayList<>(this.observers);
|
||||||
|
this.observers.clear();
|
||||||
|
task = null;
|
||||||
|
}
|
||||||
|
for (TorReachabilityObserver o : observers) o.onTorReachable();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
package org.briarproject.bramble.plugin;
|
package org.briarproject.bramble.plugin;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.Backoff;
|
import org.briarproject.bramble.api.plugin.Backoff;
|
||||||
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
||||||
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
@@ -11,8 +13,9 @@ import javax.annotation.concurrent.Immutable;
|
|||||||
class BackoffFactoryImpl implements BackoffFactory {
|
class BackoffFactoryImpl implements BackoffFactory {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Backoff createBackoff(int minInterval, int maxInterval,
|
public Backoff createBackoff(EventBus eventBus, TransportId transportId,
|
||||||
double base) {
|
int minInterval, int maxInterval, double base) {
|
||||||
return new BackoffImpl(minInterval, maxInterval, base);
|
return new BackoffImpl(eventBus, transportId, minInterval, maxInterval,
|
||||||
|
base);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
package org.briarproject.bramble.plugin;
|
package org.briarproject.bramble.plugin;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.Backoff;
|
import org.briarproject.bramble.api.plugin.Backoff;
|
||||||
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.PollingIntervalDecreasedEvent;
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
@@ -11,11 +14,16 @@ import javax.annotation.concurrent.ThreadSafe;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class BackoffImpl implements Backoff {
|
class BackoffImpl implements Backoff {
|
||||||
|
|
||||||
|
private final EventBus eventBus;
|
||||||
|
private final TransportId transportId;
|
||||||
private final int minInterval, maxInterval;
|
private final int minInterval, maxInterval;
|
||||||
private final double base;
|
private final double base;
|
||||||
private final AtomicInteger backoff;
|
private final AtomicInteger backoff;
|
||||||
|
|
||||||
BackoffImpl(int minInterval, int maxInterval, double base) {
|
BackoffImpl(EventBus eventBus, TransportId transportId,
|
||||||
|
int minInterval, int maxInterval, double base) {
|
||||||
|
this.eventBus = eventBus;
|
||||||
|
this.transportId = transportId;
|
||||||
this.minInterval = minInterval;
|
this.minInterval = minInterval;
|
||||||
this.maxInterval = maxInterval;
|
this.maxInterval = maxInterval;
|
||||||
this.base = base;
|
this.base = base;
|
||||||
@@ -37,6 +45,9 @@ class BackoffImpl implements Backoff {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reset() {
|
public void reset() {
|
||||||
backoff.set(0);
|
int old = backoff.getAndSet(0);
|
||||||
|
if (old > 0) {
|
||||||
|
eventBus.broadcast(new PollingIntervalDecreasedEvent(transportId));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import org.briarproject.bramble.api.plugin.TransportId;
|
|||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.PollingIntervalDecreasedEvent;
|
||||||
import org.briarproject.bramble.api.plugin.event.TransportActiveEvent;
|
import org.briarproject.bramble.api.plugin.event.TransportActiveEvent;
|
||||||
import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent;
|
import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent;
|
||||||
import org.briarproject.bramble.api.plugin.event.TransportStateEvent;
|
import org.briarproject.bramble.api.plugin.event.TransportStateEvent;
|
||||||
@@ -362,6 +363,11 @@ class PluginManagerImpl implements PluginManager, Service {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void pollingIntervalDecreased() {
|
||||||
|
eventBus.broadcast(new PollingIntervalDecreasedEvent(id));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleConnection(DuplexTransportConnection d) {
|
public void handleConnection(DuplexTransportConnection d) {
|
||||||
connectionManager.manageIncomingConnection(id, d);
|
connectionManager.manageIncomingConnection(id, d);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import org.briarproject.bramble.api.plugin.TransportId;
|
|||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||||
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
|
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
|
||||||
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
|
import org.briarproject.bramble.api.plugin.event.PollingIntervalDecreasedEvent;
|
||||||
import org.briarproject.bramble.api.plugin.event.TransportActiveEvent;
|
import org.briarproject.bramble.api.plugin.event.TransportActiveEvent;
|
||||||
import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent;
|
import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent;
|
||||||
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
|
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
|
||||||
@@ -107,10 +107,14 @@ class PollerImpl implements Poller, EventListener {
|
|||||||
if (!c.isIncoming() && c.isException()) {
|
if (!c.isIncoming() && c.isException()) {
|
||||||
connectToContact(c.getContactId(), c.getTransportId());
|
connectToContact(c.getContactId(), c.getTransportId());
|
||||||
}
|
}
|
||||||
} else if (e instanceof ConnectionOpenedEvent) {
|
} else if (e instanceof PollingIntervalDecreasedEvent) {
|
||||||
ConnectionOpenedEvent c = (ConnectionOpenedEvent) e;
|
PollingIntervalDecreasedEvent p = (PollingIntervalDecreasedEvent) e;
|
||||||
// Reschedule polling, the polling interval may have decreased
|
TransportId t = p.getTransportId();
|
||||||
reschedule(c.getTransportId());
|
if (LOG.isLoggable(INFO)) {
|
||||||
|
LOG.info("Polling interval decreased for " + t);
|
||||||
|
}
|
||||||
|
// Reschedule polling
|
||||||
|
reschedule(t);
|
||||||
} else if (e instanceof TransportActiveEvent) {
|
} else if (e instanceof TransportActiveEvent) {
|
||||||
TransportActiveEvent t = (TransportActiveEvent) e;
|
TransportActiveEvent t = (TransportActiveEvent) e;
|
||||||
// Poll the newly activated transport
|
// Poll the newly activated transport
|
||||||
@@ -228,7 +232,7 @@ class PollerImpl implements Poller, EventListener {
|
|||||||
if (!connected.contains(c))
|
if (!connected.contains(c))
|
||||||
properties.add(new Pair<>(e.getValue(), new Handler(c, t)));
|
properties.add(new Pair<>(e.getValue(), new Handler(c, t)));
|
||||||
}
|
}
|
||||||
if (!properties.isEmpty()) p.poll(properties);
|
p.poll(properties);
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -335,7 +335,7 @@ abstract class AbstractBluetoothPlugin<S, SS> implements BluetoothPlugin,
|
|||||||
@Override
|
@Override
|
||||||
public void poll(Collection<Pair<TransportProperties, ConnectionHandler>>
|
public void poll(Collection<Pair<TransportProperties, ConnectionHandler>>
|
||||||
properties) {
|
properties) {
|
||||||
if (getState() != ACTIVE) return;
|
if (properties.isEmpty() || getState() != ACTIVE) return;
|
||||||
backoff.increment();
|
backoff.increment();
|
||||||
for (Pair<TransportProperties, ConnectionHandler> p : properties) {
|
for (Pair<TransportProperties, ConnectionHandler> p : properties) {
|
||||||
connect(p.getFirst(), p.getSecond());
|
connect(p.getFirst(), p.getSecond());
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ abstract class AbstractRemovableDrivePlugin implements SimplexPlugin {
|
|||||||
public void start() {
|
public void start() {
|
||||||
callback.mergeLocalProperties(
|
callback.mergeLocalProperties(
|
||||||
new TransportProperties(singletonMap(PROP_SUPPORTED, "true")));
|
new TransportProperties(singletonMap(PROP_SUPPORTED, "true")));
|
||||||
|
callback.pluginStateChanged(ACTIVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import java.io.File;
|
|||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
@@ -29,11 +30,6 @@ abstract class FilePlugin implements SimplexPlugin {
|
|||||||
protected final PluginCallback callback;
|
protected final PluginCallback callback;
|
||||||
protected final long maxLatency;
|
protected final long maxLatency;
|
||||||
|
|
||||||
protected abstract void writerFinished(File f, boolean exception);
|
|
||||||
|
|
||||||
protected abstract void readerFinished(File f, boolean exception,
|
|
||||||
boolean recognised);
|
|
||||||
|
|
||||||
FilePlugin(PluginCallback callback, long maxLatency) {
|
FilePlugin(PluginCallback callback, long maxLatency) {
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
this.maxLatency = maxLatency;
|
this.maxLatency = maxLatency;
|
||||||
@@ -50,9 +46,8 @@ abstract class FilePlugin implements SimplexPlugin {
|
|||||||
String path = p.get(PROP_PATH);
|
String path = p.get(PROP_PATH);
|
||||||
if (isNullOrEmpty(path)) return null;
|
if (isNullOrEmpty(path)) return null;
|
||||||
try {
|
try {
|
||||||
File file = new File(path);
|
FileInputStream in = new FileInputStream(path);
|
||||||
FileInputStream in = new FileInputStream(file);
|
return new TransportInputStreamReader(in);
|
||||||
return new FileTransportReader(file, in, this);
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
return null;
|
return null;
|
||||||
@@ -70,8 +65,8 @@ abstract class FilePlugin implements SimplexPlugin {
|
|||||||
LOG.info("Failed to create file");
|
LOG.info("Failed to create file");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
FileOutputStream out = new FileOutputStream(file);
|
OutputStream out = new FileOutputStream(file);
|
||||||
return new FileTransportWriter(file, out, this);
|
return new TransportOutputStreamWriter(this, out);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
package org.briarproject.bramble.plugin.file;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static org.briarproject.bramble.util.IoUtils.tryToClose;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
class FileTransportReader implements TransportConnectionReader {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
Logger.getLogger(FileTransportReader.class.getName());
|
|
||||||
|
|
||||||
private final File file;
|
|
||||||
private final InputStream in;
|
|
||||||
private final FilePlugin plugin;
|
|
||||||
|
|
||||||
FileTransportReader(File file, InputStream in, FilePlugin plugin) {
|
|
||||||
this.file = file;
|
|
||||||
this.in = in;
|
|
||||||
this.plugin = plugin;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InputStream getInputStream() {
|
|
||||||
return in;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void dispose(boolean exception, boolean recognised) {
|
|
||||||
tryToClose(in, LOG, WARNING);
|
|
||||||
plugin.readerFinished(file, exception, recognised);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
package org.briarproject.bramble.plugin.file;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static org.briarproject.bramble.util.IoUtils.tryToClose;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
class FileTransportWriter implements TransportConnectionWriter {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
Logger.getLogger(FileTransportWriter.class.getName());
|
|
||||||
|
|
||||||
private final File file;
|
|
||||||
private final OutputStream out;
|
|
||||||
private final FilePlugin plugin;
|
|
||||||
|
|
||||||
FileTransportWriter(File file, OutputStream out, FilePlugin plugin) {
|
|
||||||
this.file = file;
|
|
||||||
this.out = out;
|
|
||||||
this.plugin = plugin;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getMaxLatency() {
|
|
||||||
return plugin.getMaxLatency();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMaxIdleTime() {
|
|
||||||
return plugin.getMaxIdleTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isLossyAndCheap() {
|
|
||||||
return plugin.isLossyAndCheap();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public OutputStream getOutputStream() {
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void dispose(boolean exception) {
|
|
||||||
tryToClose(out, LOG, WARNING);
|
|
||||||
plugin.writerFinished(file, exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package org.briarproject.bramble.plugin.file;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.Pair;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.ConnectionHandler;
|
||||||
|
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||||
|
import org.briarproject.bramble.api.plugin.PluginException;
|
||||||
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
|
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxConstants.ID;
|
||||||
|
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
class MailboxPlugin extends FilePlugin {
|
||||||
|
|
||||||
|
MailboxPlugin(PluginCallback callback, long maxLatency) {
|
||||||
|
super(callback, maxLatency);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TransportId getId() {
|
||||||
|
return ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxIdleTime() {
|
||||||
|
// Unused for simplex transports
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start() throws PluginException {
|
||||||
|
callback.pluginStateChanged(ACTIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() throws PluginException {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public State getState() {
|
||||||
|
return ACTIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getReasonsDisabled() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldPoll() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPollingInterval() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void poll(
|
||||||
|
Collection<Pair<TransportProperties, ConnectionHandler>> properties) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLossyAndCheap() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package org.briarproject.bramble.plugin.file;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||||
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
|
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
|
||||||
|
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static java.util.concurrent.TimeUnit.DAYS;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxConstants.ID;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
public class MailboxPluginFactory implements SimplexPluginFactory {
|
||||||
|
|
||||||
|
private static final long MAX_LATENCY = DAYS.toMillis(14);
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
MailboxPluginFactory() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TransportId getId() {
|
||||||
|
return ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getMaxLatency() {
|
||||||
|
return MAX_LATENCY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public SimplexPlugin createPlugin(PluginCallback callback) {
|
||||||
|
return new MailboxPlugin(callback, MAX_LATENCY);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -56,8 +56,8 @@ public class LanTcpPluginFactory implements DuplexPluginFactory {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DuplexPlugin createPlugin(PluginCallback callback) {
|
public DuplexPlugin createPlugin(PluginCallback callback) {
|
||||||
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
Backoff backoff = backoffFactory.createBackoff(eventBus, ID,
|
||||||
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
MIN_POLLING_INTERVAL, MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
||||||
LanTcpPlugin plugin = new LanTcpPlugin(ioExecutor, wakefulIoExecutor,
|
LanTcpPlugin plugin = new LanTcpPlugin(ioExecutor, wakefulIoExecutor,
|
||||||
backoff, callback, MAX_LATENCY, MAX_IDLE_TIME,
|
backoff, callback, MAX_LATENCY, MAX_IDLE_TIME,
|
||||||
CONNECTION_TIMEOUT);
|
CONNECTION_TIMEOUT);
|
||||||
|
|||||||
@@ -246,7 +246,7 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener {
|
|||||||
@Override
|
@Override
|
||||||
public void poll(Collection<Pair<TransportProperties, ConnectionHandler>>
|
public void poll(Collection<Pair<TransportProperties, ConnectionHandler>>
|
||||||
properties) {
|
properties) {
|
||||||
if (getState() != ACTIVE) return;
|
if (properties.isEmpty() || getState() != ACTIVE) return;
|
||||||
backoff.increment();
|
backoff.increment();
|
||||||
for (Pair<TransportProperties, ConnectionHandler> p : properties) {
|
for (Pair<TransportProperties, ConnectionHandler> p : properties) {
|
||||||
connect(p.getFirst(), p.getSecond());
|
connect(p.getFirst(), p.getSecond());
|
||||||
|
|||||||
@@ -60,8 +60,8 @@ public class WanTcpPluginFactory implements DuplexPluginFactory {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DuplexPlugin createPlugin(PluginCallback callback) {
|
public DuplexPlugin createPlugin(PluginCallback callback) {
|
||||||
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
Backoff backoff = backoffFactory.createBackoff(eventBus, ID,
|
||||||
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
MIN_POLLING_INTERVAL, MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
||||||
PortMapper portMapper = new PortMapperImpl(shutdownManager);
|
PortMapper portMapper = new PortMapperImpl(shutdownManager);
|
||||||
WanTcpPlugin plugin = new WanTcpPlugin(ioExecutor, wakefulIoExecutor,
|
WanTcpPlugin plugin = new WanTcpPlugin(ioExecutor, wakefulIoExecutor,
|
||||||
backoff, portMapper, callback, MAX_LATENCY, MAX_IDLE_TIME,
|
backoff, portMapper, callback, MAX_LATENCY, MAX_IDLE_TIME,
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public interface CircumventionProvider {
|
|||||||
* Countries where bridge connections are likely to work.
|
* Countries where bridge connections are likely to work.
|
||||||
* Should be a subset of {@link #BLOCKED} and the union of
|
* Should be a subset of {@link #BLOCKED} and the union of
|
||||||
* {@link #DEFAULT_BRIDGES}, {@link #NON_DEFAULT_BRIDGES} and
|
* {@link #DEFAULT_BRIDGES}, {@link #NON_DEFAULT_BRIDGES} and
|
||||||
* {@link #MEEK_BRIDGES}.
|
* {@link #DPI_BRIDGES}.
|
||||||
*/
|
*/
|
||||||
String[] BRIDGES = {"BY", "CN", "EG", "IR", "RU", "TM", "VE"};
|
String[] BRIDGES = {"BY", "CN", "EG", "IR", "RU", "TM", "VE"};
|
||||||
|
|
||||||
@@ -44,10 +44,10 @@ public interface CircumventionProvider {
|
|||||||
String[] NON_DEFAULT_BRIDGES = {"BY", "RU", "TM"};
|
String[] NON_DEFAULT_BRIDGES = {"BY", "RU", "TM"};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Countries where obfs4 and vanilla bridges won't work and meek is needed.
|
* Countries where vanilla bridges are blocked via DPI but non-default
|
||||||
* Should be a subset of {@link #BRIDGES}.
|
* obfs4 bridges and meek may work. Should be a subset of {@link #BRIDGES}.
|
||||||
*/
|
*/
|
||||||
String[] MEEK_BRIDGES = {"CN", "IR"};
|
String[] DPI_BRIDGES = {"CN", "IR"};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if vanilla Tor connections are blocked in the given country.
|
* Returns true if vanilla Tor connections are blocked in the given country.
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import javax.annotation.concurrent.Immutable;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static java.util.Collections.singletonList;
|
|
||||||
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.DEFAULT_OBFS4;
|
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.DEFAULT_OBFS4;
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.MEEK;
|
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.MEEK;
|
||||||
@@ -35,8 +34,8 @@ class CircumventionProviderImpl implements CircumventionProvider {
|
|||||||
new HashSet<>(asList(DEFAULT_BRIDGES));
|
new HashSet<>(asList(DEFAULT_BRIDGES));
|
||||||
private static final Set<String> NON_DEFAULT_BRIDGE_COUNTRIES =
|
private static final Set<String> NON_DEFAULT_BRIDGE_COUNTRIES =
|
||||||
new HashSet<>(asList(NON_DEFAULT_BRIDGES));
|
new HashSet<>(asList(NON_DEFAULT_BRIDGES));
|
||||||
private static final Set<String> MEEK_COUNTRIES =
|
private static final Set<String> DPI_COUNTRIES =
|
||||||
new HashSet<>(asList(MEEK_BRIDGES));
|
new HashSet<>(asList(DPI_BRIDGES));
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
CircumventionProviderImpl() {
|
CircumventionProviderImpl() {
|
||||||
@@ -58,8 +57,8 @@ class CircumventionProviderImpl implements CircumventionProvider {
|
|||||||
return asList(DEFAULT_OBFS4, VANILLA);
|
return asList(DEFAULT_OBFS4, VANILLA);
|
||||||
} else if (NON_DEFAULT_BRIDGE_COUNTRIES.contains(countryCode)) {
|
} else if (NON_DEFAULT_BRIDGE_COUNTRIES.contains(countryCode)) {
|
||||||
return asList(NON_DEFAULT_OBFS4, VANILLA);
|
return asList(NON_DEFAULT_OBFS4, VANILLA);
|
||||||
} else if (MEEK_COUNTRIES.contains(countryCode)) {
|
} else if (DPI_COUNTRIES.contains(countryCode)) {
|
||||||
return singletonList(MEEK);
|
return asList(NON_DEFAULT_OBFS4, MEEK);
|
||||||
} else {
|
} else {
|
||||||
return asList(DEFAULT_OBFS4, VANILLA);
|
return asList(DEFAULT_OBFS4, VANILLA);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import org.briarproject.bramble.api.network.event.NetworkStatusEvent;
|
|||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
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.ConnectionHandler;
|
import org.briarproject.bramble.api.plugin.ConnectionHandler;
|
||||||
import org.briarproject.bramble.api.plugin.PluginCallback;
|
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||||
import org.briarproject.bramble.api.plugin.PluginException;
|
import org.briarproject.bramble.api.plugin.PluginException;
|
||||||
@@ -26,6 +25,7 @@ import org.briarproject.bramble.api.plugin.TorConstants;
|
|||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
|
||||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||||
import org.briarproject.bramble.api.rendezvous.KeyMaterialSource;
|
import org.briarproject.bramble.api.rendezvous.KeyMaterialSource;
|
||||||
import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint;
|
import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint;
|
||||||
@@ -67,6 +67,7 @@ import javax.net.SocketFactory;
|
|||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
|
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||||
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 java.util.logging.Logger.getLogger;
|
import static java.util.logging.Logger.getLogger;
|
||||||
@@ -107,7 +108,7 @@ import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
|||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||||
|
|
||||||
private static final Logger LOG = getLogger(TorPlugin.class.getName());
|
protected static final Logger LOG = getLogger(TorPlugin.class.getName());
|
||||||
|
|
||||||
private static final String[] EVENTS = {
|
private static final String[] EVENTS = {
|
||||||
"CIRC",
|
"CIRC",
|
||||||
@@ -124,14 +125,53 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
private static final int COOKIE_POLLING_INTERVAL_MS = 200;
|
private static final int COOKIE_POLLING_INTERVAL_MS = 200;
|
||||||
private static final Pattern ONION_V3 = Pattern.compile("[a-z2-7]{56}");
|
private static final Pattern ONION_V3 = Pattern.compile("[a-z2-7]{56}");
|
||||||
|
|
||||||
private final Executor ioExecutor, wakefulIoExecutor;
|
/**
|
||||||
|
* After this many consecutive successful connections to our own hidden
|
||||||
|
* service we consider the network to be stable.
|
||||||
|
* <p>
|
||||||
|
* This constant times {@link #POLLING_INTERVAL_UNSTABLE} should be
|
||||||
|
* greater than {@link #POLLING_INTERVAL_STABLE}. This ensures that if
|
||||||
|
* we experience a network outage that isn't detected by the
|
||||||
|
* {@link NetworkManager}, and if one of our contacts comes online during
|
||||||
|
* the outage, then either the outage lasts longer than
|
||||||
|
* {@link #POLLING_INTERVAL_STABLE}, in which case we detect the outage
|
||||||
|
* by failing to connect to our own hidden service, or the outage ends
|
||||||
|
* before the contact considers their own network connection to be stable,
|
||||||
|
* in which case the contact is still trying to connect to us when the
|
||||||
|
* outage ends. Either way, we don't end up in a situation where both we
|
||||||
|
* and the contact consider our network connections to be stable and stop
|
||||||
|
* trying to connect to each other, despite both being online.
|
||||||
|
*/
|
||||||
|
private static final int STABLE_NETWORK_THRESHOLD = 10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* After this many consecutive failed connections to our own hidden service
|
||||||
|
* we consider our connection to the Tor network to be broken.
|
||||||
|
*/
|
||||||
|
private static final int BROKEN_NETWORK_THRESHOLD = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How often to poll our own hidden service when the network is considered
|
||||||
|
* to be stable.
|
||||||
|
*/
|
||||||
|
private static final int POLLING_INTERVAL_STABLE =
|
||||||
|
(int) MINUTES.toMillis(10);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How often to poll our own hidden service and our contacts' hidden
|
||||||
|
* services when the network is considered to be unstable.
|
||||||
|
*/
|
||||||
|
private static final int POLLING_INTERVAL_UNSTABLE =
|
||||||
|
(int) MINUTES.toMillis(2);
|
||||||
|
|
||||||
|
protected final Executor ioExecutor;
|
||||||
|
private final Executor wakefulIoExecutor;
|
||||||
private final Executor connectionStatusExecutor;
|
private final Executor connectionStatusExecutor;
|
||||||
private final NetworkManager networkManager;
|
private final NetworkManager networkManager;
|
||||||
private final LocationUtils locationUtils;
|
private final LocationUtils locationUtils;
|
||||||
private final SocketFactory torSocketFactory;
|
private final SocketFactory torSocketFactory;
|
||||||
private final Clock clock;
|
private final Clock clock;
|
||||||
private final BatteryManager batteryManager;
|
private final BatteryManager batteryManager;
|
||||||
private final Backoff backoff;
|
|
||||||
private final TorRendezvousCrypto torRendezvousCrypto;
|
private final TorRendezvousCrypto torRendezvousCrypto;
|
||||||
private final PluginCallback callback;
|
private final PluginCallback callback;
|
||||||
private final String architecture;
|
private final String architecture;
|
||||||
@@ -151,6 +191,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
private volatile Socket controlSocket = null;
|
private volatile Socket controlSocket = null;
|
||||||
private volatile TorControlConnection controlConnection = null;
|
private volatile TorControlConnection controlConnection = null;
|
||||||
private volatile Settings settings = null;
|
private volatile Settings settings = null;
|
||||||
|
private volatile String ownOnion = null;
|
||||||
|
|
||||||
protected abstract int getProcessId();
|
protected abstract int getProcessId();
|
||||||
|
|
||||||
@@ -165,7 +206,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
ResourceProvider resourceProvider,
|
ResourceProvider resourceProvider,
|
||||||
CircumventionProvider circumventionProvider,
|
CircumventionProvider circumventionProvider,
|
||||||
BatteryManager batteryManager,
|
BatteryManager batteryManager,
|
||||||
Backoff backoff,
|
|
||||||
TorRendezvousCrypto torRendezvousCrypto,
|
TorRendezvousCrypto torRendezvousCrypto,
|
||||||
PluginCallback callback,
|
PluginCallback callback,
|
||||||
String architecture,
|
String architecture,
|
||||||
@@ -183,7 +223,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
this.resourceProvider = resourceProvider;
|
this.resourceProvider = resourceProvider;
|
||||||
this.circumventionProvider = circumventionProvider;
|
this.circumventionProvider = circumventionProvider;
|
||||||
this.batteryManager = batteryManager;
|
this.batteryManager = batteryManager;
|
||||||
this.backoff = backoff;
|
|
||||||
this.torRendezvousCrypto = torRendezvousCrypto;
|
this.torRendezvousCrypto = torRendezvousCrypto;
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
this.architecture = architecture;
|
this.architecture = architecture;
|
||||||
@@ -254,34 +293,15 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
Map<String, String> env = pb.environment();
|
Map<String, String> env = pb.environment();
|
||||||
env.put("HOME", torDirectory.getAbsolutePath());
|
env.put("HOME", torDirectory.getAbsolutePath());
|
||||||
pb.directory(torDirectory);
|
pb.directory(torDirectory);
|
||||||
|
pb.redirectErrorStream(true);
|
||||||
try {
|
try {
|
||||||
torProcess = pb.start();
|
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
|
|
||||||
if (LOG.isLoggable(INFO)) {
|
|
||||||
Scanner stdout = new Scanner(torProcess.getInputStream());
|
|
||||||
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();
|
|
||||||
stderr.close();
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
// Wait for the process to detach or exit
|
// Wait for the Tor process to start
|
||||||
int exit = torProcess.waitFor();
|
waitForTorToStart(torProcess);
|
||||||
if (exit != 0) {
|
|
||||||
if (LOG.isLoggable(WARNING))
|
|
||||||
LOG.warning("Tor exited with value " + exit);
|
|
||||||
throw new PluginException();
|
|
||||||
}
|
|
||||||
// Wait for the auth cookie file to be created/updated
|
// Wait for the auth cookie file to be created/updated
|
||||||
long start = clock.currentTimeMillis();
|
long start = clock.currentTimeMillis();
|
||||||
while (cookieFile.length() < 32) {
|
while (cookieFile.length() < 32) {
|
||||||
@@ -397,7 +417,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
return zin;
|
return zin;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void append(StringBuilder strb, String name, int value) {
|
private static void append(StringBuilder strb, String name, Object value) {
|
||||||
strb.append(name);
|
strb.append(name);
|
||||||
strb.append(" ");
|
strb.append(" ");
|
||||||
strb.append(value);
|
strb.append(value);
|
||||||
@@ -405,13 +425,17 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private InputStream getConfigInputStream() {
|
private InputStream getConfigInputStream() {
|
||||||
|
File dataDirectory = new File(torDirectory, ".tor");
|
||||||
StringBuilder strb = new StringBuilder();
|
StringBuilder strb = new StringBuilder();
|
||||||
append(strb, "ControlPort", torControlPort);
|
append(strb, "ControlPort", torControlPort);
|
||||||
append(strb, "CookieAuthentication", 1);
|
append(strb, "CookieAuthentication", 1);
|
||||||
|
append(strb, "DataDirectory", dataDirectory.getAbsolutePath());
|
||||||
append(strb, "DisableNetwork", 1);
|
append(strb, "DisableNetwork", 1);
|
||||||
append(strb, "RunAsDaemon", 1);
|
append(strb, "RunAsDaemon", 1);
|
||||||
append(strb, "SafeSocks", 1);
|
append(strb, "SafeSocks", 1);
|
||||||
append(strb, "SocksPort", torSocksPort);
|
append(strb, "SocksPort", torSocksPort);
|
||||||
|
strb.append("GeoIPFile\n");
|
||||||
|
strb.append("GeoIPv6File\n");
|
||||||
//noinspection CharsetObjectCanBeUsed
|
//noinspection CharsetObjectCanBeUsed
|
||||||
return new ByteArrayInputStream(
|
return new ByteArrayInputStream(
|
||||||
strb.toString().getBytes(Charset.forName("UTF-8")));
|
strb.toString().getBytes(Charset.forName("UTF-8")));
|
||||||
@@ -442,6 +466,23 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void waitForTorToStart(Process torProcess)
|
||||||
|
throws InterruptedException, PluginException {
|
||||||
|
Scanner stdout = new Scanner(torProcess.getInputStream());
|
||||||
|
// Log the first line of stdout (contains Tor and library versions)
|
||||||
|
if (stdout.hasNextLine()) LOG.info(stdout.nextLine());
|
||||||
|
// Read the process's stdout (and redirected stderr) until it detaches
|
||||||
|
while (stdout.hasNextLine()) stdout.nextLine();
|
||||||
|
stdout.close();
|
||||||
|
// Wait for the process to detach or exit
|
||||||
|
int exit = torProcess.waitFor();
|
||||||
|
if (exit != 0) {
|
||||||
|
if (LOG.isLoggable(WARNING))
|
||||||
|
LOG.warning("Tor exited with value " + exit);
|
||||||
|
throw new PluginException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void bind() {
|
private void bind() {
|
||||||
ioExecutor.execute(() -> {
|
ioExecutor.execute(() -> {
|
||||||
// If there's already a port number stored in config, reuse it
|
// If there's already a port number stored in config, reuse it
|
||||||
@@ -471,7 +512,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
callback.mergeSettings(s);
|
callback.mergeSettings(s);
|
||||||
// Create a hidden service if necessary
|
// Create a hidden service if necessary
|
||||||
ioExecutor.execute(() -> publishHiddenService(localPort));
|
ioExecutor.execute(() -> publishHiddenService(localPort));
|
||||||
backoff.reset();
|
|
||||||
// Accept incoming hidden service connections from Tor
|
// Accept incoming hidden service connections from Tor
|
||||||
acceptContactConnections(ss);
|
acceptContactConnections(ss);
|
||||||
});
|
});
|
||||||
@@ -510,6 +550,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String onion3 = response.get(HS_ADDRESS);
|
String onion3 = response.get(HS_ADDRESS);
|
||||||
|
ownOnion = onion3;
|
||||||
if (LOG.isLoggable(INFO)) {
|
if (LOG.isLoggable(INFO)) {
|
||||||
LOG.info("V3 hidden service " + scrubOnion(onion3));
|
LOG.info("V3 hidden service " + scrubOnion(onion3));
|
||||||
}
|
}
|
||||||
@@ -538,7 +579,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LOG.info("Connection received");
|
LOG.info("Connection received");
|
||||||
backoff.reset();
|
|
||||||
callback.handleConnection(new TorTransportConnection(this, s));
|
callback.handleConnection(new TorTransportConnection(this, s));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -615,14 +655,58 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getPollingInterval() {
|
public int getPollingInterval() {
|
||||||
return backoff.getPollingInterval();
|
if (state.isNetworkStable()) {
|
||||||
|
LOG.info("Using stable polling interval");
|
||||||
|
return POLLING_INTERVAL_STABLE;
|
||||||
|
} else {
|
||||||
|
LOG.info("Using unstable polling interval");
|
||||||
|
return POLLING_INTERVAL_UNSTABLE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void poll(Collection<Pair<TransportProperties, ConnectionHandler>>
|
public void poll(Collection<Pair<TransportProperties, ConnectionHandler>>
|
||||||
properties) {
|
properties) {
|
||||||
if (getState() != ACTIVE) return;
|
if (getState() != ACTIVE) return;
|
||||||
backoff.increment();
|
String ownOnion = this.ownOnion;
|
||||||
|
if (ownOnion == null) {
|
||||||
|
// Our own hidden service hasn't been created yet
|
||||||
|
pollContacts(properties);
|
||||||
|
} else {
|
||||||
|
// If the network is unstable, poll our contacts
|
||||||
|
boolean stable = state.isNetworkStable();
|
||||||
|
if (!stable) pollContacts(properties);
|
||||||
|
// Poll our own hidden service to check if the network is stable
|
||||||
|
wakefulIoExecutor.execute(() -> {
|
||||||
|
LOG.info("Connecting to own hidden service");
|
||||||
|
TransportProperties p = new TransportProperties();
|
||||||
|
p.put(PROP_ONION_V3, ownOnion);
|
||||||
|
DuplexTransportConnection d = createConnection(p);
|
||||||
|
if (d == null) {
|
||||||
|
LOG.info("Could not connect to own hidden service");
|
||||||
|
state.onStabilityCheckFailed();
|
||||||
|
// If the network was previously considered stable then
|
||||||
|
// we didn't poll our contacts above, so poll them now
|
||||||
|
if (stable) pollContacts(properties);
|
||||||
|
} else {
|
||||||
|
LOG.info("Connected to own hidden service");
|
||||||
|
// Close the connection (this will cause the other end of
|
||||||
|
// the connection to log an EOFException)
|
||||||
|
try {
|
||||||
|
d.getWriter().dispose(false);
|
||||||
|
d.getReader().dispose(false, false);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
}
|
||||||
|
state.onStabilityCheckSucceeded();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void pollContacts(
|
||||||
|
Collection<Pair<TransportProperties, ConnectionHandler>> properties) {
|
||||||
|
if (properties.isEmpty() || getState() != ACTIVE) return;
|
||||||
for (Pair<TransportProperties, ConnectionHandler> p : properties) {
|
for (Pair<TransportProperties, ConnectionHandler> p : properties) {
|
||||||
connect(p.getFirst(), p.getSecond());
|
connect(p.getFirst(), p.getSecond());
|
||||||
}
|
}
|
||||||
@@ -631,10 +715,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
private void connect(TransportProperties p, ConnectionHandler h) {
|
private void connect(TransportProperties p, ConnectionHandler h) {
|
||||||
wakefulIoExecutor.execute(() -> {
|
wakefulIoExecutor.execute(() -> {
|
||||||
DuplexTransportConnection d = createConnection(p);
|
DuplexTransportConnection d = createConnection(p);
|
||||||
if (d != null) {
|
if (d != null) h.handleConnection(d);
|
||||||
backoff.reset();
|
|
||||||
h.handleConnection(d);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -757,7 +838,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
// DisableNetwork, set our circuitBuilt flag if not already set
|
// DisableNetwork, set our circuitBuilt flag if not already set
|
||||||
if (status.equals("BUILT") && !state.getAndSetCircuitBuilt(true)) {
|
if (status.equals("BUILT") && !state.getAndSetCircuitBuilt(true)) {
|
||||||
LOG.info("Circuit built");
|
LOG.info("Circuit built");
|
||||||
backoff.reset();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -784,6 +864,9 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
@Override
|
@Override
|
||||||
public void message(String severity, String msg) {
|
public void message(String severity, String msg) {
|
||||||
if (LOG.isLoggable(INFO)) LOG.info(severity + " " + msg);
|
if (LOG.isLoggable(INFO)) LOG.info(severity + " " + msg);
|
||||||
|
if (msg.startsWith("Switching to guard context")) {
|
||||||
|
state.resetNetworkStability();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -810,12 +893,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
if (msg.startsWith("BOOTSTRAP PROGRESS=100")) {
|
if (msg.startsWith("BOOTSTRAP PROGRESS=100")) {
|
||||||
LOG.info("Bootstrapped");
|
LOG.info("Bootstrapped");
|
||||||
state.setBootstrapped();
|
state.setBootstrapped();
|
||||||
backoff.reset();
|
|
||||||
} else if (msg.startsWith("CIRCUIT_ESTABLISHED")) {
|
} else if (msg.startsWith("CIRCUIT_ESTABLISHED")) {
|
||||||
if (!state.getAndSetCircuitBuilt(true)) {
|
if (!state.getAndSetCircuitBuilt(true)) LOG.info("Circuit built");
|
||||||
LOG.info("Circuit built");
|
|
||||||
backoff.reset();
|
|
||||||
}
|
|
||||||
} else if (msg.startsWith("CIRCUIT_NOT_ESTABLISHED")) {
|
} else if (msg.startsWith("CIRCUIT_NOT_ESTABLISHED")) {
|
||||||
if (state.getAndSetCircuitBuilt(false)) {
|
if (state.getAndSetCircuitBuilt(false)) {
|
||||||
LOG.info("Circuit not built");
|
LOG.info("Circuit not built");
|
||||||
@@ -860,7 +939,15 @@ abstract 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 ConnectionClosedEvent) {
|
||||||
|
ConnectionClosedEvent c = (ConnectionClosedEvent) e;
|
||||||
|
if (c.getTransportId().equals(getId())
|
||||||
|
&& !c.isIncoming() && c.isException()) {
|
||||||
|
LOG.info("Outgoing connection closed with exception");
|
||||||
|
// The failure may indicate that the network is unstable
|
||||||
|
state.resetNetworkStability();
|
||||||
|
}
|
||||||
|
} else if (e instanceof SettingsUpdatedEvent) {
|
||||||
SettingsUpdatedEvent s = (SettingsUpdatedEvent) e;
|
SettingsUpdatedEvent s = (SettingsUpdatedEvent) e;
|
||||||
if (s.getNamespace().equals(ID.getString())) {
|
if (s.getNamespace().equals(ID.getString())) {
|
||||||
LOG.info("Tor settings updated");
|
LOG.info("Tor settings updated");
|
||||||
@@ -877,6 +964,18 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void disableAndReenableNetwork() {
|
||||||
|
connectionStatusExecutor.execute(() -> {
|
||||||
|
try {
|
||||||
|
if (state.isTorRunning()) enableNetwork(false);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logException(LOG, WARNING, ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
updateConnectionStatus(networkManager.getNetworkStatus(),
|
||||||
|
batteryManager.isCharging());
|
||||||
|
}
|
||||||
|
|
||||||
private void updateConnectionStatus(NetworkStatus status,
|
private void updateConnectionStatus(NetworkStatus status,
|
||||||
boolean charging) {
|
boolean charging) {
|
||||||
connectionStatusExecutor.execute(() -> {
|
connectionStatusExecutor.execute(() -> {
|
||||||
@@ -1005,6 +1104,13 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
@GuardedBy("this")
|
@GuardedBy("this")
|
||||||
private int reasonsDisabled = 0;
|
private int reasonsDisabled = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of consecutive successful (positive) or failed (negative)
|
||||||
|
* connections to our own hidden service.
|
||||||
|
*/
|
||||||
|
@GuardedBy("this")
|
||||||
|
private int networkStability = 0;
|
||||||
|
|
||||||
@GuardedBy("this")
|
@GuardedBy("this")
|
||||||
@Nullable
|
@Nullable
|
||||||
private ServerSocket serverSocket = null;
|
private ServerSocket serverSocket = null;
|
||||||
@@ -1099,6 +1205,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
}
|
}
|
||||||
logOrConnections();
|
logOrConnections();
|
||||||
if (orConnectionsConnected == 0 && oldConnected != 0) {
|
if (orConnectionsConnected == 0 && oldConnected != 0) {
|
||||||
|
resetNetworkStability();
|
||||||
callback.pluginStateChanged(getState());
|
callback.pluginStateChanged(getState());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1109,5 +1216,43 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
LOG.info(orConnectionsConnected + " OR connections connected");
|
LOG.info(orConnectionsConnected + " OR connections connected");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private synchronized boolean isNetworkStable() {
|
||||||
|
return networkStability >= STABLE_NETWORK_THRESHOLD;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void onStabilityCheckSucceeded() {
|
||||||
|
if (networkStability <= 0) networkStability = 1;
|
||||||
|
else networkStability++;
|
||||||
|
logNetworkStability();
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void onStabilityCheckFailed() {
|
||||||
|
if (networkStability >= 0) networkStability = -1;
|
||||||
|
else networkStability--;
|
||||||
|
if (networkStability <= -BROKEN_NETWORK_THRESHOLD) {
|
||||||
|
LOG.warning("Connection to Tor appears to be broken");
|
||||||
|
resetNetworkStability();
|
||||||
|
disableAndReenableNetwork();
|
||||||
|
} else {
|
||||||
|
logNetworkStability();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void resetNetworkStability() {
|
||||||
|
int old = networkStability;
|
||||||
|
networkStability = 0;
|
||||||
|
logNetworkStability();
|
||||||
|
if (old >= STABLE_NETWORK_THRESHOLD) {
|
||||||
|
callback.pollingIntervalDecreased();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GuardedBy("this")
|
||||||
|
private void logNetworkStability() {
|
||||||
|
if (LOG.isLoggable(INFO)) {
|
||||||
|
LOG.info("Network stability score " + networkStability);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ import org.briarproject.bramble.api.event.EventBus;
|
|||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.network.NetworkManager;
|
import org.briarproject.bramble.api.network.NetworkManager;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.Backoff;
|
|
||||||
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
|
||||||
import org.briarproject.bramble.api.plugin.PluginCallback;
|
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||||
import org.briarproject.bramble.api.plugin.TorConstants;
|
import org.briarproject.bramble.api.plugin.TorConstants;
|
||||||
import org.briarproject.bramble.api.plugin.TorControlPort;
|
import org.briarproject.bramble.api.plugin.TorControlPort;
|
||||||
@@ -41,16 +39,12 @@ abstract class TorPluginFactory implements DuplexPluginFactory {
|
|||||||
|
|
||||||
protected static final int MAX_LATENCY = 30 * 1000; // 30 seconds
|
protected static final int MAX_LATENCY = 30 * 1000; // 30 seconds
|
||||||
protected static final int MAX_IDLE_TIME = 30 * 1000; // 30 seconds
|
protected static final int MAX_IDLE_TIME = 30 * 1000; // 30 seconds
|
||||||
private static final int MIN_POLLING_INTERVAL = 60 * 1000; // 1 minute
|
|
||||||
private static final int MAX_POLLING_INTERVAL = 10 * 60 * 1000; // 10 mins
|
|
||||||
private static final double BACKOFF_BASE = 1.2;
|
|
||||||
|
|
||||||
protected final Executor ioExecutor, wakefulIoExecutor;
|
protected final Executor ioExecutor, wakefulIoExecutor;
|
||||||
protected final NetworkManager networkManager;
|
protected final NetworkManager networkManager;
|
||||||
protected final LocationUtils locationUtils;
|
protected final LocationUtils locationUtils;
|
||||||
protected final EventBus eventBus;
|
protected final EventBus eventBus;
|
||||||
protected final SocketFactory torSocketFactory;
|
protected final SocketFactory torSocketFactory;
|
||||||
protected final BackoffFactory backoffFactory;
|
|
||||||
protected final ResourceProvider resourceProvider;
|
protected final ResourceProvider resourceProvider;
|
||||||
protected final CircumventionProvider circumventionProvider;
|
protected final CircumventionProvider circumventionProvider;
|
||||||
protected final BatteryManager batteryManager;
|
protected final BatteryManager batteryManager;
|
||||||
@@ -66,7 +60,6 @@ abstract class TorPluginFactory implements DuplexPluginFactory {
|
|||||||
LocationUtils locationUtils,
|
LocationUtils locationUtils,
|
||||||
EventBus eventBus,
|
EventBus eventBus,
|
||||||
SocketFactory torSocketFactory,
|
SocketFactory torSocketFactory,
|
||||||
BackoffFactory backoffFactory,
|
|
||||||
ResourceProvider resourceProvider,
|
ResourceProvider resourceProvider,
|
||||||
CircumventionProvider circumventionProvider,
|
CircumventionProvider circumventionProvider,
|
||||||
BatteryManager batteryManager,
|
BatteryManager batteryManager,
|
||||||
@@ -81,7 +74,6 @@ abstract class TorPluginFactory implements DuplexPluginFactory {
|
|||||||
this.locationUtils = locationUtils;
|
this.locationUtils = locationUtils;
|
||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
this.torSocketFactory = torSocketFactory;
|
this.torSocketFactory = torSocketFactory;
|
||||||
this.backoffFactory = backoffFactory;
|
|
||||||
this.resourceProvider = resourceProvider;
|
this.resourceProvider = resourceProvider;
|
||||||
this.circumventionProvider = circumventionProvider;
|
this.circumventionProvider = circumventionProvider;
|
||||||
this.batteryManager = batteryManager;
|
this.batteryManager = batteryManager;
|
||||||
@@ -95,7 +87,7 @@ abstract class TorPluginFactory implements DuplexPluginFactory {
|
|||||||
@Nullable
|
@Nullable
|
||||||
abstract String getArchitectureForTorBinary();
|
abstract String getArchitectureForTorBinary();
|
||||||
|
|
||||||
abstract TorPlugin createPluginInstance(Backoff backoff,
|
abstract TorPlugin createPluginInstance(
|
||||||
TorRendezvousCrypto torRendezvousCrypto, PluginCallback callback,
|
TorRendezvousCrypto torRendezvousCrypto, PluginCallback callback,
|
||||||
String architecture);
|
String architecture);
|
||||||
|
|
||||||
@@ -122,11 +114,9 @@ abstract class TorPluginFactory implements DuplexPluginFactory {
|
|||||||
LOG.info("The selected architecture for Tor is " + architecture);
|
LOG.info("The selected architecture for Tor is " + architecture);
|
||||||
}
|
}
|
||||||
|
|
||||||
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
|
||||||
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
|
||||||
TorRendezvousCrypto torRendezvousCrypto =
|
TorRendezvousCrypto torRendezvousCrypto =
|
||||||
new TorRendezvousCryptoImpl(crypto);
|
new TorRendezvousCryptoImpl(crypto);
|
||||||
TorPlugin plugin = createPluginInstance(backoff, torRendezvousCrypto,
|
TorPlugin plugin = createPluginInstance(torRendezvousCrypto,
|
||||||
callback, architecture);
|
callback, architecture);
|
||||||
eventBus.addListener(plugin);
|
eventBus.addListener(plugin);
|
||||||
return plugin;
|
return plugin;
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ n Bridge obfs4 46.226.107.197:10300 A38FD6BDFD902882F5F5B9B7CCC95602A20B0BC4 cer
|
|||||||
n Bridge obfs4 185.181.11.86:443 A961609729E7FDF520B4E81F1F1B8FA1045285C3 cert=e5faG9Zk4Ni+e7z2YgGfevyKPQlMvkVGi4ublSsHYjaBovKeNXpOhbeFxzbZZoAzxAoGUQ iat-mode=0
|
n Bridge obfs4 185.181.11.86:443 A961609729E7FDF520B4E81F1F1B8FA1045285C3 cert=e5faG9Zk4Ni+e7z2YgGfevyKPQlMvkVGi4ublSsHYjaBovKeNXpOhbeFxzbZZoAzxAoGUQ iat-mode=0
|
||||||
n Bridge obfs4 85.242.211.221:8042 A36A938DD7FDB8BACC846BA326EE0BA0D89A9252 cert=1AN6Pt1eFca3Y/WYD2TGAU3Al9cO4eouXE9SX63s66Z/ks3tVmgQ5GeXi1B5DOvx6Il7Zw iat-mode=0
|
n Bridge obfs4 85.242.211.221:8042 A36A938DD7FDB8BACC846BA326EE0BA0D89A9252 cert=1AN6Pt1eFca3Y/WYD2TGAU3Al9cO4eouXE9SX63s66Z/ks3tVmgQ5GeXi1B5DOvx6Il7Zw iat-mode=0
|
||||||
n Bridge obfs4 74.104.165.202:9002 EF432018A6AA5D970B2F84E39CD30A147030141C cert=PhppfUusY85dHGvWtGTybZ1fED4DtbHmALkNMIOIYrAz1B4xN7/2a5gyiZe1epju1BOHVg iat-mode=0
|
n Bridge obfs4 74.104.165.202:9002 EF432018A6AA5D970B2F84E39CD30A147030141C cert=PhppfUusY85dHGvWtGTybZ1fED4DtbHmALkNMIOIYrAz1B4xN7/2a5gyiZe1epju1BOHVg iat-mode=0
|
||||||
n Bridge obfs4 70.34.242.31:443 7F026956402CDFF4BCBA8E11EE9C50E3FE0A2B72 cert=hP/KU7JATSfWH3HwS5Er/YLT0J+bRO3+s2fWx2yirrgf37EyrWvm/BQshoNje8WfUm6CBw iat-mode=0
|
|
||||||
n Bridge obfs4 93.95.226.151:41185 460B0CFFC0CF1D965F3DE064E08BA1915E7C916A cert=inluPzp5Jp5OzZar1eQb4dcQ/YlAj/v0kHAUCoCr3rmLt03+pVuVTjoH4mRy4+acXpn+Gw iat-mode=0
|
n Bridge obfs4 93.95.226.151:41185 460B0CFFC0CF1D965F3DE064E08BA1915E7C916A cert=inluPzp5Jp5OzZar1eQb4dcQ/YlAj/v0kHAUCoCr3rmLt03+pVuVTjoH4mRy4+acXpn+Gw iat-mode=0
|
||||||
n Bridge obfs4 120.29.217.52:5223 40FE3DB9800272F9CDC76422F8ED7883280EE96D cert=/71PS4l8c/XJ4DIItlH9xMqNvPFg2RUTrHvPlQWh48u5et8h/yyyjCcYphUadDsfBWpaGQ iat-mode=0
|
n Bridge obfs4 120.29.217.52:5223 40FE3DB9800272F9CDC76422F8ED7883280EE96D cert=/71PS4l8c/XJ4DIItlH9xMqNvPFg2RUTrHvPlQWh48u5et8h/yyyjCcYphUadDsfBWpaGQ iat-mode=0
|
||||||
n Bridge obfs4 83.97.179.29:1199 D83068BFAA28E71DB024B786E1E803BE14257127 cert=IduGtt05tM59Xmvo0oVNWgIRgY4OGPJjFP+y2oa6RMDHQBL/GRyFOOgX70iiazNAIJNkPw iat-mode=0
|
n Bridge obfs4 83.97.179.29:1199 D83068BFAA28E71DB024B786E1E803BE14257127 cert=IduGtt05tM59Xmvo0oVNWgIRgY4OGPJjFP+y2oa6RMDHQBL/GRyFOOgX70iiazNAIJNkPw iat-mode=0
|
||||||
@@ -24,7 +23,11 @@ n Bridge obfs4 76.255.201.112:8888 96CF36C2ECCFB7376AB6BE905BECD2C2AE8AEFCD cert
|
|||||||
n Bridge obfs4 65.108.159.114:14174 E1AD374BA9F34BD98862D128AC54D40C7DC138AE cert=YMkxMSBN2OOd99AkJpFaEUyKkdqZgJt9oVJEgO1QnT37n/Vc2yR4nhx4k4VkPLfEP1f4eg iat-mode=0
|
n Bridge obfs4 65.108.159.114:14174 E1AD374BA9F34BD98862D128AC54D40C7DC138AE cert=YMkxMSBN2OOd99AkJpFaEUyKkdqZgJt9oVJEgO1QnT37n/Vc2yR4nhx4k4VkPLfEP1f4eg iat-mode=0
|
||||||
n Bridge obfs4 185.177.207.138:8443 53716FE26F23C8C6A71A2BC5D9D8DC64747278C7 cert=6jcYVilMEzxdsWghSrQFpYYJlkkir/GPIXw/EnddUK3S8ToVpMG8u1SwMOEdEs735RrMHw iat-mode=0
|
n Bridge obfs4 185.177.207.138:8443 53716FE26F23C8C6A71A2BC5D9D8DC64747278C7 cert=6jcYVilMEzxdsWghSrQFpYYJlkkir/GPIXw/EnddUK3S8ToVpMG8u1SwMOEdEs735RrMHw iat-mode=0
|
||||||
n Bridge obfs4 176.123.2.253:1933 B855D141CE6C4DE0F7EA4AAED83EBA8373FA8191 cert=1rOoSaRagc6PPR//paIl+ukv1N+xWKCdBXMFxK0k/moEwH0lk5bURBrUDzIX35fVzaiicQ iat-mode=0
|
n Bridge obfs4 176.123.2.253:1933 B855D141CE6C4DE0F7EA4AAED83EBA8373FA8191 cert=1rOoSaRagc6PPR//paIl+ukv1N+xWKCdBXMFxK0k/moEwH0lk5bURBrUDzIX35fVzaiicQ iat-mode=0
|
||||||
|
n Bridge obfs4 5.252.176.61:9418 3E61130100AD827AB9CB33DAC052D9BC49A39509 cert=/aMyBPnKbQFISithD3i1KHUdpWeMpWG3SvUpq1YWCf2EQohFxQfw+646gW1knm4BI/DLRA iat-mode=0
|
||||||
|
n Bridge obfs4 70.34.213.156:12345 BC1C79ABBAE085D305346E7A2B0E838953B4B4D3 cert=3Sk4uA3/NiAsn4ObOUzjIzARclGmkiEUrku8o8bkq4ZL+dek9uLj/d5LZ5nAXT6L9S0CZA iat-mode=0
|
||||||
|
n Bridge obfs4 202.61.224.111:6902 A4F91299763DB925AE3BD29A0FC1A9821E5D9BAE cert=NBKm2MJ83wMvYShkqpD5RrbDtW5YpIZrFNnMw7Dj1XOM3plU60Bh4eziaQXe8fGtb8ZqKg iat-mode=0
|
||||||
v Bridge 135.181.113.164:54444 74AF4CCA614C454B7D3E81FF8BACD78CEBC7D7DE
|
v Bridge 135.181.113.164:54444 74AF4CCA614C454B7D3E81FF8BACD78CEBC7D7DE
|
||||||
v Bridge 92.243.15.235:9001 477EAD3C04036B48235F1F27FC91420A286A4B7F
|
v Bridge 92.243.15.235:9001 477EAD3C04036B48235F1F27FC91420A286A4B7F
|
||||||
v Bridge 77.96.91.103:443 ED000A75B79A58F1D83A4D1675C2A9395B71BE8E
|
v Bridge 77.96.91.103:443 ED000A75B79A58F1D83A4D1675C2A9395B71BE8E
|
||||||
|
v Bridge 213.108.108.145:17674 A39C0FE47963B6E8CFE9815549864DE544935A31
|
||||||
m Bridge meek_lite 192.0.2.2:2 97700DFE9F483596DDA6264C4D7DF7641E1E39CE url=https://meek.azureedge.net/ front=ajax.aspnetcdn.com
|
m Bridge meek_lite 192.0.2.2:2 97700DFE9F483596DDA6264C4D7DF7641E1E39CE url=https://meek.azureedge.net/ front=ajax.aspnetcdn.com
|
||||||
@@ -3,6 +3,7 @@ package org.briarproject.bramble.db;
|
|||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
import org.briarproject.bramble.util.StringUtils;
|
||||||
|
import org.junit.Before;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
@@ -10,10 +11,18 @@ import java.sql.DriverManager;
|
|||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.isCryptoStrengthUnlimited;
|
||||||
|
import static org.junit.Assume.assumeTrue;
|
||||||
|
|
||||||
public class BasicHyperSqlTest extends BasicDatabaseTest {
|
public class BasicHyperSqlTest extends BasicDatabaseTest {
|
||||||
|
|
||||||
private final SecretKey key = TestUtils.getSecretKey();
|
private final SecretKey key = TestUtils.getSecretKey();
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
assumeTrue(isCryptoStrengthUnlimited());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getBinaryType() {
|
protected String getBinaryType() {
|
||||||
return "BINARY(32)";
|
return "BINARY(32)";
|
||||||
|
|||||||
@@ -3,9 +3,18 @@ package org.briarproject.bramble.db;
|
|||||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||||
import org.briarproject.bramble.api.sync.MessageFactory;
|
import org.briarproject.bramble.api.sync.MessageFactory;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
|
import org.junit.Before;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.isCryptoStrengthUnlimited;
|
||||||
|
import static org.junit.Assume.assumeTrue;
|
||||||
|
|
||||||
public class HyperSqlDatabaseTest extends JdbcDatabaseTest {
|
public class HyperSqlDatabaseTest extends JdbcDatabaseTest {
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
assumeTrue(isCryptoStrengthUnlimited());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected JdbcDatabase createDatabase(DatabaseConfig config,
|
protected JdbcDatabase createDatabase(DatabaseConfig config,
|
||||||
MessageFactory messageFactory, Clock clock) {
|
MessageFactory messageFactory, Clock clock) {
|
||||||
|
|||||||
@@ -1,13 +1,22 @@
|
|||||||
package org.briarproject.bramble.db;
|
package org.briarproject.bramble.db;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.junit.Before;
|
||||||
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.isCryptoStrengthUnlimited;
|
||||||
|
import static org.junit.Assume.assumeTrue;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class HyperSqlMigrationTest extends DatabaseMigrationTest {
|
public class HyperSqlMigrationTest extends DatabaseMigrationTest {
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
assumeTrue(isCryptoStrengthUnlimited());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
Database<Connection> createDatabase(
|
Database<Connection> createDatabase(
|
||||||
List<Migration<Connection>> migrations) {
|
List<Migration<Connection>> migrations) {
|
||||||
|
|||||||
@@ -9,12 +9,16 @@ import org.briarproject.bramble.api.lifecycle.event.LifecycleEvent;
|
|||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
import org.briarproject.bramble.test.DbExpectations;
|
import org.briarproject.bramble.test.DbExpectations;
|
||||||
|
import org.jmock.Expectations;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import static junit.framework.TestCase.assertTrue;
|
import static junit.framework.TestCase.assertTrue;
|
||||||
|
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.RUNNING;
|
||||||
|
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STARTING;
|
||||||
|
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STOPPING;
|
||||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.CLOCK_ERROR;
|
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.CLOCK_ERROR;
|
||||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SUCCESS;
|
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SUCCESS;
|
||||||
import static org.briarproject.bramble.api.system.Clock.MAX_REASONABLE_TIME_MS;
|
import static org.briarproject.bramble.api.system.Clock.MAX_REASONABLE_TIME_MS;
|
||||||
@@ -57,6 +61,7 @@ public class LifecycleManagerImplTest extends BrambleMockTestCase {
|
|||||||
lifecycleManager.registerOpenDatabaseHook(hook);
|
lifecycleManager.registerOpenDatabaseHook(hook);
|
||||||
|
|
||||||
assertEquals(SUCCESS, lifecycleManager.startServices(dbKey));
|
assertEquals(SUCCESS, lifecycleManager.startServices(dbKey));
|
||||||
|
assertEquals(RUNNING, lifecycleManager.getLifecycleState());
|
||||||
assertTrue(called.get());
|
assertTrue(called.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,6 +74,7 @@ public class LifecycleManagerImplTest extends BrambleMockTestCase {
|
|||||||
}});
|
}});
|
||||||
|
|
||||||
assertEquals(CLOCK_ERROR, lifecycleManager.startServices(dbKey));
|
assertEquals(CLOCK_ERROR, lifecycleManager.startServices(dbKey));
|
||||||
|
assertEquals(STARTING, lifecycleManager.getLifecycleState());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -80,5 +86,40 @@ public class LifecycleManagerImplTest extends BrambleMockTestCase {
|
|||||||
}});
|
}});
|
||||||
|
|
||||||
assertEquals(CLOCK_ERROR, lifecycleManager.startServices(dbKey));
|
assertEquals(CLOCK_ERROR, lifecycleManager.startServices(dbKey));
|
||||||
|
assertEquals(STARTING, lifecycleManager.getLifecycleState());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSecondCallToStopServicesReturnsEarly() throws Exception {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
Transaction txn = new Transaction(null, false);
|
||||||
|
|
||||||
|
context.checking(new DbExpectations() {{
|
||||||
|
oneOf(clock).currentTimeMillis();
|
||||||
|
will(returnValue(now));
|
||||||
|
oneOf(db).open(dbKey, lifecycleManager);
|
||||||
|
will(returnValue(false));
|
||||||
|
oneOf(db).transaction(with(false), withDbRunnable(txn));
|
||||||
|
oneOf(db).removeTemporaryMessages(txn);
|
||||||
|
exactly(2).of(eventBus).broadcast(with(any(LifecycleEvent.class)));
|
||||||
|
}});
|
||||||
|
|
||||||
|
assertEquals(SUCCESS, lifecycleManager.startServices(dbKey));
|
||||||
|
assertEquals(RUNNING, lifecycleManager.getLifecycleState());
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(eventBus).broadcast(with(any(LifecycleEvent.class)));
|
||||||
|
oneOf(db).close();
|
||||||
|
}});
|
||||||
|
|
||||||
|
lifecycleManager.stopServices();
|
||||||
|
assertEquals(STOPPING, lifecycleManager.getLifecycleState());
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
|
// Calling stopServices() again should not broadcast another event or
|
||||||
|
// try to close the DB again
|
||||||
|
lifecycleManager.stopServices();
|
||||||
|
assertEquals(STOPPING, lifecycleManager.getLifecycleState());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import org.junit.Test;
|
|||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxConstants.CLIENT_SUPPORTS;
|
||||||
import static org.briarproject.bramble.mailbox.ConnectivityCheckerImpl.CONNECTIVITY_CHECK_FRESHNESS_MS;
|
import static org.briarproject.bramble.mailbox.ConnectivityCheckerImpl.CONNECTIVITY_CHECK_FRESHNESS_MS;
|
||||||
import static org.briarproject.bramble.mailbox.MailboxApi.CLIENT_SUPPORTS;
|
|
||||||
import static org.briarproject.bramble.test.TestUtils.getMailboxProperties;
|
import static org.briarproject.bramble.test.TestUtils.getMailboxProperties;
|
||||||
|
|
||||||
public class ConnectivityCheckerImplTest extends BrambleMockTestCase {
|
public class ConnectivityCheckerImplTest extends BrambleMockTestCase {
|
||||||
@@ -187,6 +187,32 @@ public class ConnectivityCheckerImplTest extends BrambleMockTestCase {
|
|||||||
checker.onConnectivityCheckSucceeded(now);
|
checker.onConnectivityCheckSucceeded(now);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCheckIsCancelledWhenObserverIsRemoved() {
|
||||||
|
ConnectivityCheckerImpl checker = createChecker();
|
||||||
|
|
||||||
|
// When checkConnectivity() is called a check should be started
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(clock).currentTimeMillis();
|
||||||
|
will(returnValue(now));
|
||||||
|
oneOf(mailboxApiCaller).retryWithBackoff(apiCall);
|
||||||
|
will(returnValue(task));
|
||||||
|
}});
|
||||||
|
|
||||||
|
checker.checkConnectivity(properties, observer1);
|
||||||
|
|
||||||
|
// When the observer is removed the check should be cancelled
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(task).cancel();
|
||||||
|
}});
|
||||||
|
|
||||||
|
checker.removeObserver(observer1);
|
||||||
|
|
||||||
|
// If the check runs anyway (cancellation came too late) the observer
|
||||||
|
// should not be called
|
||||||
|
checker.onConnectivityCheckSucceeded(now);
|
||||||
|
}
|
||||||
|
|
||||||
private ConnectivityCheckerImpl createChecker() {
|
private ConnectivityCheckerImpl createChecker() {
|
||||||
|
|
||||||
return new ConnectivityCheckerImpl(clock, mailboxApiCaller) {
|
return new ConnectivityCheckerImpl(clock, mailboxApiCaller) {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import org.junit.Test;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
import static org.briarproject.bramble.mailbox.MailboxApi.CLIENT_SUPPORTS;
|
import static org.briarproject.bramble.api.mailbox.MailboxConstants.CLIENT_SUPPORTS;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getMailboxProperties;
|
import static org.briarproject.bramble.test.TestUtils.getMailboxProperties;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|||||||
@@ -0,0 +1,215 @@
|
|||||||
|
package org.briarproject.bramble.mailbox;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxFileId;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||||
|
import org.briarproject.bramble.mailbox.MailboxApi.MailboxFile;
|
||||||
|
import org.briarproject.bramble.mailbox.MailboxApi.TolerableFailureException;
|
||||||
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
|
import org.briarproject.bramble.test.CaptureArgumentAction;
|
||||||
|
import org.jmock.Expectations;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
import static java.util.Arrays.asList;
|
||||||
|
import static java.util.Collections.emptyList;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxConstants.CLIENT_SUPPORTS;
|
||||||
|
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getMailboxProperties;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
|
||||||
|
public class ContactMailboxDownloadWorkerTest extends BrambleMockTestCase {
|
||||||
|
|
||||||
|
private final ConnectivityChecker connectivityChecker =
|
||||||
|
context.mock(ConnectivityChecker.class);
|
||||||
|
private final TorReachabilityMonitor torReachabilityMonitor =
|
||||||
|
context.mock(TorReachabilityMonitor.class);
|
||||||
|
private final MailboxApiCaller mailboxApiCaller =
|
||||||
|
context.mock(MailboxApiCaller.class);
|
||||||
|
private final MailboxApi mailboxApi = context.mock(MailboxApi.class);
|
||||||
|
private final MailboxFileManager mailboxFileManager =
|
||||||
|
context.mock(MailboxFileManager.class);
|
||||||
|
|
||||||
|
private final MailboxProperties mailboxProperties =
|
||||||
|
getMailboxProperties(false, CLIENT_SUPPORTS);
|
||||||
|
private final long now = System.currentTimeMillis();
|
||||||
|
private final MailboxFile file1 =
|
||||||
|
new MailboxFile(new MailboxFileId(getRandomId()), now - 1);
|
||||||
|
private final MailboxFile file2 =
|
||||||
|
new MailboxFile(new MailboxFileId(getRandomId()), now);
|
||||||
|
private final List<MailboxFile> files = asList(file1, file2);
|
||||||
|
|
||||||
|
private File testDir, tempFile;
|
||||||
|
private ContactMailboxDownloadWorker worker;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
testDir = getTestDirectory();
|
||||||
|
tempFile = new File(testDir, "temp");
|
||||||
|
worker = new ContactMailboxDownloadWorker(connectivityChecker,
|
||||||
|
torReachabilityMonitor, mailboxApiCaller, mailboxApi,
|
||||||
|
mailboxFileManager, mailboxProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
deleteTestDirectory(testDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testChecksConnectivityWhenStartedAndRemovesObserverWhenDestroyed() {
|
||||||
|
// When the worker is started it should start a connectivity check
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(connectivityChecker).checkConnectivity(
|
||||||
|
with(mailboxProperties), with(worker));
|
||||||
|
}});
|
||||||
|
|
||||||
|
worker.start();
|
||||||
|
|
||||||
|
// When the worker is destroyed it should remove the connectivity
|
||||||
|
// and reachability observers
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(connectivityChecker).removeObserver(worker);
|
||||||
|
oneOf(torReachabilityMonitor).removeObserver(worker);
|
||||||
|
}});
|
||||||
|
|
||||||
|
worker.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDownloadsFilesWhenConnectivityCheckSucceeds()
|
||||||
|
throws Exception {
|
||||||
|
// When the worker is started it should start a connectivity check
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(connectivityChecker).checkConnectivity(
|
||||||
|
with(mailboxProperties), with(worker));
|
||||||
|
}});
|
||||||
|
|
||||||
|
worker.start();
|
||||||
|
|
||||||
|
// When the connectivity check succeeds, a list-inbox task should be
|
||||||
|
// started for the first download cycle
|
||||||
|
AtomicReference<ApiCall> listTask = new AtomicReference<>(null);
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(mailboxApiCaller).retryWithBackoff(with(any(ApiCall.class)));
|
||||||
|
will(new CaptureArgumentAction<>(listTask, ApiCall.class, 0));
|
||||||
|
}});
|
||||||
|
|
||||||
|
worker.onConnectivityCheckSucceeded();
|
||||||
|
|
||||||
|
// When the list-inbox tasks runs and finds some files to download,
|
||||||
|
// it should start a download task for the first file
|
||||||
|
AtomicReference<ApiCall> downloadTask = new AtomicReference<>(null);
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(mailboxApi).getFiles(mailboxProperties,
|
||||||
|
requireNonNull(mailboxProperties.getInboxId()));
|
||||||
|
will(returnValue(files));
|
||||||
|
oneOf(mailboxApiCaller).retryWithBackoff(with(any(ApiCall.class)));
|
||||||
|
will(new CaptureArgumentAction<>(downloadTask, ApiCall.class, 0));
|
||||||
|
}});
|
||||||
|
|
||||||
|
assertFalse(listTask.get().callApi());
|
||||||
|
|
||||||
|
// When the first download task runs it should download the file to the
|
||||||
|
// location provided by the file manager and start a delete task
|
||||||
|
AtomicReference<ApiCall> deleteTask = new AtomicReference<>(null);
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(mailboxFileManager).createTempFileForDownload();
|
||||||
|
will(returnValue(tempFile));
|
||||||
|
oneOf(mailboxApi).getFile(mailboxProperties,
|
||||||
|
requireNonNull(mailboxProperties.getInboxId()),
|
||||||
|
file1.name, tempFile);
|
||||||
|
oneOf(mailboxFileManager).handleDownloadedFile(tempFile);
|
||||||
|
oneOf(mailboxApiCaller).retryWithBackoff(with(any(ApiCall.class)));
|
||||||
|
will(new CaptureArgumentAction<>(deleteTask, ApiCall.class, 0));
|
||||||
|
}});
|
||||||
|
|
||||||
|
assertFalse(downloadTask.get().callApi());
|
||||||
|
|
||||||
|
// When the first delete task runs it should delete the file, ignore
|
||||||
|
// the tolerable failure, and start a download task for the next file
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(mailboxApi).deleteFile(mailboxProperties,
|
||||||
|
requireNonNull(mailboxProperties.getInboxId()), file1.name);
|
||||||
|
will(throwException(new TolerableFailureException()));
|
||||||
|
oneOf(mailboxApiCaller).retryWithBackoff(with(any(ApiCall.class)));
|
||||||
|
will(new CaptureArgumentAction<>(downloadTask, ApiCall.class, 0));
|
||||||
|
}});
|
||||||
|
|
||||||
|
assertFalse(deleteTask.get().callApi());
|
||||||
|
|
||||||
|
// When the second download task runs it should download the file to
|
||||||
|
// the location provided by the file manager and start a delete task
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(mailboxFileManager).createTempFileForDownload();
|
||||||
|
will(returnValue(tempFile));
|
||||||
|
oneOf(mailboxApi).getFile(mailboxProperties,
|
||||||
|
requireNonNull(mailboxProperties.getInboxId()),
|
||||||
|
file2.name, tempFile);
|
||||||
|
oneOf(mailboxFileManager).handleDownloadedFile(tempFile);
|
||||||
|
oneOf(mailboxApiCaller).retryWithBackoff(with(any(ApiCall.class)));
|
||||||
|
will(new CaptureArgumentAction<>(deleteTask, ApiCall.class, 0));
|
||||||
|
}});
|
||||||
|
|
||||||
|
assertFalse(downloadTask.get().callApi());
|
||||||
|
|
||||||
|
// When the second delete task runs it should delete the file and
|
||||||
|
// start a list-inbox task to check for files that may have arrived
|
||||||
|
// since the first download cycle started
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(mailboxApi).deleteFile(mailboxProperties,
|
||||||
|
requireNonNull(mailboxProperties.getInboxId()), file2.name);
|
||||||
|
will(throwException(new TolerableFailureException()));
|
||||||
|
oneOf(mailboxApiCaller).retryWithBackoff(with(any(ApiCall.class)));
|
||||||
|
will(new CaptureArgumentAction<>(listTask, ApiCall.class, 0));
|
||||||
|
}});
|
||||||
|
|
||||||
|
assertFalse(deleteTask.get().callApi());
|
||||||
|
|
||||||
|
// When the list-inbox tasks runs and finds no more files to download,
|
||||||
|
// it should add a Tor reachability observer
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(mailboxApi).getFiles(mailboxProperties,
|
||||||
|
requireNonNull(mailboxProperties.getInboxId()));
|
||||||
|
will(returnValue(emptyList()));
|
||||||
|
oneOf(torReachabilityMonitor).addOneShotObserver(worker);
|
||||||
|
}});
|
||||||
|
|
||||||
|
assertFalse(listTask.get().callApi());
|
||||||
|
|
||||||
|
// When the reachability observer is called, a list-inbox task should
|
||||||
|
// be started for the second download cycle
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(mailboxApiCaller).retryWithBackoff(with(any(ApiCall.class)));
|
||||||
|
will(new CaptureArgumentAction<>(listTask, ApiCall.class, 0));
|
||||||
|
}});
|
||||||
|
|
||||||
|
worker.onTorReachable();
|
||||||
|
|
||||||
|
// When the list-inbox tasks runs and finds no more files to download,
|
||||||
|
// it should finish the second download cycle
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(mailboxApi).getFiles(mailboxProperties,
|
||||||
|
requireNonNull(mailboxProperties.getInboxId()));
|
||||||
|
will(returnValue(emptyList()));
|
||||||
|
}});
|
||||||
|
|
||||||
|
assertFalse(listTask.get().callApi());
|
||||||
|
|
||||||
|
// When the worker is destroyed it should remove the connectivity
|
||||||
|
// and reachability observers
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(connectivityChecker).removeObserver(worker);
|
||||||
|
oneOf(torReachabilityMonitor).removeObserver(worker);
|
||||||
|
}});
|
||||||
|
|
||||||
|
worker.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,7 +35,7 @@ import okio.Buffer;
|
|||||||
|
|
||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||||
import static org.briarproject.bramble.mailbox.MailboxApi.CLIENT_SUPPORTS;
|
import static org.briarproject.bramble.api.mailbox.MailboxConstants.CLIENT_SUPPORTS;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getContactId;
|
import static org.briarproject.bramble.test.TestUtils.getContactId;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getMailboxProperties;
|
import static org.briarproject.bramble.test.TestUtils.getMailboxProperties;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||||
|
|||||||
@@ -0,0 +1,194 @@
|
|||||||
|
package org.briarproject.bramble.mailbox;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.connection.ConnectionManager;
|
||||||
|
import org.briarproject.bramble.api.connection.ConnectionManager.TagController;
|
||||||
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState;
|
||||||
|
import org.briarproject.bramble.api.plugin.PluginManager;
|
||||||
|
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.TransportActiveEvent;
|
||||||
|
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
|
||||||
|
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||||
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
|
import org.briarproject.bramble.test.CaptureArgumentAction;
|
||||||
|
import org.briarproject.bramble.test.RunAction;
|
||||||
|
import org.jmock.Expectations;
|
||||||
|
import org.jmock.lib.action.DoAllAction;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.RUNNING;
|
||||||
|
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STOPPING;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxConstants.ID;
|
||||||
|
import static org.briarproject.bramble.api.plugin.file.FileConstants.PROP_PATH;
|
||||||
|
import static org.briarproject.bramble.mailbox.MailboxFileManagerImpl.DOWNLOAD_DIR_NAME;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
public class MailboxFileManagerImplTest extends BrambleMockTestCase {
|
||||||
|
|
||||||
|
private final Executor ioExecutor = context.mock(Executor.class);
|
||||||
|
private final PluginManager pluginManager =
|
||||||
|
context.mock(PluginManager.class);
|
||||||
|
private final ConnectionManager connectionManager =
|
||||||
|
context.mock(ConnectionManager.class);
|
||||||
|
private final LifecycleManager lifecycleManager =
|
||||||
|
context.mock(LifecycleManager.class);
|
||||||
|
private final EventBus eventBus = context.mock(EventBus.class);
|
||||||
|
private final SimplexPlugin plugin = context.mock(SimplexPlugin.class);
|
||||||
|
private final TransportConnectionReader transportConnectionReader =
|
||||||
|
context.mock(TransportConnectionReader.class);
|
||||||
|
|
||||||
|
private File mailboxDir;
|
||||||
|
private MailboxFileManagerImpl manager;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
mailboxDir = getTestDirectory();
|
||||||
|
manager = new MailboxFileManagerImpl(ioExecutor, pluginManager,
|
||||||
|
connectionManager, lifecycleManager, mailboxDir, eventBus);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
deleteTestDirectory(mailboxDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHandlesOrphanedFilesAtStartup() throws Exception {
|
||||||
|
// Create an orphaned file, left behind at the previous shutdown
|
||||||
|
File downloadDir = new File(mailboxDir, DOWNLOAD_DIR_NAME);
|
||||||
|
//noinspection ResultOfMethodCallIgnored
|
||||||
|
downloadDir.mkdirs();
|
||||||
|
File orphan = new File(downloadDir, "orphan");
|
||||||
|
assertTrue(orphan.createNewFile());
|
||||||
|
|
||||||
|
TransportProperties props = new TransportProperties();
|
||||||
|
props.put(PROP_PATH, orphan.getAbsolutePath());
|
||||||
|
|
||||||
|
// When the plugin becomes active the orphaned file should be handled
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(ioExecutor).execute(with(any(Runnable.class)));
|
||||||
|
will(new RunAction());
|
||||||
|
oneOf(eventBus).removeListener(manager);
|
||||||
|
oneOf(pluginManager).getPlugin(ID);
|
||||||
|
will(returnValue(plugin));
|
||||||
|
oneOf(plugin).createReader(props);
|
||||||
|
will(returnValue(transportConnectionReader));
|
||||||
|
oneOf(connectionManager).manageIncomingConnection(with(ID),
|
||||||
|
with(any(TransportConnectionReader.class)),
|
||||||
|
with(any(TagController.class)));
|
||||||
|
}});
|
||||||
|
|
||||||
|
manager.eventOccurred(new TransportActiveEvent(ID));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeletesFileWhenReadSucceeds() throws Exception {
|
||||||
|
expectCheckForOrphans();
|
||||||
|
manager.eventOccurred(new TransportActiveEvent(ID));
|
||||||
|
|
||||||
|
File f = manager.createTempFileForDownload();
|
||||||
|
AtomicReference<TransportConnectionReader> reader =
|
||||||
|
new AtomicReference<>(null);
|
||||||
|
AtomicReference<TagController> controller = new AtomicReference<>(null);
|
||||||
|
|
||||||
|
expectPassFileToConnectionManager(f, reader, controller);
|
||||||
|
manager.handleDownloadedFile(f);
|
||||||
|
|
||||||
|
// The read is successful, so the tag controller should allow the tag
|
||||||
|
// to be marked as read and the reader should delete the file
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(transportConnectionReader).dispose(false, true);
|
||||||
|
}});
|
||||||
|
|
||||||
|
assertTrue(controller.get().shouldMarkTagAsRecognised(false));
|
||||||
|
reader.get().dispose(false, true);
|
||||||
|
assertFalse(f.exists());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeletesFileWhenTagIsNotRecognised() throws Exception {
|
||||||
|
testDeletesFile(false, RUNNING, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeletesFileWhenReadFails() throws Exception {
|
||||||
|
testDeletesFile(true, RUNNING, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDoesNotDeleteFileWhenTagIsNotRecognisedAtShutdown()
|
||||||
|
throws Exception {
|
||||||
|
testDeletesFile(false, STOPPING, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDoesNotDeleteFileWhenReadFailsAtShutdown()
|
||||||
|
throws Exception {
|
||||||
|
testDeletesFile(true, STOPPING, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testDeletesFile(boolean recognised, LifecycleState state,
|
||||||
|
boolean fileExists) throws Exception {
|
||||||
|
expectCheckForOrphans();
|
||||||
|
manager.eventOccurred(new TransportActiveEvent(ID));
|
||||||
|
|
||||||
|
File f = manager.createTempFileForDownload();
|
||||||
|
AtomicReference<TransportConnectionReader> reader =
|
||||||
|
new AtomicReference<>(null);
|
||||||
|
AtomicReference<TagController> controller = new AtomicReference<>(null);
|
||||||
|
|
||||||
|
expectPassFileToConnectionManager(f, reader, controller);
|
||||||
|
manager.handleDownloadedFile(f);
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(transportConnectionReader).dispose(true, recognised);
|
||||||
|
oneOf(lifecycleManager).getLifecycleState();
|
||||||
|
will(returnValue(state));
|
||||||
|
}});
|
||||||
|
|
||||||
|
reader.get().dispose(true, recognised);
|
||||||
|
assertEquals(fileExists, f.exists());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expectCheckForOrphans() {
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(ioExecutor).execute(with(any(Runnable.class)));
|
||||||
|
will(new RunAction());
|
||||||
|
oneOf(eventBus).removeListener(manager);
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expectPassFileToConnectionManager(File f,
|
||||||
|
AtomicReference<TransportConnectionReader> reader,
|
||||||
|
AtomicReference<TagController> controller) {
|
||||||
|
TransportProperties props = new TransportProperties();
|
||||||
|
props.put(PROP_PATH, f.getAbsolutePath());
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(pluginManager).getPlugin(ID);
|
||||||
|
will(returnValue(plugin));
|
||||||
|
oneOf(plugin).createReader(props);
|
||||||
|
will(returnValue(transportConnectionReader));
|
||||||
|
oneOf(connectionManager).manageIncomingConnection(with(ID),
|
||||||
|
with(any(TransportConnectionReader.class)),
|
||||||
|
with(any(TagController.class)));
|
||||||
|
will(new DoAllAction(
|
||||||
|
new CaptureArgumentAction<>(reader,
|
||||||
|
TransportConnectionReader.class, 1),
|
||||||
|
new CaptureArgumentAction<>(controller,
|
||||||
|
TagController.class, 2)
|
||||||
|
));
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -146,12 +146,17 @@ public class MailboxSettingsManagerImplTest extends BrambleMockTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testRecordsSuccess() throws Exception {
|
public void testRecordsSuccess() throws Exception {
|
||||||
Transaction txn = new Transaction(null, false);
|
Transaction txn = new Transaction(null, false);
|
||||||
|
Settings oldSettings = new Settings();
|
||||||
|
oldSettings
|
||||||
|
.putIntArray(SETTINGS_KEY_SERVER_SUPPORTS, serverSupportsInts);
|
||||||
Settings expectedSettings = new Settings();
|
Settings expectedSettings = new Settings();
|
||||||
expectedSettings.putLong(SETTINGS_KEY_LAST_ATTEMPT, now);
|
expectedSettings.putLong(SETTINGS_KEY_LAST_ATTEMPT, now);
|
||||||
expectedSettings.putLong(SETTINGS_KEY_LAST_SUCCESS, now);
|
expectedSettings.putLong(SETTINGS_KEY_LAST_SUCCESS, now);
|
||||||
expectedSettings.putInt(SETTINGS_KEY_ATTEMPTS, 0);
|
expectedSettings.putInt(SETTINGS_KEY_ATTEMPTS, 0);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(settingsManager).getSettings(txn, SETTINGS_NAMESPACE);
|
||||||
|
will(returnValue(oldSettings));
|
||||||
oneOf(settingsManager).mergeSettings(txn, expectedSettings,
|
oneOf(settingsManager).mergeSettings(txn, expectedSettings,
|
||||||
SETTINGS_NAMESPACE);
|
SETTINGS_NAMESPACE);
|
||||||
}});
|
}});
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import org.junit.Test;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
import static org.briarproject.bramble.mailbox.MailboxApi.CLIENT_SUPPORTS;
|
import static org.briarproject.bramble.api.mailbox.MailboxConstants.CLIENT_SUPPORTS;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getMailboxProperties;
|
import static org.briarproject.bramble.test.TestUtils.getMailboxProperties;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|||||||
@@ -0,0 +1,246 @@
|
|||||||
|
package org.briarproject.bramble.mailbox;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.Cancellable;
|
||||||
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
|
import org.briarproject.bramble.api.plugin.Plugin;
|
||||||
|
import org.briarproject.bramble.api.plugin.PluginManager;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.TransportActiveEvent;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent;
|
||||||
|
import org.briarproject.bramble.api.system.TaskScheduler;
|
||||||
|
import org.briarproject.bramble.mailbox.TorReachabilityMonitor.TorReachabilityObserver;
|
||||||
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
|
import org.briarproject.bramble.test.CaptureArgumentAction;
|
||||||
|
import org.jmock.Expectations;
|
||||||
|
import org.jmock.lib.action.DoAllAction;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||||
|
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
|
||||||
|
import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED;
|
||||||
|
import static org.briarproject.bramble.api.plugin.Plugin.State.ENABLING;
|
||||||
|
import static org.briarproject.bramble.api.plugin.TorConstants.ID;
|
||||||
|
import static org.briarproject.bramble.mailbox.TorReachabilityMonitor.REACHABILITY_PERIOD_MS;
|
||||||
|
|
||||||
|
public class TorReachabilityMonitorImplTest extends BrambleMockTestCase {
|
||||||
|
|
||||||
|
private final Executor ioExecutor = context.mock(Executor.class);
|
||||||
|
private final TaskScheduler taskScheduler =
|
||||||
|
context.mock(TaskScheduler.class);
|
||||||
|
private final PluginManager pluginManager =
|
||||||
|
context.mock(PluginManager.class);
|
||||||
|
private final EventBus eventBus = context.mock(EventBus.class);
|
||||||
|
private final Plugin plugin = context.mock(Plugin.class);
|
||||||
|
private final Cancellable scheduledTask = context.mock(Cancellable.class);
|
||||||
|
private final TorReachabilityObserver observer =
|
||||||
|
context.mock(TorReachabilityObserver.class);
|
||||||
|
|
||||||
|
private TorReachabilityMonitorImpl monitor;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
monitor = new TorReachabilityMonitorImpl(ioExecutor, taskScheduler,
|
||||||
|
pluginManager, eventBus);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSchedulesTaskWhenStartedIfTorIsActive() {
|
||||||
|
// Starting the monitor should schedule a task
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(eventBus).addListener(monitor);
|
||||||
|
oneOf(pluginManager).getPlugin(ID);
|
||||||
|
will(returnValue(plugin));
|
||||||
|
oneOf(plugin).getState();
|
||||||
|
will(returnValue(ACTIVE));
|
||||||
|
oneOf(taskScheduler).schedule(with(any(Runnable.class)),
|
||||||
|
with(ioExecutor), with(REACHABILITY_PERIOD_MS),
|
||||||
|
with(MILLISECONDS));
|
||||||
|
will(returnValue(scheduledTask));
|
||||||
|
}});
|
||||||
|
|
||||||
|
monitor.start();
|
||||||
|
|
||||||
|
// If Tor has only just become active and the TransportActiveEvent
|
||||||
|
// arrives after the task has already been scheduled, a second task
|
||||||
|
// should not be scheduled
|
||||||
|
monitor.eventOccurred(new TransportActiveEvent(ID));
|
||||||
|
|
||||||
|
// Destroying the monitor should cancel the task
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(eventBus).removeListener(monitor);
|
||||||
|
oneOf(scheduledTask).cancel();
|
||||||
|
}});
|
||||||
|
|
||||||
|
monitor.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSchedulesTaskWhenTorBecomesActive() {
|
||||||
|
// Starting the monitor should not schedule a task as Tor is inactive
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(eventBus).addListener(monitor);
|
||||||
|
oneOf(pluginManager).getPlugin(ID);
|
||||||
|
will(returnValue(plugin));
|
||||||
|
oneOf(plugin).getState();
|
||||||
|
will(returnValue(ENABLING));
|
||||||
|
}});
|
||||||
|
|
||||||
|
monitor.start();
|
||||||
|
|
||||||
|
// When Tor becomes active, a task should be scheduled
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(taskScheduler).schedule(with(any(Runnable.class)),
|
||||||
|
with(ioExecutor), with(REACHABILITY_PERIOD_MS),
|
||||||
|
with(MILLISECONDS));
|
||||||
|
will(returnValue(scheduledTask));
|
||||||
|
}});
|
||||||
|
|
||||||
|
monitor.eventOccurred(new TransportActiveEvent(ID));
|
||||||
|
|
||||||
|
// Destroying the monitor should cancel the task
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(eventBus).removeListener(monitor);
|
||||||
|
oneOf(scheduledTask).cancel();
|
||||||
|
}});
|
||||||
|
|
||||||
|
monitor.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCancelsTaskWhenTorBecomesInactive() {
|
||||||
|
// Starting the monitor should schedule a task
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(eventBus).addListener(monitor);
|
||||||
|
oneOf(pluginManager).getPlugin(ID);
|
||||||
|
will(returnValue(plugin));
|
||||||
|
oneOf(plugin).getState();
|
||||||
|
will(returnValue(ACTIVE));
|
||||||
|
oneOf(taskScheduler).schedule(with(any(Runnable.class)),
|
||||||
|
with(ioExecutor), with(REACHABILITY_PERIOD_MS),
|
||||||
|
with(MILLISECONDS));
|
||||||
|
will(returnValue(scheduledTask));
|
||||||
|
}});
|
||||||
|
|
||||||
|
monitor.start();
|
||||||
|
|
||||||
|
// When Tor becomes inactive, the task should be cancelled
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(scheduledTask).cancel();
|
||||||
|
}});
|
||||||
|
|
||||||
|
monitor.eventOccurred(new TransportInactiveEvent(ID));
|
||||||
|
|
||||||
|
// Destroying the monitor should not affect the task, which has
|
||||||
|
// already been cancelled
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(eventBus).removeListener(monitor);
|
||||||
|
}});
|
||||||
|
|
||||||
|
monitor.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testObserverRegisteredBeforeTorBecomesActiveIsCalled() {
|
||||||
|
// Starting the monitor should not schedule a task as Tor is inactive
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(eventBus).addListener(monitor);
|
||||||
|
oneOf(pluginManager).getPlugin(ID);
|
||||||
|
will(returnValue(plugin));
|
||||||
|
oneOf(plugin).getState();
|
||||||
|
will(returnValue(DISABLED));
|
||||||
|
}});
|
||||||
|
|
||||||
|
monitor.start();
|
||||||
|
|
||||||
|
// Register an observer
|
||||||
|
monitor.addOneShotObserver(observer);
|
||||||
|
|
||||||
|
// When Tor becomes active, a task should be scheduled
|
||||||
|
AtomicReference<Runnable> runnable = new AtomicReference<>(null);
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(taskScheduler).schedule(with(any(Runnable.class)),
|
||||||
|
with(ioExecutor), with(REACHABILITY_PERIOD_MS),
|
||||||
|
with(MILLISECONDS));
|
||||||
|
will(new DoAllAction(
|
||||||
|
new CaptureArgumentAction<>(runnable, Runnable.class, 0),
|
||||||
|
returnValue(scheduledTask)
|
||||||
|
));
|
||||||
|
}});
|
||||||
|
|
||||||
|
monitor.eventOccurred(new TransportActiveEvent(ID));
|
||||||
|
|
||||||
|
// When the task runs, the observer should be called
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(observer).onTorReachable();
|
||||||
|
}});
|
||||||
|
|
||||||
|
runnable.get().run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testObserverRegisteredBeforeTorBecomesReachableIsCalled() {
|
||||||
|
// Starting the monitor should schedule a task
|
||||||
|
AtomicReference<Runnable> runnable = new AtomicReference<>(null);
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(eventBus).addListener(monitor);
|
||||||
|
oneOf(pluginManager).getPlugin(ID);
|
||||||
|
will(returnValue(plugin));
|
||||||
|
oneOf(plugin).getState();
|
||||||
|
will(returnValue(ACTIVE));
|
||||||
|
oneOf(taskScheduler).schedule(with(any(Runnable.class)),
|
||||||
|
with(ioExecutor), with(REACHABILITY_PERIOD_MS),
|
||||||
|
with(MILLISECONDS));
|
||||||
|
will(new DoAllAction(
|
||||||
|
new CaptureArgumentAction<>(runnable, Runnable.class, 0),
|
||||||
|
returnValue(scheduledTask)
|
||||||
|
));
|
||||||
|
}});
|
||||||
|
|
||||||
|
monitor.start();
|
||||||
|
|
||||||
|
// Register an observer
|
||||||
|
monitor.addOneShotObserver(observer);
|
||||||
|
|
||||||
|
// When the task runs, the observer should be called
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(observer).onTorReachable();
|
||||||
|
}});
|
||||||
|
|
||||||
|
runnable.get().run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testObserverRegisteredAfterTorBecomesReachableIsCalled() {
|
||||||
|
// Starting the monitor should schedule a task
|
||||||
|
AtomicReference<Runnable> runnable = new AtomicReference<>(null);
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(eventBus).addListener(monitor);
|
||||||
|
oneOf(pluginManager).getPlugin(ID);
|
||||||
|
will(returnValue(plugin));
|
||||||
|
oneOf(plugin).getState();
|
||||||
|
will(returnValue(ACTIVE));
|
||||||
|
oneOf(taskScheduler).schedule(with(any(Runnable.class)),
|
||||||
|
with(ioExecutor), with(REACHABILITY_PERIOD_MS),
|
||||||
|
with(MILLISECONDS));
|
||||||
|
will(new DoAllAction(
|
||||||
|
new CaptureArgumentAction<>(runnable, Runnable.class, 0),
|
||||||
|
returnValue(scheduledTask)
|
||||||
|
));
|
||||||
|
}});
|
||||||
|
|
||||||
|
monitor.start();
|
||||||
|
|
||||||
|
// When the task runs, no observers have been registered yet
|
||||||
|
runnable.get().run();
|
||||||
|
|
||||||
|
// When an observer is registered, it should be called immediately
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(observer).onTorReachable();
|
||||||
|
}});
|
||||||
|
|
||||||
|
monitor.addOneShotObserver(observer);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,45 +1,69 @@
|
|||||||
package org.briarproject.bramble.plugin;
|
package org.briarproject.bramble.plugin;
|
||||||
|
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
|
import org.briarproject.bramble.api.plugin.event.PollingIntervalDecreasedEvent;
|
||||||
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
|
import org.jmock.Expectations;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getTransportId;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public class BackoffImplTest extends BrambleTestCase {
|
public class BackoffImplTest extends BrambleMockTestCase {
|
||||||
|
|
||||||
|
private final EventBus eventBus = context.mock(EventBus.class);
|
||||||
|
|
||||||
|
private final TransportId transportId = getTransportId();
|
||||||
private static final int MIN_INTERVAL = 60 * 1000;
|
private static final int MIN_INTERVAL = 60 * 1000;
|
||||||
private static final int MAX_INTERVAL = 60 * 60 * 1000;
|
private static final int MAX_INTERVAL = 60 * 60 * 1000;
|
||||||
private static final double BASE = 1.2;
|
private static final double BASE = 1.2;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPollingIntervalStartsAtMinimum() {
|
public void testPollingIntervalStartsAtMinimum() {
|
||||||
BackoffImpl b = new BackoffImpl(MIN_INTERVAL, MAX_INTERVAL, BASE);
|
BackoffImpl b = new BackoffImpl(eventBus, transportId,
|
||||||
|
MIN_INTERVAL, MAX_INTERVAL, BASE);
|
||||||
assertEquals(MIN_INTERVAL, b.getPollingInterval());
|
assertEquals(MIN_INTERVAL, b.getPollingInterval());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIncrementIncreasesPollingInterval() {
|
public void testIncrementMethodIncreasesPollingInterval() {
|
||||||
BackoffImpl b = new BackoffImpl(MIN_INTERVAL, MAX_INTERVAL, BASE);
|
BackoffImpl b = new BackoffImpl(eventBus, transportId,
|
||||||
|
MIN_INTERVAL, MAX_INTERVAL, BASE);
|
||||||
b.increment();
|
b.increment();
|
||||||
assertTrue(b.getPollingInterval() > MIN_INTERVAL);
|
assertTrue(b.getPollingInterval() > MIN_INTERVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testResetResetsPollingInterval() {
|
public void testResetMethodResetsPollingInterval() {
|
||||||
BackoffImpl b = new BackoffImpl(MIN_INTERVAL, MAX_INTERVAL, BASE);
|
BackoffImpl b = new BackoffImpl(eventBus, transportId,
|
||||||
b.increment();
|
MIN_INTERVAL, MAX_INTERVAL, BASE);
|
||||||
b.increment();
|
b.increment();
|
||||||
|
assertTrue(b.getPollingInterval() > MIN_INTERVAL);
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(eventBus).broadcast(with(any(
|
||||||
|
PollingIntervalDecreasedEvent.class)));
|
||||||
|
}});
|
||||||
|
|
||||||
|
b.reset();
|
||||||
|
assertEquals(MIN_INTERVAL, b.getPollingInterval());
|
||||||
|
context.assertIsSatisfied();
|
||||||
|
|
||||||
|
// Resetting again should not broadcast another event
|
||||||
b.reset();
|
b.reset();
|
||||||
assertEquals(MIN_INTERVAL, b.getPollingInterval());
|
assertEquals(MIN_INTERVAL, b.getPollingInterval());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBaseAffectsBackoffSpeed() {
|
public void testBaseAffectsBackoffSpeed() {
|
||||||
BackoffImpl b = new BackoffImpl(MIN_INTERVAL, MAX_INTERVAL, BASE);
|
BackoffImpl b = new BackoffImpl(eventBus, transportId,
|
||||||
|
MIN_INTERVAL, MAX_INTERVAL, BASE);
|
||||||
b.increment();
|
b.increment();
|
||||||
int interval = b.getPollingInterval();
|
int interval = b.getPollingInterval();
|
||||||
BackoffImpl b1 = new BackoffImpl(MIN_INTERVAL, MAX_INTERVAL, BASE * 2);
|
BackoffImpl b1 = new BackoffImpl(eventBus, transportId, MIN_INTERVAL,
|
||||||
|
MAX_INTERVAL, BASE * 2);
|
||||||
b1.increment();
|
b1.increment();
|
||||||
int interval1 = b1.getPollingInterval();
|
int interval1 = b1.getPollingInterval();
|
||||||
assertTrue(interval < interval1);
|
assertTrue(interval < interval1);
|
||||||
@@ -47,15 +71,16 @@ public class BackoffImplTest extends BrambleTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIntervalDoesNotExceedMaxInterval() {
|
public void testIntervalDoesNotExceedMaxInterval() {
|
||||||
BackoffImpl b = new BackoffImpl(MIN_INTERVAL, MAX_INTERVAL, BASE);
|
BackoffImpl b = new BackoffImpl(eventBus, transportId,
|
||||||
|
MIN_INTERVAL, MAX_INTERVAL, BASE);
|
||||||
for (int i = 0; i < 100; i++) b.increment();
|
for (int i = 0; i < 100; i++) b.increment();
|
||||||
assertEquals(MAX_INTERVAL, b.getPollingInterval());
|
assertEquals(MAX_INTERVAL, b.getPollingInterval());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIntervalDoesNotExceedMaxIntervalWithInfiniteMultiplier() {
|
public void testIntervalDoesNotExceedMaxIntervalWithInfiniteMultiplier() {
|
||||||
BackoffImpl b = new BackoffImpl(MIN_INTERVAL, MAX_INTERVAL,
|
BackoffImpl b = new BackoffImpl(eventBus, transportId,
|
||||||
Double.POSITIVE_INFINITY);
|
MIN_INTERVAL, MAX_INTERVAL, Double.POSITIVE_INFINITY);
|
||||||
b.increment();
|
b.increment();
|
||||||
assertEquals(MAX_INTERVAL, b.getPollingInterval());
|
assertEquals(MAX_INTERVAL, b.getPollingInterval());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import org.briarproject.bramble.api.plugin.TransportId;
|
|||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||||
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
|
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
|
||||||
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
|
import org.briarproject.bramble.api.plugin.event.PollingIntervalDecreasedEvent;
|
||||||
import org.briarproject.bramble.api.plugin.event.TransportActiveEvent;
|
import org.briarproject.bramble.api.plugin.event.TransportActiveEvent;
|
||||||
import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent;
|
import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent;
|
||||||
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
|
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
|
||||||
@@ -35,7 +35,6 @@ import java.util.concurrent.Executor;
|
|||||||
|
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
import static java.util.Collections.singletonList;
|
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||||
import static org.briarproject.bramble.test.CollectionMatcher.collectionOf;
|
import static org.briarproject.bramble.test.CollectionMatcher.collectionOf;
|
||||||
@@ -217,7 +216,7 @@ public class PollerImplTest extends BrambleMockTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRescheduleOnConnectionOpened() {
|
public void testRescheduleOnPollingIntervalDecreased() {
|
||||||
Plugin plugin = context.mock(Plugin.class);
|
Plugin plugin = context.mock(Plugin.class);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
@@ -240,8 +239,7 @@ public class PollerImplTest extends BrambleMockTestCase {
|
|||||||
will(returnValue(cancellable));
|
will(returnValue(cancellable));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
poller.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
|
poller.eventOccurred(new PollingIntervalDecreasedEvent(transportId));
|
||||||
false));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -281,10 +279,8 @@ public class PollerImplTest extends BrambleMockTestCase {
|
|||||||
will(returnValue(now + 1));
|
will(returnValue(now + 1));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
poller.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
|
poller.eventOccurred(new PollingIntervalDecreasedEvent(transportId));
|
||||||
false));
|
poller.eventOccurred(new PollingIntervalDecreasedEvent(transportId));
|
||||||
poller.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
|
|
||||||
false));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -328,10 +324,8 @@ public class PollerImplTest extends BrambleMockTestCase {
|
|||||||
with(MILLISECONDS));
|
with(MILLISECONDS));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
poller.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
|
poller.eventOccurred(new PollingIntervalDecreasedEvent(transportId));
|
||||||
false));
|
poller.eventOccurred(new PollingIntervalDecreasedEvent(transportId));
|
||||||
poller.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
|
|
||||||
false));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -378,48 +372,6 @@ public class PollerImplTest extends BrambleMockTestCase {
|
|||||||
poller.eventOccurred(new TransportActiveEvent(transportId));
|
poller.eventOccurred(new TransportActiveEvent(transportId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDoesNotPollIfAllContactsAreConnected() throws Exception {
|
|
||||||
DuplexPlugin plugin = context.mock(DuplexPlugin.class);
|
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
allowing(plugin).getId();
|
|
||||||
will(returnValue(transportId));
|
|
||||||
// Get the plugin
|
|
||||||
oneOf(pluginManager).getPlugin(transportId);
|
|
||||||
will(returnValue(plugin));
|
|
||||||
// The plugin supports polling
|
|
||||||
oneOf(plugin).shouldPoll();
|
|
||||||
will(returnValue(true));
|
|
||||||
// Schedule a polling task immediately
|
|
||||||
oneOf(clock).currentTimeMillis();
|
|
||||||
will(returnValue(now));
|
|
||||||
oneOf(scheduler).schedule(with(any(Runnable.class)),
|
|
||||||
with(ioExecutor), with(0L), with(MILLISECONDS));
|
|
||||||
will(returnValue(cancellable));
|
|
||||||
will(new RunAction());
|
|
||||||
// Running the polling task schedules the next polling task
|
|
||||||
oneOf(plugin).getPollingInterval();
|
|
||||||
will(returnValue(pollingInterval));
|
|
||||||
oneOf(random).nextDouble();
|
|
||||||
will(returnValue(0.5));
|
|
||||||
oneOf(clock).currentTimeMillis();
|
|
||||||
will(returnValue(now));
|
|
||||||
oneOf(scheduler).schedule(with(any(Runnable.class)),
|
|
||||||
with(ioExecutor), with((long) (pollingInterval * 0.5)),
|
|
||||||
with(MILLISECONDS));
|
|
||||||
will(returnValue(cancellable));
|
|
||||||
// Get the transport properties and connected contacts
|
|
||||||
oneOf(transportPropertyManager).getRemoteProperties(transportId);
|
|
||||||
will(returnValue(singletonMap(contactId, properties)));
|
|
||||||
oneOf(connectionRegistry).getConnectedOrBetterContacts(transportId);
|
|
||||||
will(returnValue(singletonList(contactId)));
|
|
||||||
// All contacts are connected, so don't poll the plugin
|
|
||||||
}});
|
|
||||||
|
|
||||||
poller.eventOccurred(new TransportActiveEvent(transportId));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCancelsPollingOnTransportDeactivated() {
|
public void testCancelsPollingOnTransportDeactivated() {
|
||||||
Plugin plugin = context.mock(Plugin.class);
|
Plugin plugin = context.mock(Plugin.class);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import org.briarproject.bramble.system.DefaultWakefulIoExecutorModule;
|
|||||||
import org.briarproject.bramble.system.TimeTravelModule;
|
import org.briarproject.bramble.system.TimeTravelModule;
|
||||||
import org.briarproject.bramble.test.TestDatabaseConfigModule;
|
import org.briarproject.bramble.test.TestDatabaseConfigModule;
|
||||||
import org.briarproject.bramble.test.TestFeatureFlagModule;
|
import org.briarproject.bramble.test.TestFeatureFlagModule;
|
||||||
|
import org.briarproject.bramble.test.TestMailboxDirectoryModule;
|
||||||
import org.briarproject.bramble.test.TestSecureRandomModule;
|
import org.briarproject.bramble.test.TestSecureRandomModule;
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
@@ -27,6 +28,7 @@ import dagger.Component;
|
|||||||
DefaultWakefulIoExecutorModule.class,
|
DefaultWakefulIoExecutorModule.class,
|
||||||
TestDatabaseConfigModule.class,
|
TestDatabaseConfigModule.class,
|
||||||
TestFeatureFlagModule.class,
|
TestFeatureFlagModule.class,
|
||||||
|
TestMailboxDirectoryModule.class,
|
||||||
RemovableDriveIntegrationTestModule.class,
|
RemovableDriveIntegrationTestModule.class,
|
||||||
RemovableDriveModule.class,
|
RemovableDriveModule.class,
|
||||||
TestSecureRandomModule.class,
|
TestSecureRandomModule.class,
|
||||||
|
|||||||
@@ -342,6 +342,10 @@ public class LanTcpPluginTest extends BrambleTestCase {
|
|||||||
public void pluginStateChanged(State newState) {
|
public void pluginStateChanged(State newState) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void pollingIntervalDecreased() {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleConnection(DuplexTransportConnection d) {
|
public void handleConnection(DuplexTransportConnection d) {
|
||||||
connectionsLatch.countDown();
|
connectionsLatch.countDown();
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import java.util.HashSet;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static java.util.Collections.singletonList;
|
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BLOCKED;
|
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BLOCKED;
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BRIDGES;
|
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BRIDGES;
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.DEFAULT_OBFS4;
|
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.DEFAULT_OBFS4;
|
||||||
@@ -15,7 +14,7 @@ import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeTy
|
|||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.NON_DEFAULT_OBFS4;
|
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.NON_DEFAULT_OBFS4;
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.VANILLA;
|
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.VANILLA;
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.DEFAULT_BRIDGES;
|
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.DEFAULT_BRIDGES;
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.MEEK_BRIDGES;
|
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.DPI_BRIDGES;
|
||||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.NON_DEFAULT_BRIDGES;
|
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.NON_DEFAULT_BRIDGES;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
@@ -32,18 +31,18 @@ public class CircumventionProviderTest extends BrambleTestCase {
|
|||||||
Set<String> defaultBridges = new HashSet<>(asList(DEFAULT_BRIDGES));
|
Set<String> defaultBridges = new HashSet<>(asList(DEFAULT_BRIDGES));
|
||||||
Set<String> nonDefaultBridges =
|
Set<String> nonDefaultBridges =
|
||||||
new HashSet<>(asList(NON_DEFAULT_BRIDGES));
|
new HashSet<>(asList(NON_DEFAULT_BRIDGES));
|
||||||
Set<String> meekBridges = new HashSet<>(asList(MEEK_BRIDGES));
|
Set<String> dpiBridges = new HashSet<>(asList(DPI_BRIDGES));
|
||||||
// BRIDGES should be a subset of BLOCKED
|
// BRIDGES should be a subset of BLOCKED
|
||||||
assertTrue(blocked.containsAll(bridges));
|
assertTrue(blocked.containsAll(bridges));
|
||||||
// BRIDGES should be the union of the bridge type sets
|
// BRIDGES should be the union of the bridge type sets
|
||||||
Set<String> union = new HashSet<>(defaultBridges);
|
Set<String> union = new HashSet<>(defaultBridges);
|
||||||
union.addAll(nonDefaultBridges);
|
union.addAll(nonDefaultBridges);
|
||||||
union.addAll(meekBridges);
|
union.addAll(dpiBridges);
|
||||||
assertEquals(bridges, union);
|
assertEquals(bridges, union);
|
||||||
// The bridge type sets should not overlap
|
// The bridge type sets should not overlap
|
||||||
assertEmptyIntersection(defaultBridges, nonDefaultBridges);
|
assertEmptyIntersection(defaultBridges, nonDefaultBridges);
|
||||||
assertEmptyIntersection(defaultBridges, meekBridges);
|
assertEmptyIntersection(defaultBridges, dpiBridges);
|
||||||
assertEmptyIntersection(nonDefaultBridges, meekBridges);
|
assertEmptyIntersection(nonDefaultBridges, dpiBridges);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -56,8 +55,8 @@ public class CircumventionProviderTest extends BrambleTestCase {
|
|||||||
assertEquals(asList(NON_DEFAULT_OBFS4, VANILLA),
|
assertEquals(asList(NON_DEFAULT_OBFS4, VANILLA),
|
||||||
provider.getSuitableBridgeTypes(country));
|
provider.getSuitableBridgeTypes(country));
|
||||||
}
|
}
|
||||||
for (String country : MEEK_BRIDGES) {
|
for (String country : DPI_BRIDGES) {
|
||||||
assertEquals(singletonList(MEEK),
|
assertEquals(asList(NON_DEFAULT_OBFS4, MEEK),
|
||||||
provider.getSuitableBridgeTypes(country));
|
provider.getSuitableBridgeTypes(country));
|
||||||
}
|
}
|
||||||
assertEquals(asList(DEFAULT_OBFS4, VANILLA),
|
assertEquals(asList(DEFAULT_OBFS4, VANILLA),
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import dagger.Module;
|
|||||||
DefaultWakefulIoExecutorModule.class,
|
DefaultWakefulIoExecutorModule.class,
|
||||||
TestDatabaseConfigModule.class,
|
TestDatabaseConfigModule.class,
|
||||||
TestFeatureFlagModule.class,
|
TestFeatureFlagModule.class,
|
||||||
|
TestMailboxDirectoryModule.class,
|
||||||
TestPluginConfigModule.class,
|
TestPluginConfigModule.class,
|
||||||
TestSecureRandomModule.class,
|
TestSecureRandomModule.class,
|
||||||
TimeTravelModule.class
|
TimeTravelModule.class
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package org.briarproject.bramble.test;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxDirectory;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.Provides;
|
||||||
|
|
||||||
|
@Module
|
||||||
|
public class TestMailboxDirectoryModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@MailboxDirectory
|
||||||
|
File provideMailboxDirectory() {
|
||||||
|
return new File("mailbox");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,7 +18,9 @@ dependencies {
|
|||||||
implementation "net.java.dev.jna:jna:$jna_version"
|
implementation "net.java.dev.jna:jna:$jna_version"
|
||||||
implementation "net.java.dev.jna:jna-platform:$jna_version"
|
implementation "net.java.dev.jna:jna-platform:$jna_version"
|
||||||
tor "org.briarproject:tor-linux:$tor_version"
|
tor "org.briarproject:tor-linux:$tor_version"
|
||||||
|
tor "org.briarproject:tor-windows:$tor_version"
|
||||||
tor "org.briarproject:obfs4proxy-linux:$obfs4proxy_version"
|
tor "org.briarproject:obfs4proxy-linux:$obfs4proxy_version"
|
||||||
|
tor "org.briarproject:obfs4proxy-windows:$obfs4proxy_version"
|
||||||
|
|
||||||
annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
|
annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
|
||||||
|
|
||||||
|
|||||||
@@ -69,8 +69,8 @@ public class JavaBluetoothPluginFactory implements DuplexPluginFactory {
|
|||||||
BluetoothConnectionFactory<StreamConnection> connectionFactory =
|
BluetoothConnectionFactory<StreamConnection> connectionFactory =
|
||||||
new JavaBluetoothConnectionFactory(connectionLimiter,
|
new JavaBluetoothConnectionFactory(connectionLimiter,
|
||||||
timeoutMonitor);
|
timeoutMonitor);
|
||||||
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
|
Backoff backoff = backoffFactory.createBackoff(eventBus, ID,
|
||||||
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
MIN_POLLING_INTERVAL, MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
||||||
JavaBluetoothPlugin plugin = new JavaBluetoothPlugin(connectionLimiter,
|
JavaBluetoothPlugin plugin = new JavaBluetoothPlugin(connectionLimiter,
|
||||||
connectionFactory, ioExecutor, wakefulIoExecutor, secureRandom,
|
connectionFactory, ioExecutor, wakefulIoExecutor, secureRandom,
|
||||||
backoff, callback, MAX_LATENCY, MAX_IDLE_TIME);
|
backoff, callback, MAX_LATENCY, MAX_IDLE_TIME);
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package org.briarproject.bramble.plugin.tor;
|
|||||||
import org.briarproject.bramble.api.battery.BatteryManager;
|
import org.briarproject.bramble.api.battery.BatteryManager;
|
||||||
import org.briarproject.bramble.api.network.NetworkManager;
|
import org.briarproject.bramble.api.network.NetworkManager;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.Backoff;
|
|
||||||
import org.briarproject.bramble.api.plugin.PluginCallback;
|
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
import org.briarproject.bramble.api.system.LocationUtils;
|
||||||
@@ -29,7 +28,6 @@ abstract class JavaTorPlugin extends TorPlugin {
|
|||||||
ResourceProvider resourceProvider,
|
ResourceProvider resourceProvider,
|
||||||
CircumventionProvider circumventionProvider,
|
CircumventionProvider circumventionProvider,
|
||||||
BatteryManager batteryManager,
|
BatteryManager batteryManager,
|
||||||
Backoff backoff,
|
|
||||||
TorRendezvousCrypto torRendezvousCrypto,
|
TorRendezvousCrypto torRendezvousCrypto,
|
||||||
PluginCallback callback,
|
PluginCallback callback,
|
||||||
String architecture,
|
String architecture,
|
||||||
@@ -40,7 +38,7 @@ abstract class JavaTorPlugin extends TorPlugin {
|
|||||||
int torControlPort) {
|
int torControlPort) {
|
||||||
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
||||||
torSocketFactory, clock, resourceProvider,
|
torSocketFactory, clock, resourceProvider,
|
||||||
circumventionProvider, batteryManager, backoff,
|
circumventionProvider, batteryManager,
|
||||||
torRendezvousCrypto, callback, architecture,
|
torRendezvousCrypto, callback, architecture,
|
||||||
maxLatency, maxIdleTime, torDirectory, torSocksPort,
|
maxLatency, maxIdleTime, torDirectory, torSocksPort,
|
||||||
torControlPort);
|
torControlPort);
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import com.sun.jna.Native;
|
|||||||
import org.briarproject.bramble.api.battery.BatteryManager;
|
import org.briarproject.bramble.api.battery.BatteryManager;
|
||||||
import org.briarproject.bramble.api.network.NetworkManager;
|
import org.briarproject.bramble.api.network.NetworkManager;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.Backoff;
|
|
||||||
import org.briarproject.bramble.api.plugin.PluginCallback;
|
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
import org.briarproject.bramble.api.system.LocationUtils;
|
||||||
@@ -29,7 +28,6 @@ class UnixTorPlugin extends JavaTorPlugin {
|
|||||||
ResourceProvider resourceProvider,
|
ResourceProvider resourceProvider,
|
||||||
CircumventionProvider circumventionProvider,
|
CircumventionProvider circumventionProvider,
|
||||||
BatteryManager batteryManager,
|
BatteryManager batteryManager,
|
||||||
Backoff backoff,
|
|
||||||
TorRendezvousCrypto torRendezvousCrypto,
|
TorRendezvousCrypto torRendezvousCrypto,
|
||||||
PluginCallback callback,
|
PluginCallback callback,
|
||||||
String architecture,
|
String architecture,
|
||||||
@@ -40,7 +38,7 @@ class UnixTorPlugin extends JavaTorPlugin {
|
|||||||
int torControlPort) {
|
int torControlPort) {
|
||||||
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
||||||
torSocketFactory, clock, resourceProvider,
|
torSocketFactory, clock, resourceProvider,
|
||||||
circumventionProvider, batteryManager, backoff,
|
circumventionProvider, batteryManager,
|
||||||
torRendezvousCrypto, callback, architecture,
|
torRendezvousCrypto, callback, architecture,
|
||||||
maxLatency, maxIdleTime, torDirectory, torSocksPort,
|
maxLatency, maxIdleTime, torDirectory, torSocksPort,
|
||||||
torControlPort);
|
torControlPort);
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ import org.briarproject.bramble.api.event.EventBus;
|
|||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.network.NetworkManager;
|
import org.briarproject.bramble.api.network.NetworkManager;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.Backoff;
|
|
||||||
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
|
||||||
import org.briarproject.bramble.api.plugin.PluginCallback;
|
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||||
import org.briarproject.bramble.api.plugin.TorControlPort;
|
import org.briarproject.bramble.api.plugin.TorControlPort;
|
||||||
import org.briarproject.bramble.api.plugin.TorDirectory;
|
import org.briarproject.bramble.api.plugin.TorDirectory;
|
||||||
@@ -39,7 +37,6 @@ public class UnixTorPluginFactory extends TorPluginFactory {
|
|||||||
LocationUtils locationUtils,
|
LocationUtils locationUtils,
|
||||||
EventBus eventBus,
|
EventBus eventBus,
|
||||||
SocketFactory torSocketFactory,
|
SocketFactory torSocketFactory,
|
||||||
BackoffFactory backoffFactory,
|
|
||||||
ResourceProvider resourceProvider,
|
ResourceProvider resourceProvider,
|
||||||
CircumventionProvider circumventionProvider,
|
CircumventionProvider circumventionProvider,
|
||||||
BatteryManager batteryManager,
|
BatteryManager batteryManager,
|
||||||
@@ -49,7 +46,7 @@ public class UnixTorPluginFactory extends TorPluginFactory {
|
|||||||
@TorSocksPort int torSocksPort,
|
@TorSocksPort int torSocksPort,
|
||||||
@TorControlPort int torControlPort) {
|
@TorControlPort int torControlPort) {
|
||||||
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
||||||
eventBus, torSocketFactory, backoffFactory, resourceProvider,
|
eventBus, torSocketFactory, resourceProvider,
|
||||||
circumventionProvider, batteryManager, clock, crypto,
|
circumventionProvider, batteryManager, clock, crypto,
|
||||||
torDirectory, torSocksPort, torControlPort);
|
torDirectory, torSocksPort, torControlPort);
|
||||||
}
|
}
|
||||||
@@ -69,13 +66,13 @@ public class UnixTorPluginFactory extends TorPluginFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
TorPlugin createPluginInstance(Backoff backoff,
|
TorPlugin createPluginInstance(TorRendezvousCrypto torRendezvousCrypto,
|
||||||
TorRendezvousCrypto torRendezvousCrypto, PluginCallback callback,
|
PluginCallback callback,
|
||||||
String architecture) {
|
String architecture) {
|
||||||
return new UnixTorPlugin(ioExecutor, wakefulIoExecutor,
|
return new UnixTorPlugin(ioExecutor, wakefulIoExecutor,
|
||||||
networkManager, locationUtils, torSocketFactory, clock,
|
networkManager, locationUtils, torSocketFactory, clock,
|
||||||
resourceProvider, circumventionProvider, batteryManager,
|
resourceProvider, circumventionProvider, batteryManager,
|
||||||
backoff, torRendezvousCrypto, callback, architecture,
|
torRendezvousCrypto, callback, architecture,
|
||||||
MAX_LATENCY, MAX_IDLE_TIME, torDirectory, torSocksPort,
|
MAX_LATENCY, MAX_IDLE_TIME, torDirectory, torSocksPort,
|
||||||
torControlPort);
|
torControlPort);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package org.briarproject.bramble.plugin.tor;
|
||||||
|
|
||||||
|
import com.sun.jna.platform.win32.Kernel32;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.battery.BatteryManager;
|
||||||
|
import org.briarproject.bramble.api.network.NetworkManager;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||||
|
import org.briarproject.bramble.api.plugin.PluginException;
|
||||||
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
|
import org.briarproject.bramble.api.system.LocationUtils;
|
||||||
|
import org.briarproject.bramble.api.system.ResourceProvider;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Scanner;
|
||||||
|
import java.util.concurrent.ArrayBlockingQueue;
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
import javax.net.SocketFactory;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.INFO;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
class WindowsTorPlugin extends JavaTorPlugin {
|
||||||
|
|
||||||
|
WindowsTorPlugin(Executor ioExecutor,
|
||||||
|
Executor wakefulIoExecutor,
|
||||||
|
NetworkManager networkManager,
|
||||||
|
LocationUtils locationUtils,
|
||||||
|
SocketFactory torSocketFactory,
|
||||||
|
Clock clock,
|
||||||
|
ResourceProvider resourceProvider,
|
||||||
|
CircumventionProvider circumventionProvider,
|
||||||
|
BatteryManager batteryManager,
|
||||||
|
TorRendezvousCrypto torRendezvousCrypto,
|
||||||
|
PluginCallback callback,
|
||||||
|
String architecture,
|
||||||
|
long maxLatency,
|
||||||
|
int maxIdleTime,
|
||||||
|
File torDirectory,
|
||||||
|
int torSocksPort,
|
||||||
|
int torControlPort) {
|
||||||
|
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
||||||
|
torSocketFactory, clock, resourceProvider,
|
||||||
|
circumventionProvider, batteryManager,
|
||||||
|
torRendezvousCrypto, callback, architecture,
|
||||||
|
maxLatency, maxIdleTime, torDirectory, torSocksPort,
|
||||||
|
torControlPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getProcessId() {
|
||||||
|
return Kernel32.INSTANCE.GetCurrentProcessId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void waitForTorToStart(Process torProcess)
|
||||||
|
throws InterruptedException, PluginException {
|
||||||
|
// On Windows the RunAsDaemon option has no effect, so Tor won't detach.
|
||||||
|
// Wait for the control port to be opened, then continue to read its
|
||||||
|
// stdout and stderr in a background thread until it exits.
|
||||||
|
BlockingQueue<Boolean> success = new ArrayBlockingQueue<>(1);
|
||||||
|
ioExecutor.execute(() -> {
|
||||||
|
boolean started = false;
|
||||||
|
// Read the process's stdout (and redirected stderr)
|
||||||
|
Scanner stdout = new Scanner(torProcess.getInputStream());
|
||||||
|
// Log the first line of stdout (contains Tor and library versions)
|
||||||
|
if (stdout.hasNextLine()) LOG.info(stdout.nextLine());
|
||||||
|
// Startup has succeeded when the control port is open
|
||||||
|
while (stdout.hasNextLine()) {
|
||||||
|
String line = stdout.nextLine();
|
||||||
|
if (!started && line.contains("Opened Control listener")) {
|
||||||
|
success.add(true);
|
||||||
|
started = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stdout.close();
|
||||||
|
// If the control port wasn't opened, startup has failed
|
||||||
|
if (!started) success.add(false);
|
||||||
|
// Wait for the process to exit
|
||||||
|
try {
|
||||||
|
int exit = torProcess.waitFor();
|
||||||
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info("Tor exited with value " + exit);
|
||||||
|
} catch (InterruptedException e1) {
|
||||||
|
LOG.warning("Interrupted while waiting for Tor to exit");
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Wait for the startup result
|
||||||
|
if (!success.take()) throw new PluginException();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package org.briarproject.bramble.plugin.tor;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.battery.BatteryManager;
|
||||||
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
|
import org.briarproject.bramble.api.network.NetworkManager;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||||
|
import org.briarproject.bramble.api.plugin.TorControlPort;
|
||||||
|
import org.briarproject.bramble.api.plugin.TorDirectory;
|
||||||
|
import org.briarproject.bramble.api.plugin.TorSocksPort;
|
||||||
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
|
import org.briarproject.bramble.api.system.LocationUtils;
|
||||||
|
import org.briarproject.bramble.api.system.ResourceProvider;
|
||||||
|
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.net.SocketFactory;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.INFO;
|
||||||
|
import static org.briarproject.bramble.util.OsUtils.isWindows;
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
public class WindowsTorPluginFactory extends TorPluginFactory {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
WindowsTorPluginFactory(@IoExecutor Executor ioExecutor,
|
||||||
|
@WakefulIoExecutor Executor wakefulIoExecutor,
|
||||||
|
NetworkManager networkManager,
|
||||||
|
LocationUtils locationUtils,
|
||||||
|
EventBus eventBus,
|
||||||
|
SocketFactory torSocketFactory,
|
||||||
|
ResourceProvider resourceProvider,
|
||||||
|
CircumventionProvider circumventionProvider,
|
||||||
|
BatteryManager batteryManager,
|
||||||
|
Clock clock,
|
||||||
|
CryptoComponent crypto,
|
||||||
|
@TorDirectory File torDirectory,
|
||||||
|
@TorSocksPort int torSocksPort,
|
||||||
|
@TorControlPort int torControlPort) {
|
||||||
|
super(ioExecutor, wakefulIoExecutor, networkManager, locationUtils,
|
||||||
|
eventBus, torSocketFactory, resourceProvider,
|
||||||
|
circumventionProvider, batteryManager, clock, crypto,
|
||||||
|
torDirectory, torSocksPort, torControlPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
String getArchitectureForTorBinary() {
|
||||||
|
if (!isWindows()) return null;
|
||||||
|
String arch = System.getProperty("os.arch");
|
||||||
|
if (LOG.isLoggable(INFO)) {
|
||||||
|
LOG.info("System's os.arch is " + arch);
|
||||||
|
}
|
||||||
|
if (arch.equals("amd64")) return "windows-x86_64";
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
TorPlugin createPluginInstance(TorRendezvousCrypto torRendezvousCrypto,
|
||||||
|
PluginCallback callback,
|
||||||
|
String architecture) {
|
||||||
|
return new WindowsTorPlugin(ioExecutor, wakefulIoExecutor,
|
||||||
|
networkManager, locationUtils, torSocketFactory, clock,
|
||||||
|
resourceProvider, circumventionProvider, batteryManager,
|
||||||
|
torRendezvousCrypto, callback, architecture,
|
||||||
|
MAX_LATENCY, MAX_IDLE_TIME, torDirectory, torSocksPort,
|
||||||
|
torControlPort);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,9 +16,7 @@ public class DesktopSecureRandomModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
SecureRandomProvider provideSecureRandomProvider() {
|
SecureRandomProvider provideSecureRandomProvider() {
|
||||||
if (isLinux() || isMac())
|
if (isLinux() || isMac()) return new UnixSecureRandomProvider();
|
||||||
return new UnixSecureRandomProvider();
|
return () -> null; // Use system default
|
||||||
// TODO: Create a secure random provider for Windows
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import org.briarproject.bramble.api.event.EventBus;
|
|||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.network.NetworkManager;
|
import org.briarproject.bramble.api.network.NetworkManager;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
import org.briarproject.bramble.api.system.LocationUtils;
|
||||||
@@ -110,8 +109,6 @@ public class BridgeTest extends BrambleTestCase {
|
|||||||
@Inject
|
@Inject
|
||||||
EventBus eventBus;
|
EventBus eventBus;
|
||||||
@Inject
|
@Inject
|
||||||
BackoffFactory backoffFactory;
|
|
||||||
@Inject
|
|
||||||
Clock clock;
|
Clock clock;
|
||||||
@Inject
|
@Inject
|
||||||
CryptoComponent crypto;
|
CryptoComponent crypto;
|
||||||
@@ -166,9 +163,8 @@ public class BridgeTest extends BrambleTestCase {
|
|||||||
};
|
};
|
||||||
factory = new UnixTorPluginFactory(ioExecutor, wakefulIoExecutor,
|
factory = new UnixTorPluginFactory(ioExecutor, wakefulIoExecutor,
|
||||||
networkManager, locationUtils, eventBus, torSocketFactory,
|
networkManager, locationUtils, eventBus, torSocketFactory,
|
||||||
backoffFactory, resourceProvider, bridgeProvider,
|
resourceProvider, bridgeProvider, batteryManager, clock,
|
||||||
batteryManager, clock, crypto, torDir,
|
crypto, torDir, SOCKS_PORT, CONTROL_PORT);
|
||||||
SOCKS_PORT, CONTROL_PORT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
|||||||
@@ -43,6 +43,10 @@ public class TestPluginCallback implements PluginCallback {
|
|||||||
public void pluginStateChanged(State state) {
|
public void pluginStateChanged(State state) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void pollingIntervalDecreased() {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleConnection(DuplexTransportConnection c) {
|
public void handleConnection(DuplexTransportConnection c) {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ dependencyVerification {
|
|||||||
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
|
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
|
||||||
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
|
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
|
||||||
'org.briarproject:obfs4proxy-linux:0.0.12:obfs4proxy-linux-0.0.12.jar:3dd83aff25fe1cb3e4eab78a02c76ac921f552be6877b3af83a472438525df2a',
|
'org.briarproject:obfs4proxy-linux:0.0.12:obfs4proxy-linux-0.0.12.jar:3dd83aff25fe1cb3e4eab78a02c76ac921f552be6877b3af83a472438525df2a',
|
||||||
|
'org.briarproject:obfs4proxy-windows:0.0.12:obfs4proxy-windows-0.0.12.jar:392aa4b9d9c6fef0c659c4068d019d6c6471991bbb62ff00553884ec36018c7b',
|
||||||
'org.briarproject:tor-linux:0.4.5.12-2:tor-linux-0.4.5.12-2.jar:d275f323faf5e70b33d2c8a1bdab1bb3ab5a0d8f4e23c4a6dda03d86f4e95838',
|
'org.briarproject:tor-linux:0.4.5.12-2:tor-linux-0.4.5.12-2.jar:d275f323faf5e70b33d2c8a1bdab1bb3ab5a0d8f4e23c4a6dda03d86f4e95838',
|
||||||
|
'org.briarproject:tor-windows:0.4.5.12-2:tor-windows-0.4.5.12-2.jar:46599a15d099ed35a360113293f66acc119571c24ec2e37e85e4fb54b4722e07',
|
||||||
'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d',
|
'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d',
|
||||||
'org.checkerframework:checker-qual:2.5.2:checker-qual-2.5.2.jar:64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a',
|
'org.checkerframework:checker-qual:2.5.2:checker-qual-2.5.2.jar:64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a',
|
||||||
'org.codehaus.mojo:animal-sniffer-annotations:1.17:animal-sniffer-annotations-1.17.jar:92654f493ecfec52082e76354f0ebf87648dc3d5cec2e3c3cdb947c016747a53',
|
'org.codehaus.mojo:animal-sniffer-annotations:1.17:animal-sniffer-annotations-1.17.jar:92654f493ecfec52082e76354f0ebf87648dc3d5cec2e3c3cdb947c016747a53',
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import org.briarproject.bramble.api.crypto.PublicKey;
|
|||||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxDirectory;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.BluetoothConstants;
|
import org.briarproject.bramble.api.plugin.BluetoothConstants;
|
||||||
import org.briarproject.bramble.api.plugin.LanTcpConstants;
|
import org.briarproject.bramble.api.plugin.LanTcpConstants;
|
||||||
@@ -27,6 +28,7 @@ import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
|
|||||||
import org.briarproject.bramble.api.reporting.DevConfig;
|
import org.briarproject.bramble.api.reporting.DevConfig;
|
||||||
import org.briarproject.bramble.plugin.bluetooth.AndroidBluetoothPluginFactory;
|
import org.briarproject.bramble.plugin.bluetooth.AndroidBluetoothPluginFactory;
|
||||||
import org.briarproject.bramble.plugin.file.AndroidRemovableDrivePluginFactory;
|
import org.briarproject.bramble.plugin.file.AndroidRemovableDrivePluginFactory;
|
||||||
|
import org.briarproject.bramble.plugin.file.MailboxPluginFactory;
|
||||||
import org.briarproject.bramble.plugin.tcp.AndroidLanTcpPluginFactory;
|
import org.briarproject.bramble.plugin.tcp.AndroidLanTcpPluginFactory;
|
||||||
import org.briarproject.bramble.plugin.tor.AndroidTorPluginFactory;
|
import org.briarproject.bramble.plugin.tor.AndroidTorPluginFactory;
|
||||||
import org.briarproject.bramble.util.AndroidUtils;
|
import org.briarproject.bramble.util.AndroidUtils;
|
||||||
@@ -63,6 +65,7 @@ import org.briarproject.briar.api.test.TestAvatarCreator;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -76,7 +79,6 @@ import dagger.Provides;
|
|||||||
import static android.content.Context.MODE_PRIVATE;
|
import static android.content.Context.MODE_PRIVATE;
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static java.util.Collections.emptyList;
|
|
||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_CONTROL_PORT;
|
import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_CONTROL_PORT;
|
||||||
@@ -156,6 +158,13 @@ public class AppModule {
|
|||||||
return new AndroidDatabaseConfig(dbDir, keyDir, keyStrengthener);
|
return new AndroidDatabaseConfig(dbDir, keyDir, keyStrengthener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
@MailboxDirectory
|
||||||
|
File provideMailboxDirectory(Application app) {
|
||||||
|
return app.getDir("mailbox", MODE_PRIVATE);
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
@TorDirectory
|
@TorDirectory
|
||||||
@@ -190,7 +199,7 @@ public class AppModule {
|
|||||||
PluginConfig providePluginConfig(AndroidBluetoothPluginFactory bluetooth,
|
PluginConfig providePluginConfig(AndroidBluetoothPluginFactory bluetooth,
|
||||||
AndroidTorPluginFactory tor, AndroidLanTcpPluginFactory lan,
|
AndroidTorPluginFactory tor, AndroidLanTcpPluginFactory lan,
|
||||||
AndroidRemovableDrivePluginFactory drive,
|
AndroidRemovableDrivePluginFactory drive,
|
||||||
FeatureFlags featureFlags) {
|
MailboxPluginFactory mailbox, FeatureFlags featureFlags) {
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
PluginConfig pluginConfig = new PluginConfig() {
|
PluginConfig pluginConfig = new PluginConfig() {
|
||||||
|
|
||||||
@@ -201,8 +210,10 @@ public class AppModule {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<SimplexPluginFactory> getSimplexFactories() {
|
public Collection<SimplexPluginFactory> getSimplexFactories() {
|
||||||
if (SDK_INT >= 19) return singletonList(drive);
|
List<SimplexPluginFactory> simplex = new ArrayList<>();
|
||||||
else return emptyList();
|
if (featureFlags.shouldEnableMailbox()) simplex.add(mailbox);
|
||||||
|
if (SDK_INT >= 19) simplex.add(drive);
|
||||||
|
return simplex;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import static android.view.View.VISIBLE;
|
|||||||
import static androidx.core.content.ContextCompat.getColor;
|
import static androidx.core.content.ContextCompat.getColor;
|
||||||
import static androidx.core.widget.ImageViewCompat.setImageTintList;
|
import static androidx.core.widget.ImageViewCompat.setImageTintList;
|
||||||
import static androidx.transition.TransitionManager.beginDelayedTransition;
|
import static androidx.transition.TransitionManager.beginDelayedTransition;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxConstants.API_CLIENT_TOO_OLD;
|
||||||
import static org.briarproject.briar.android.AppModule.getAndroidComponent;
|
import static org.briarproject.briar.android.AppModule.getAndroidComponent;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.MIN_DATE_RESOLUTION;
|
import static org.briarproject.briar.android.util.UiUtils.MIN_DATE_RESOLUTION;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.formatDate;
|
import static org.briarproject.briar.android.util.UiUtils.formatDate;
|
||||||
@@ -47,7 +48,6 @@ import static org.briarproject.briar.android.util.UiUtils.showFragment;
|
|||||||
public class MailboxStatusFragment extends Fragment {
|
public class MailboxStatusFragment extends Fragment {
|
||||||
|
|
||||||
static final String TAG = MailboxStatusFragment.class.getName();
|
static final String TAG = MailboxStatusFragment.class.getName();
|
||||||
private static final int NUM_FAILURES = 4;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ViewModelProvider.Factory viewModelFactory;
|
ViewModelProvider.Factory viewModelFactory;
|
||||||
@@ -59,8 +59,7 @@ public class MailboxStatusFragment extends Fragment {
|
|||||||
private boolean showUnlinkWarning = true;
|
private boolean showUnlinkWarning = true;
|
||||||
|
|
||||||
private ImageView imageView;
|
private ImageView imageView;
|
||||||
private TextView statusTitleView;
|
private TextView statusTitleView, statusMessageView, statusInfoView;
|
||||||
private TextView statusInfoView;
|
|
||||||
private Button wizardButton;
|
private Button wizardButton;
|
||||||
private Button unlinkButton;
|
private Button unlinkButton;
|
||||||
private ProgressBar unlinkProgress;
|
private ProgressBar unlinkProgress;
|
||||||
@@ -95,6 +94,7 @@ public class MailboxStatusFragment extends Fragment {
|
|||||||
|
|
||||||
imageView = v.findViewById(R.id.imageView);
|
imageView = v.findViewById(R.id.imageView);
|
||||||
statusTitleView = v.findViewById(R.id.statusTitleView);
|
statusTitleView = v.findViewById(R.id.statusTitleView);
|
||||||
|
statusMessageView = v.findViewById(R.id.statusMessageView);
|
||||||
statusInfoView = v.findViewById(R.id.statusInfoView);
|
statusInfoView = v.findViewById(R.id.statusInfoView);
|
||||||
viewModel.getStatus()
|
viewModel.getStatus()
|
||||||
.observe(getViewLifecycleOwner(), this::onMailboxStateChanged);
|
.observe(getViewLifecycleOwner(), this::onMailboxStateChanged);
|
||||||
@@ -133,29 +133,51 @@ public class MailboxStatusFragment extends Fragment {
|
|||||||
@ColorRes int tintRes;
|
@ColorRes int tintRes;
|
||||||
@DrawableRes int iconRes;
|
@DrawableRes int iconRes;
|
||||||
String title;
|
String title;
|
||||||
if (status.getAttemptsSinceSuccess() == 0) {
|
String message = null;
|
||||||
iconRes = R.drawable.ic_check_circle_outline;
|
if (status.hasProblem(System.currentTimeMillis())) {
|
||||||
title = getString(R.string.mailbox_status_connected_title);
|
|
||||||
tintRes = R.color.briar_brand_green;
|
|
||||||
showUnlinkWarning = true;
|
|
||||||
wizardButton.setVisibility(GONE);
|
|
||||||
} else if (!status.hasProblem(System.currentTimeMillis())) {
|
|
||||||
iconRes = R.drawable.ic_help_outline_white;
|
|
||||||
title = getString(R.string.mailbox_status_problem_title);
|
|
||||||
tintRes = R.color.briar_orange_500;
|
|
||||||
showUnlinkWarning = false;
|
|
||||||
wizardButton.setVisibility(VISIBLE);
|
|
||||||
} else {
|
|
||||||
tintRes = R.color.briar_red_500;
|
tintRes = R.color.briar_red_500;
|
||||||
title = getString(R.string.mailbox_status_failure_title);
|
title = getString(R.string.mailbox_status_failure_title);
|
||||||
iconRes = R.drawable.alerts_and_states_error;
|
iconRes = R.drawable.alerts_and_states_error;
|
||||||
showUnlinkWarning = false;
|
showUnlinkWarning = false;
|
||||||
wizardButton.setVisibility(VISIBLE);
|
wizardButton.setVisibility(VISIBLE);
|
||||||
|
} else if (status.getAttemptsSinceSuccess() > 0) {
|
||||||
|
iconRes = R.drawable.ic_help_outline_white;
|
||||||
|
title = getString(R.string.mailbox_status_problem_title);
|
||||||
|
tintRes = R.color.briar_orange_500;
|
||||||
|
showUnlinkWarning = false;
|
||||||
|
wizardButton.setVisibility(VISIBLE);
|
||||||
|
} else if (status.getMailboxCompatibility() < 0) {
|
||||||
|
tintRes = R.color.briar_red_500;
|
||||||
|
if (status.getMailboxCompatibility() == API_CLIENT_TOO_OLD) {
|
||||||
|
title = getString(R.string.mailbox_status_app_too_old_title);
|
||||||
|
message =
|
||||||
|
getString(R.string.mailbox_status_app_too_old_message);
|
||||||
|
} else {
|
||||||
|
title = getString(
|
||||||
|
R.string.mailbox_status_mailbox_too_old_title);
|
||||||
|
message = getString(
|
||||||
|
R.string.mailbox_status_mailbox_too_old_message);
|
||||||
|
}
|
||||||
|
iconRes = R.drawable.alerts_and_states_error;
|
||||||
|
showUnlinkWarning = true;
|
||||||
|
wizardButton.setVisibility(GONE);
|
||||||
|
} else {
|
||||||
|
iconRes = R.drawable.ic_check_circle_outline;
|
||||||
|
title = getString(R.string.mailbox_status_connected_title);
|
||||||
|
tintRes = R.color.briar_brand_green;
|
||||||
|
showUnlinkWarning = true;
|
||||||
|
wizardButton.setVisibility(GONE);
|
||||||
}
|
}
|
||||||
imageView.setImageResource(iconRes);
|
imageView.setImageResource(iconRes);
|
||||||
int color = getColor(requireContext(), tintRes);
|
int color = getColor(requireContext(), tintRes);
|
||||||
setImageTintList(imageView, ColorStateList.valueOf(color));
|
setImageTintList(imageView, ColorStateList.valueOf(color));
|
||||||
statusTitleView.setText(title);
|
statusTitleView.setText(title);
|
||||||
|
if (message == null) {
|
||||||
|
statusMessageView.setVisibility(GONE);
|
||||||
|
} else {
|
||||||
|
statusMessageView.setVisibility(VISIBLE);
|
||||||
|
statusMessageView.setText(message);
|
||||||
|
}
|
||||||
|
|
||||||
long lastSuccess = status.getTimeOfLastSuccess();
|
long lastSuccess = status.getTimeOfLastSuccess();
|
||||||
String lastConnectionText;
|
String lastConnectionText;
|
||||||
|
|||||||
@@ -208,16 +208,12 @@ class MailboxViewModel extends DbViewModel
|
|||||||
|
|
||||||
LiveData<Boolean> checkConnection() {
|
LiveData<Boolean> checkConnection() {
|
||||||
MutableLiveData<Boolean> liveData = new MutableLiveData<>();
|
MutableLiveData<Boolean> liveData = new MutableLiveData<>();
|
||||||
checkConnection(success -> {
|
checkConnection(liveData::postValue);
|
||||||
liveData.postValue(success);
|
|
||||||
if (!success) onConnectionCheckFailure();
|
|
||||||
});
|
|
||||||
return liveData;
|
return liveData;
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkConnectionFromWizard() {
|
void checkConnectionFromWizard() {
|
||||||
checkConnection(success -> {
|
checkConnection(success -> {
|
||||||
if (!success) onConnectionCheckFailure();
|
|
||||||
boolean isOnline = isTorActive();
|
boolean isOnline = isTorActive();
|
||||||
// make UI move back to status fragment by changing pairingState
|
// make UI move back to status fragment by changing pairingState
|
||||||
pairingState.postEvent(new MailboxState.IsPaired(isOnline));
|
pairingState.postEvent(new MailboxState.IsPaired(isOnline));
|
||||||
@@ -234,15 +230,6 @@ class MailboxViewModel extends DbViewModel
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onConnectionCheckFailure() {
|
|
||||||
MailboxStatus lastStatus = status.getValue();
|
|
||||||
long lastSuccess = lastStatus == null ?
|
|
||||||
-1 : lastStatus.getTimeOfLastSuccess();
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
// force failure screen
|
|
||||||
status.postValue(new MailboxStatus(now, lastSuccess, 999));
|
|
||||||
}
|
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
void unlink() {
|
void unlink() {
|
||||||
ioExecutor.execute(() -> {
|
ioExecutor.execute(() -> {
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
package org.briarproject.briar.android.reporting;
|
package org.briarproject.briar.android.reporting;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.ActivityManager;
|
|
||||||
import android.bluetooth.BluetoothAdapter;
|
import android.bluetooth.BluetoothAdapter;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.FeatureInfo;
|
import android.content.pm.FeatureInfo;
|
||||||
@@ -19,7 +18,6 @@ import android.net.NetworkInfo;
|
|||||||
import android.net.wifi.WifiInfo;
|
import android.net.wifi.WifiInfo;
|
||||||
import android.net.wifi.WifiManager;
|
import android.net.wifi.WifiManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Debug;
|
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.Pair;
|
import org.briarproject.bramble.api.Pair;
|
||||||
@@ -29,6 +27,7 @@ import org.briarproject.briar.R;
|
|||||||
import org.briarproject.briar.android.reporting.ReportData.MultiReportInfo;
|
import org.briarproject.briar.android.reporting.ReportData.MultiReportInfo;
|
||||||
import org.briarproject.briar.android.reporting.ReportData.ReportItem;
|
import org.briarproject.briar.android.reporting.ReportData.ReportItem;
|
||||||
import org.briarproject.briar.android.reporting.ReportData.SingleReportInfo;
|
import org.briarproject.briar.android.reporting.ReportData.SingleReportInfo;
|
||||||
|
import org.briarproject.briar.api.android.MemoryStats;
|
||||||
import org.briarproject.briar.api.android.NetworkUsageMetrics;
|
import org.briarproject.briar.api.android.NetworkUsageMetrics;
|
||||||
import org.briarproject.briar.api.android.NetworkUsageMetrics.Metrics;
|
import org.briarproject.briar.api.android.NetworkUsageMetrics.Metrics;
|
||||||
|
|
||||||
@@ -76,14 +75,14 @@ class BriarReportCollector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ReportData collectReportData(@Nullable Throwable t, long appStartTime,
|
ReportData collectReportData(@Nullable Throwable t, long appStartTime,
|
||||||
String logs) {
|
String logs, MemoryStats memoryStats) {
|
||||||
ReportData reportData = new ReportData()
|
ReportData reportData = new ReportData()
|
||||||
.add(getBasicInfo(t))
|
.add(getBasicInfo(t))
|
||||||
.add(getDeviceInfo());
|
.add(getDeviceInfo());
|
||||||
if (t != null) reportData.add(getStacktrace(t));
|
if (t != null) reportData.add(getStacktrace(t));
|
||||||
return reportData
|
return reportData
|
||||||
.add(getTimeInfo(appStartTime))
|
.add(getTimeInfo(appStartTime))
|
||||||
.add(getMemory())
|
.add(getMemory(memoryStats))
|
||||||
.add(getStorage())
|
.add(getStorage())
|
||||||
.add(getConnectivity())
|
.add(getConnectivity())
|
||||||
.add(getNetworkUsage())
|
.add(getNetworkUsage())
|
||||||
@@ -154,26 +153,22 @@ class BriarReportCollector {
|
|||||||
return format.format(new Date(time));
|
return format.format(new Date(time));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ReportItem getMemory() {
|
private ReportItem getMemory(MemoryStats stats) {
|
||||||
MultiReportInfo memInfo = new MultiReportInfo();
|
MultiReportInfo memInfo = new MultiReportInfo();
|
||||||
|
|
||||||
// System memory
|
// System memory
|
||||||
ActivityManager am = getSystemService(ctx, ActivityManager.class);
|
memInfo.add("SystemMemoryTotal", stats.systemMemoryTotal);
|
||||||
ActivityManager.MemoryInfo mem = new ActivityManager.MemoryInfo();
|
memInfo.add("SystemMemoryFree", stats.systemMemoryFree);
|
||||||
requireNonNull(am).getMemoryInfo(mem);
|
memInfo.add("SystemMemoryThreshold", stats.systemMemoryThreshold);
|
||||||
memInfo.add("SystemMemoryTotal", mem.totalMem);
|
memInfo.add("SystemMemoryLow", stats.systemMemoryLow);
|
||||||
memInfo.add("SystemMemoryFree", mem.availMem);
|
|
||||||
memInfo.add("SystemMemoryThreshold", mem.threshold);
|
|
||||||
memInfo.add("SystemMemoryLow", mem.lowMemory);
|
|
||||||
|
|
||||||
// Virtual machine memory
|
// Virtual machine memory
|
||||||
Runtime runtime = Runtime.getRuntime();
|
memInfo.add("VirtualMachineMemoryTotal", stats.vmMemoryTotal);
|
||||||
memInfo.add("VirtualMachineMemoryTotal", runtime.totalMemory());
|
memInfo.add("VirtualMachineMemoryFree", stats.vmMemoryFree);
|
||||||
memInfo.add("VirtualMachineMemoryFree", runtime.freeMemory());
|
memInfo.add("VirtualMachineMemoryMaximum", stats.vmMemoryMax);
|
||||||
memInfo.add("VirtualMachineMemoryMaximum", runtime.maxMemory());
|
memInfo.add("NativeHeapTotal", stats.nativeHeapTotal);
|
||||||
memInfo.add("NativeHeapTotal", Debug.getNativeHeapSize());
|
memInfo.add("NativeHeapAllocated", stats.nativeHeapAllocated);
|
||||||
memInfo.add("NativeHeapAllocated", Debug.getNativeHeapAllocatedSize());
|
memInfo.add("NativeHeapFree", stats.nativeHeapFree);
|
||||||
memInfo.add("NativeHeapFree", Debug.getNativeHeapFreeSize());
|
|
||||||
|
|
||||||
return new ReportItem("Memory", R.string.dev_report_memory, memInfo);
|
return new ReportItem("Memory", R.string.dev_report_memory, memInfo);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import org.briarproject.briar.android.activity.BaseActivity;
|
|||||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||||
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
|
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
|
||||||
import org.briarproject.briar.android.logout.HideUiActivity;
|
import org.briarproject.briar.android.logout.HideUiActivity;
|
||||||
|
import org.briarproject.briar.api.android.MemoryStats;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
@@ -37,6 +38,7 @@ public class CrashReportActivity extends BaseActivity
|
|||||||
public static final String EXTRA_THROWABLE = "throwable";
|
public static final String EXTRA_THROWABLE = "throwable";
|
||||||
public static final String EXTRA_APP_START_TIME = "appStartTime";
|
public static final String EXTRA_APP_START_TIME = "appStartTime";
|
||||||
public static final String EXTRA_APP_LOGCAT = "logcat";
|
public static final String EXTRA_APP_LOGCAT = "logcat";
|
||||||
|
public static final String EXTRA_MEMORY_STATS = "memoryStats";
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ViewModelProvider.Factory viewModelFactory;
|
ViewModelProvider.Factory viewModelFactory;
|
||||||
@@ -60,7 +62,9 @@ public class CrashReportActivity extends BaseActivity
|
|||||||
Throwable t = (Throwable) intent.getSerializableExtra(EXTRA_THROWABLE);
|
Throwable t = (Throwable) intent.getSerializableExtra(EXTRA_THROWABLE);
|
||||||
long appStartTime = intent.getLongExtra(EXTRA_APP_START_TIME, -1);
|
long appStartTime = intent.getLongExtra(EXTRA_APP_START_TIME, -1);
|
||||||
byte[] logKey = intent.getByteArrayExtra(EXTRA_APP_LOGCAT);
|
byte[] logKey = intent.getByteArrayExtra(EXTRA_APP_LOGCAT);
|
||||||
viewModel.init(t, appStartTime, logKey, initialComment);
|
MemoryStats memoryStats =
|
||||||
|
(MemoryStats) intent.getSerializableExtra(EXTRA_MEMORY_STATS);
|
||||||
|
viewModel.init(t, appStartTime, logKey, initialComment, memoryStats);
|
||||||
viewModel.getShowReport().observeEvent(this, show -> {
|
viewModel.getShowReport().observeEvent(this, show -> {
|
||||||
if (show) displayFragment(true);
|
if (show) displayFragment(true);
|
||||||
});
|
});
|
||||||
@@ -87,7 +91,7 @@ public class CrashReportActivity extends BaseActivity
|
|||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void displayFragment(boolean showReportForm) {
|
private void displayFragment(boolean showReportForm) {
|
||||||
BaseFragment f;
|
BaseFragment f;
|
||||||
if (showReportForm) {
|
if (showReportForm) {
|
||||||
f = new ReportFormFragment();
|
f = new ReportFormFragment();
|
||||||
@@ -101,7 +105,7 @@ public class CrashReportActivity extends BaseActivity
|
|||||||
.commit();
|
.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void exit() {
|
private void exit() {
|
||||||
if (!viewModel.isFeedback()) {
|
if (!viewModel.isFeedback()) {
|
||||||
Intent i = new Intent(this, HideUiActivity.class);
|
Intent i = new Intent(this, HideUiActivity.class);
|
||||||
i.addFlags(FLAG_ACTIVITY_NEW_TASK
|
i.addFlags(FLAG_ACTIVITY_NEW_TASK
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import org.briarproject.briar.android.reporting.ReportData.MultiReportInfo;
|
|||||||
import org.briarproject.briar.android.reporting.ReportData.ReportItem;
|
import org.briarproject.briar.android.reporting.ReportData.ReportItem;
|
||||||
import org.briarproject.briar.android.viewmodel.LiveEvent;
|
import org.briarproject.briar.android.viewmodel.LiveEvent;
|
||||||
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
|
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
|
||||||
|
import org.briarproject.briar.api.android.MemoryStats;
|
||||||
import org.briarproject.briar.api.android.NetworkUsageMetrics;
|
import org.briarproject.briar.api.android.NetworkUsageMetrics;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
|
|
||||||
@@ -84,7 +85,8 @@ class ReportViewModel extends AndroidViewModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void init(@Nullable Throwable t, long appStartTime,
|
void init(@Nullable Throwable t, long appStartTime,
|
||||||
@Nullable byte[] logKey, @Nullable String initialComment) {
|
@Nullable byte[] logKey, @Nullable String initialComment,
|
||||||
|
MemoryStats memoryStats) {
|
||||||
this.initialComment = initialComment;
|
this.initialComment = initialComment;
|
||||||
isFeedback = t == null;
|
isFeedback = t == null;
|
||||||
if (reportData.getValue() == null) new SingleShotAndroidExecutor(() -> {
|
if (reportData.getValue() == null) new SingleShotAndroidExecutor(() -> {
|
||||||
@@ -102,8 +104,8 @@ class ReportViewModel extends AndroidViewModel {
|
|||||||
logHandler.getRecentLogRecords());
|
logHandler.getRecentLogRecords());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ReportData data =
|
ReportData data = collector.collectReportData(t, appStartTime,
|
||||||
collector.collectReportData(t, appStartTime, decryptedLogs);
|
decryptedLogs, memoryStats);
|
||||||
reportData.postValue(data);
|
reportData.postValue(data);
|
||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
@@ -150,7 +152,7 @@ class ReportViewModel extends AndroidViewModel {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The content of the report that will be loaded after
|
* The content of the report that will be loaded after
|
||||||
* {@link #init(Throwable, long, byte[], String)} was called.
|
* {@link #init(Throwable, long, byte[], String, MemoryStats)} was called.
|
||||||
*/
|
*/
|
||||||
LiveData<ReportData> getReportData() {
|
LiveData<ReportData> getReportData() {
|
||||||
return reportData;
|
return reportData;
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package org.briarproject.briar.android.util;
|
package org.briarproject.briar.android.util;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.app.ActivityManager;
|
||||||
|
import android.app.ActivityManager.MemoryInfo;
|
||||||
import android.app.KeyguardManager;
|
import android.app.KeyguardManager;
|
||||||
import android.content.ActivityNotFoundException;
|
import android.content.ActivityNotFoundException;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -10,6 +12,7 @@ import android.content.res.Resources;
|
|||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.location.LocationManager;
|
import android.location.LocationManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Debug;
|
||||||
import android.text.Spannable;
|
import android.text.Spannable;
|
||||||
import android.text.SpannableString;
|
import android.text.SpannableString;
|
||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
@@ -38,6 +41,7 @@ import org.briarproject.bramble.util.StringUtils;
|
|||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.reporting.FeedbackActivity;
|
import org.briarproject.briar.android.reporting.FeedbackActivity;
|
||||||
import org.briarproject.briar.android.view.ArticleMovementMethod;
|
import org.briarproject.briar.android.view.ArticleMovementMethod;
|
||||||
|
import org.briarproject.briar.api.android.MemoryStats;
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@@ -113,6 +117,7 @@ import static org.briarproject.briar.android.TestingConstants.OLD_ANDROID_WARN_D
|
|||||||
import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_APP_LOGCAT;
|
import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_APP_LOGCAT;
|
||||||
import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_APP_START_TIME;
|
import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_APP_START_TIME;
|
||||||
import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_INITIAL_COMMENT;
|
import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_INITIAL_COMMENT;
|
||||||
|
import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_MEMORY_STATS;
|
||||||
import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_THROWABLE;
|
import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_THROWABLE;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@@ -429,12 +434,27 @@ public class UiUtils {
|
|||||||
Class<? extends FragmentActivity> activity, @Nullable Throwable t,
|
Class<? extends FragmentActivity> activity, @Nullable Throwable t,
|
||||||
@Nullable Long appStartTime, @Nullable byte[] logKey, @Nullable
|
@Nullable Long appStartTime, @Nullable byte[] logKey, @Nullable
|
||||||
String initialComment) {
|
String initialComment) {
|
||||||
|
// Collect memory stats from the current process, not the crash
|
||||||
|
// reporter process
|
||||||
|
ActivityManager am =
|
||||||
|
requireNonNull(getSystemService(ctx, ActivityManager.class));
|
||||||
|
MemoryInfo mem = new MemoryInfo();
|
||||||
|
am.getMemoryInfo(mem);
|
||||||
|
Runtime runtime = Runtime.getRuntime();
|
||||||
|
MemoryStats memoryStats = new MemoryStats(mem.totalMem,
|
||||||
|
mem.availMem, mem.threshold, mem.lowMemory,
|
||||||
|
runtime.totalMemory(), runtime.freeMemory(),
|
||||||
|
runtime.maxMemory(), Debug.getNativeHeapSize(),
|
||||||
|
Debug.getNativeHeapAllocatedSize(),
|
||||||
|
Debug.getNativeHeapFreeSize());
|
||||||
|
|
||||||
final Intent dialogIntent = new Intent(ctx, activity);
|
final Intent dialogIntent = new Intent(ctx, activity);
|
||||||
dialogIntent.setFlags(FLAG_ACTIVITY_NEW_TASK);
|
dialogIntent.setFlags(FLAG_ACTIVITY_NEW_TASK);
|
||||||
dialogIntent.putExtra(EXTRA_THROWABLE, t);
|
dialogIntent.putExtra(EXTRA_THROWABLE, t);
|
||||||
dialogIntent.putExtra(EXTRA_APP_START_TIME, appStartTime);
|
dialogIntent.putExtra(EXTRA_APP_START_TIME, appStartTime);
|
||||||
dialogIntent.putExtra(EXTRA_APP_LOGCAT, logKey);
|
dialogIntent.putExtra(EXTRA_APP_LOGCAT, logKey);
|
||||||
dialogIntent.putExtra(EXTRA_INITIAL_COMMENT, initialComment);
|
dialogIntent.putExtra(EXTRA_INITIAL_COMMENT, initialComment);
|
||||||
|
dialogIntent.putExtra(EXTRA_MEMORY_STATS, memoryStats);
|
||||||
ctx.startActivity(dialogIntent);
|
ctx.startActivity(dialogIntent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package org.briarproject.briar.api.android;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Memory usage stats to be included in feedback and crash reports. This class
|
||||||
|
* is {@link Serializable} so it can be passed from the crashed process to the
|
||||||
|
* crash reporter process.
|
||||||
|
*/
|
||||||
|
@Immutable
|
||||||
|
public class MemoryStats implements Serializable {
|
||||||
|
|
||||||
|
public final long systemMemoryTotal;
|
||||||
|
public final long systemMemoryFree;
|
||||||
|
public final long systemMemoryThreshold;
|
||||||
|
public final boolean systemMemoryLow;
|
||||||
|
public final long vmMemoryTotal;
|
||||||
|
public final long vmMemoryFree;
|
||||||
|
public final long vmMemoryMax;
|
||||||
|
public final long nativeHeapTotal;
|
||||||
|
public final long nativeHeapAllocated;
|
||||||
|
public final long nativeHeapFree;
|
||||||
|
|
||||||
|
public MemoryStats(long systemMemoryTotal, long systemMemoryFree,
|
||||||
|
long systemMemoryThreshold, boolean systemMemoryLow,
|
||||||
|
long vmMemoryTotal, long vmMemoryFree, long vmMemoryMax,
|
||||||
|
long nativeHeapTotal, long nativeHeapAllocated,
|
||||||
|
long nativeHeapFree) {
|
||||||
|
this.systemMemoryTotal = systemMemoryTotal;
|
||||||
|
this.systemMemoryFree = systemMemoryFree;
|
||||||
|
this.systemMemoryThreshold = systemMemoryThreshold;
|
||||||
|
this.systemMemoryLow = systemMemoryLow;
|
||||||
|
this.vmMemoryTotal = vmMemoryTotal;
|
||||||
|
this.vmMemoryFree = vmMemoryFree;
|
||||||
|
this.vmMemoryMax = vmMemoryMax;
|
||||||
|
this.nativeHeapTotal = nativeHeapTotal;
|
||||||
|
this.nativeHeapAllocated = nativeHeapAllocated;
|
||||||
|
this.nativeHeapFree = nativeHeapFree;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,12 +31,29 @@
|
|||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
|
||||||
app:layout_constrainedWidth="true"
|
app:layout_constrainedWidth="true"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/checkButton"
|
app:layout_constraintBottom_toTopOf="@+id/statusMessageView"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/imageView"
|
app:layout_constraintTop_toBottomOf="@+id/imageView"
|
||||||
tools:text="@string/mailbox_status_problem_title" />
|
tools:text="@string/mailbox_status_problem_title" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/statusMessageView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="16dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constrainedWidth="true"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/checkButton"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/statusTitleView"
|
||||||
|
tools:text="@string/mailbox_status_mailbox_too_old_message"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<org.briarproject.briar.android.view.BriarButton
|
<org.briarproject.briar.android.view.BriarButton
|
||||||
android:id="@+id/checkButton"
|
android:id="@+id/checkButton"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@@ -46,7 +63,7 @@
|
|||||||
app:layout_constraintBottom_toTopOf="@+id/statusInfoView"
|
app:layout_constraintBottom_toTopOf="@+id/statusInfoView"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/statusTitleView"
|
app:layout_constraintTop_toBottomOf="@+id/statusMessageView"
|
||||||
app:text="@string/mailbox_status_check_button" />
|
app:text="@string/mailbox_status_check_button" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
|||||||
@@ -29,7 +29,6 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:inputType="textMultiLine|textCapSentences"
|
android:inputType="textMultiLine|textCapSentences"
|
||||||
android:maxLines="5"
|
|
||||||
tools:hint="@string/describe_crash" />
|
tools:hint="@string/describe_crash" />
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|||||||
@@ -640,6 +640,10 @@
|
|||||||
<string name="mailbox_status_connected_title">Mailbox is running</string>
|
<string name="mailbox_status_connected_title">Mailbox is running</string>
|
||||||
<string name="mailbox_status_problem_title">Briar is having trouble connecting to the Mailbox</string>
|
<string name="mailbox_status_problem_title">Briar is having trouble connecting to the Mailbox</string>
|
||||||
<string name="mailbox_status_failure_title">Mailbox is unavailable</string>
|
<string name="mailbox_status_failure_title">Mailbox is unavailable</string>
|
||||||
|
<string name="mailbox_status_app_too_old_title">Briar is too old</string>
|
||||||
|
<string name="mailbox_status_app_too_old_message">Update Briar to the latest version of the app and try again.</string>
|
||||||
|
<string name="mailbox_status_mailbox_too_old_title">Mailbox is too old</string>
|
||||||
|
<string name="mailbox_status_mailbox_too_old_message">Update your Mailbox to the latest version of the app and try again.</string>
|
||||||
<string name="mailbox_status_check_button">Check Connection</string>
|
<string name="mailbox_status_check_button">Check Connection</string>
|
||||||
<!-- Example for string substitution: Last connection: 3min ago-->
|
<!-- Example for string substitution: Last connection: 3min ago-->
|
||||||
<string name="mailbox_status_connected_info">Last connection: %s</string>
|
<string name="mailbox_status_connected_info">Last connection: %s</string>
|
||||||
|
|||||||
@@ -59,7 +59,12 @@ void jarFactory(Jar jarTask, jarArchitecture) {
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
it.duplicatesStrategy(DuplicatesStrategy.EXCLUDE)
|
it.duplicatesStrategy(DuplicatesStrategy.EXCLUDE)
|
||||||
String[] architectures = ["linux-aarch64", "linux-armhf", "linux-x86_64"]
|
String[] architectures = [
|
||||||
|
"linux-aarch64",
|
||||||
|
"linux-armhf",
|
||||||
|
"linux-x86_64",
|
||||||
|
"windows-x86_64"
|
||||||
|
]
|
||||||
for (String arch : architectures) {
|
for (String arch : architectures) {
|
||||||
if (arch != jarArchitecture) {
|
if (arch != jarArchitecture) {
|
||||||
exclude "obfs4proxy_" + arch + ".zip"
|
exclude "obfs4proxy_" + arch + ".zip"
|
||||||
@@ -112,6 +117,10 @@ task x86LinuxJar(type: Jar) {
|
|||||||
jarFactory(it, 'linux-x86_64')
|
jarFactory(it, 'linux-x86_64')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task windowsJar(type: Jar) {
|
||||||
|
jarFactory(it, 'windows-x86_64')
|
||||||
|
}
|
||||||
|
|
||||||
task linuxJars {
|
task linuxJars {
|
||||||
dependsOn(aarch64LinuxJar, armhfLinuxJar, x86LinuxJar)
|
dependsOn(aarch64LinuxJar, armhfLinuxJar, x86LinuxJar)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import dagger.Provides
|
|||||||
import org.briarproject.bramble.account.AccountModule
|
import org.briarproject.bramble.account.AccountModule
|
||||||
import org.briarproject.bramble.api.FeatureFlags
|
import org.briarproject.bramble.api.FeatureFlags
|
||||||
import org.briarproject.bramble.api.db.DatabaseConfig
|
import org.briarproject.bramble.api.db.DatabaseConfig
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxDirectory
|
||||||
import org.briarproject.bramble.api.plugin.PluginConfig
|
import org.briarproject.bramble.api.plugin.PluginConfig
|
||||||
import org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_CONTROL_PORT
|
import org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_CONTROL_PORT
|
||||||
import org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_SOCKS_PORT
|
import org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_SOCKS_PORT
|
||||||
@@ -20,6 +21,7 @@ import org.briarproject.bramble.event.DefaultEventExecutorModule
|
|||||||
import org.briarproject.bramble.network.JavaNetworkModule
|
import org.briarproject.bramble.network.JavaNetworkModule
|
||||||
import org.briarproject.bramble.plugin.tor.CircumventionModule
|
import org.briarproject.bramble.plugin.tor.CircumventionModule
|
||||||
import org.briarproject.bramble.plugin.tor.UnixTorPluginFactory
|
import org.briarproject.bramble.plugin.tor.UnixTorPluginFactory
|
||||||
|
import org.briarproject.bramble.plugin.tor.WindowsTorPluginFactory
|
||||||
import org.briarproject.bramble.socks.SocksModule
|
import org.briarproject.bramble.socks.SocksModule
|
||||||
import org.briarproject.bramble.system.ClockModule
|
import org.briarproject.bramble.system.ClockModule
|
||||||
import org.briarproject.bramble.system.DefaultTaskSchedulerModule
|
import org.briarproject.bramble.system.DefaultTaskSchedulerModule
|
||||||
@@ -28,6 +30,7 @@ import org.briarproject.bramble.system.DesktopSecureRandomModule
|
|||||||
import org.briarproject.bramble.system.JavaSystemModule
|
import org.briarproject.bramble.system.JavaSystemModule
|
||||||
import org.briarproject.bramble.util.OsUtils.isLinux
|
import org.briarproject.bramble.util.OsUtils.isLinux
|
||||||
import org.briarproject.bramble.util.OsUtils.isMac
|
import org.briarproject.bramble.util.OsUtils.isMac
|
||||||
|
import org.briarproject.bramble.util.OsUtils.isWindows
|
||||||
import org.briarproject.briar.headless.blogs.HeadlessBlogModule
|
import org.briarproject.briar.headless.blogs.HeadlessBlogModule
|
||||||
import org.briarproject.briar.headless.contact.HeadlessContactModule
|
import org.briarproject.briar.headless.contact.HeadlessContactModule
|
||||||
import org.briarproject.briar.headless.event.HeadlessEventModule
|
import org.briarproject.briar.headless.event.HeadlessEventModule
|
||||||
@@ -71,6 +74,12 @@ internal class HeadlessModule(private val appDir: File) {
|
|||||||
return HeadlessDatabaseConfig(dbDir, keyDir)
|
return HeadlessDatabaseConfig(dbDir, keyDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@MailboxDirectory
|
||||||
|
internal fun provideMailboxDirectory(): File {
|
||||||
|
return File(appDir, "mailbox")
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@TorDirectory
|
@TorDirectory
|
||||||
internal fun provideTorDirectory(): File {
|
internal fun provideTorDirectory(): File {
|
||||||
@@ -87,9 +96,15 @@ internal class HeadlessModule(private val appDir: File) {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
internal fun providePluginConfig(tor: UnixTorPluginFactory): PluginConfig {
|
internal fun providePluginConfig(
|
||||||
val duplex: List<DuplexPluginFactory> =
|
unixTor: UnixTorPluginFactory,
|
||||||
if (isLinux() || isMac()) listOf(tor) else emptyList()
|
winTor: WindowsTorPluginFactory
|
||||||
|
): PluginConfig {
|
||||||
|
val duplex: List<DuplexPluginFactory> = when {
|
||||||
|
isLinux() || isMac() -> listOf(unixTor)
|
||||||
|
isWindows() -> listOf(winTor)
|
||||||
|
else -> emptyList()
|
||||||
|
}
|
||||||
return object : PluginConfig {
|
return object : PluginConfig {
|
||||||
override fun getDuplexFactories(): Collection<DuplexPluginFactory> = duplex
|
override fun getDuplexFactories(): Collection<DuplexPluginFactory> = duplex
|
||||||
override fun getSimplexFactories(): Collection<SimplexPluginFactory> = emptyList()
|
override fun getSimplexFactories(): Collection<SimplexPluginFactory> = emptyList()
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import com.github.ajalt.clikt.parameters.options.option
|
|||||||
import com.github.ajalt.clikt.parameters.types.int
|
import com.github.ajalt.clikt.parameters.types.int
|
||||||
import org.bouncycastle.util.encoders.Base64.toBase64String
|
import org.bouncycastle.util.encoders.Base64.toBase64String
|
||||||
import org.briarproject.bramble.BrambleCoreEagerSingletons
|
import org.briarproject.bramble.BrambleCoreEagerSingletons
|
||||||
|
import org.briarproject.bramble.util.OsUtils.isLinux
|
||||||
|
import org.briarproject.bramble.util.OsUtils.isMac
|
||||||
import org.briarproject.briar.BriarCoreEagerSingletons
|
import org.briarproject.briar.BriarCoreEagerSingletons
|
||||||
import org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY
|
import org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -90,11 +92,13 @@ private class Main : CliktCommand(
|
|||||||
} else if (!file.isDirectory) {
|
} else if (!file.isDirectory) {
|
||||||
throw IOException("Data dir is not a directory: ${file.absolutePath}")
|
throw IOException("Data dir is not a directory: ${file.absolutePath}")
|
||||||
}
|
}
|
||||||
val perms = HashSet<PosixFilePermission>()
|
if (isLinux() || isMac()) {
|
||||||
perms.add(OWNER_READ)
|
val perms = HashSet<PosixFilePermission>()
|
||||||
perms.add(OWNER_WRITE)
|
perms.add(OWNER_READ)
|
||||||
perms.add(OWNER_EXECUTE)
|
perms.add(OWNER_WRITE)
|
||||||
setPosixFilePermissions(file.toPath(), perms)
|
perms.add(OWNER_EXECUTE)
|
||||||
|
setPosixFilePermissions(file.toPath(), perms)
|
||||||
|
}
|
||||||
return file
|
return file
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import dagger.Module
|
|||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
import org.briarproject.bramble.account.AccountModule
|
import org.briarproject.bramble.account.AccountModule
|
||||||
import org.briarproject.bramble.api.db.DatabaseConfig
|
import org.briarproject.bramble.api.db.DatabaseConfig
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxDirectory
|
||||||
import org.briarproject.bramble.api.plugin.PluginConfig
|
import org.briarproject.bramble.api.plugin.PluginConfig
|
||||||
import org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_CONTROL_PORT
|
import org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_CONTROL_PORT
|
||||||
import org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_SOCKS_PORT
|
import org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_SOCKS_PORT
|
||||||
@@ -68,6 +69,12 @@ internal class HeadlessTestModule(private val appDir: File) {
|
|||||||
return HeadlessDatabaseConfig(dbDir, keyDir)
|
return HeadlessDatabaseConfig(dbDir, keyDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@MailboxDirectory
|
||||||
|
internal fun provideMailboxDirectory(): File {
|
||||||
|
return File(appDir, "mailbox")
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@TorSocksPort
|
@TorSocksPort
|
||||||
internal fun provideTorSocksPort(): Int = DEFAULT_SOCKS_PORT
|
internal fun provideTorSocksPort(): Int = DEFAULT_SOCKS_PORT
|
||||||
|
|||||||
Reference in New Issue
Block a user