mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-11 18:29:05 +01:00
Compare commits
24 Commits
beta-1.4.8
...
1528-log-t
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
580be7cfdc | ||
|
|
dbcea3e1d1 | ||
|
|
75b5c92495 | ||
|
|
bcc98cc4c9 | ||
|
|
2d605089bc | ||
|
|
01f8be1b66 | ||
|
|
eac6d0aa40 | ||
|
|
62af5e858c | ||
|
|
2201585a34 | ||
|
|
78f4dee43d | ||
|
|
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"
|
||||
- bramble-core/src/test/bash/wait-for-mailbox.sh
|
||||
- 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
|
||||
|
||||
@@ -16,6 +16,17 @@ public interface ConnectionManager {
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
@@ -46,4 +57,21 @@ public interface ConnectionManager {
|
||||
*/
|
||||
void manageOutgoingConnection(PendingContactId p, TransportId t,
|
||||
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;
|
||||
|
||||
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 org.briarproject.bramble.api.transport.TransportConstants.MAX_FRAME_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 {
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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 java.util.List;
|
||||
|
||||
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_NUM_CONNECTION_FAILURES;
|
||||
import static org.briarproject.bramble.api.mailbox.MailboxHelper.getHighestCommonMajorVersion;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
@@ -13,12 +17,15 @@ public class MailboxStatus {
|
||||
|
||||
private final long lastAttempt, lastSuccess;
|
||||
private final int attemptsSinceSuccess;
|
||||
private final List<MailboxVersion> serverSupports;
|
||||
|
||||
public MailboxStatus(long lastAttempt, long lastSuccess,
|
||||
int attemptsSinceSuccess) {
|
||||
int attemptsSinceSuccess,
|
||||
List<MailboxVersion> serverSupports) {
|
||||
this.lastAttempt = lastAttempt;
|
||||
this.lastSuccess = lastSuccess;
|
||||
this.attemptsSinceSuccess = attemptsSinceSuccess;
|
||||
this.serverSupports = serverSupports;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,4 +74,13 @@ public class MailboxStatus {
|
||||
return attemptsSinceSuccess >= PROBLEM_NUM_CONNECTION_FAILURES &&
|
||||
(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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -50,6 +51,7 @@ import java.util.Random;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.crypto.Cipher;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES;
|
||||
@@ -335,4 +337,13 @@ public class TestUtils {
|
||||
}
|
||||
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];
|
||||
read(in, tag);
|
||||
return tag;
|
||||
|
||||
@@ -67,7 +67,15 @@ class ConnectionManagerImpl implements ConnectionManager {
|
||||
TransportConnectionReader r) {
|
||||
ioExecutor.execute(new IncomingSimplexSyncConnection(keyManager,
|
||||
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
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
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.contact.ContactId;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
@@ -15,6 +17,8 @@ import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
|
||||
@@ -23,6 +27,8 @@ class IncomingSimplexSyncConnection extends SyncConnection implements Runnable {
|
||||
|
||||
private final TransportId transportId;
|
||||
private final TransportConnectionReader reader;
|
||||
@Nullable
|
||||
private final TagController tagController;
|
||||
|
||||
IncomingSimplexSyncConnection(KeyManager keyManager,
|
||||
ConnectionRegistry connectionRegistry,
|
||||
@@ -30,33 +36,50 @@ class IncomingSimplexSyncConnection extends SyncConnection implements Runnable {
|
||||
StreamWriterFactory streamWriterFactory,
|
||||
SyncSessionFactory syncSessionFactory,
|
||||
TransportPropertyManager transportPropertyManager,
|
||||
TransportId transportId, TransportConnectionReader reader) {
|
||||
TransportId transportId,
|
||||
TransportConnectionReader reader,
|
||||
@Nullable TagController tagController) {
|
||||
super(keyManager, connectionRegistry, streamReaderFactory,
|
||||
streamWriterFactory, syncSessionFactory,
|
||||
transportPropertyManager);
|
||||
this.transportId = transportId;
|
||||
this.reader = reader;
|
||||
this.tagController = tagController;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// 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) {
|
||||
LOG.info("Unrecognised tag");
|
||||
onError(false);
|
||||
onError();
|
||||
return;
|
||||
}
|
||||
ContactId contactId = ctx.getContactId();
|
||||
if (contactId == null) {
|
||||
LOG.warning("Received rendezvous stream, expected contact");
|
||||
onError(true);
|
||||
onError(tag);
|
||||
return;
|
||||
}
|
||||
if (ctx.isHandshakeMode()) {
|
||||
// TODO: Support handshake mode for contacts
|
||||
LOG.warning("Received handshake tag, expected rotation mode");
|
||||
onError(true);
|
||||
onError(tag);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@@ -65,15 +88,33 @@ class IncomingSimplexSyncConnection extends SyncConnection implements Runnable {
|
||||
LOG.info("Ignoring priority for simplex connection");
|
||||
// Create and run the incoming session
|
||||
createIncomingSession(ctx, reader, handler).run();
|
||||
// Success
|
||||
markTagAsRecognisedIfRequired(false, tag);
|
||||
reader.dispose(false, true);
|
||||
} catch (IOException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
onError(true);
|
||||
onError(tag);
|
||||
}
|
||||
}
|
||||
|
||||
private void onError(boolean recognised) {
|
||||
disposeOnError(reader, recognised);
|
||||
private void onError() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,15 +3,16 @@ package org.briarproject.bramble.db;
|
||||
class DatabaseTypes {
|
||||
|
||||
private final String hashType, secretType, binaryType;
|
||||
private final String counterType, stringType;
|
||||
private final String counterType, stringType, schemaQuery;
|
||||
|
||||
public DatabaseTypes(String hashType, String secretType, String binaryType,
|
||||
String counterType, String stringType) {
|
||||
DatabaseTypes(String hashType, String secretType, String binaryType,
|
||||
String counterType, String stringType, String schemaQuery) {
|
||||
this.hashType = hashType;
|
||||
this.secretType = secretType;
|
||||
this.binaryType = binaryType;
|
||||
this.counterType = counterType;
|
||||
this.stringType = stringType;
|
||||
this.schemaQuery = schemaQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -31,4 +32,8 @@ class DatabaseTypes {
|
||||
s = s.replaceAll("_STRING", stringType);
|
||||
return s;
|
||||
}
|
||||
|
||||
String getSchemaQuery() {
|
||||
return schemaQuery;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,8 +41,9 @@ class H2Database extends JdbcDatabase {
|
||||
private static final String BINARY_TYPE = "BINARY";
|
||||
private static final String COUNTER_TYPE = "INT NOT NULL AUTO_INCREMENT";
|
||||
private static final String STRING_TYPE = "VARCHAR";
|
||||
private static final String SCHEMA_QUERY = "SHOW TABLES";
|
||||
private static final DatabaseTypes dbTypes = new DatabaseTypes(HASH_TYPE,
|
||||
SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE);
|
||||
SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE, SCHEMA_QUERY);
|
||||
|
||||
private final DatabaseConfig config;
|
||||
private final String url;
|
||||
|
||||
@@ -41,8 +41,11 @@ class HyperSqlDatabase extends JdbcDatabase {
|
||||
private static final String COUNTER_TYPE =
|
||||
"INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY(START WITH 1)";
|
||||
private static final String STRING_TYPE = "VARCHAR";
|
||||
private static final String SCHEMA_QUERY =
|
||||
"SELECT TABLE_NAME FROM INFORMATION_SCHEMA.SYSTEM_TABLES"
|
||||
+ " WHERE TABLE_TYPE='TABLE'";
|
||||
private static final DatabaseTypes dbTypes = new DatabaseTypes(HASH_TYPE,
|
||||
SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE);
|
||||
SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE, SCHEMA_QUERY);
|
||||
|
||||
private final DatabaseConfig config;
|
||||
private final String url;
|
||||
|
||||
@@ -405,6 +405,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
boolean compact;
|
||||
Connection txn = startTransaction();
|
||||
try {
|
||||
if (LOG.isLoggable(INFO)) logSchema(txn);
|
||||
if (reopen) {
|
||||
Settings s = getSettings(txn, DB_SETTINGS_NAMESPACE);
|
||||
wasDirtyOnInitialisation = isDirty(s);
|
||||
@@ -447,6 +448,24 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
return wasDirtyOnInitialisation;
|
||||
}
|
||||
|
||||
private void logSchema(Connection txn) throws DbException {
|
||||
Statement s = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
s = txn.createStatement();
|
||||
rs = s.executeQuery(dbTypes.getSchemaQuery());
|
||||
StringBuilder sb = new StringBuilder("Tables:");
|
||||
while (rs.next()) sb.append('\n').append(rs.getString(1));
|
||||
LOG.info(sb.toString());
|
||||
rs.close();
|
||||
s.close();
|
||||
} catch (SQLException e) {
|
||||
tryToClose(rs, LOG, WARNING);
|
||||
tryToClose(s, LOG, WARNING);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the schema version stored in the database with the schema
|
||||
* version used by the current code and applies any suitable migrations to
|
||||
|
||||
@@ -190,6 +190,10 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (state == STOPPING) {
|
||||
LOG.info("Already stopped");
|
||||
return;
|
||||
}
|
||||
LOG.info("Stopping services");
|
||||
state = STOPPING;
|
||||
eventBus.broadcast(new LifecycleEvent(STOPPING));
|
||||
|
||||
@@ -7,7 +7,6 @@ import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxFileId;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxFolderId;
|
||||
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.nullsafety.NotNullByDefault;
|
||||
|
||||
@@ -19,18 +18,9 @@ import java.util.List;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
@NotNullByDefault
|
||||
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)
|
||||
throws IOException, ApiException;
|
||||
|
||||
|
||||
@@ -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.contact.ContactManager;
|
||||
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.mailbox.MailboxManager;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
||||
@@ -21,10 +22,10 @@ import javax.inject.Singleton;
|
||||
import dagger.Module;
|
||||
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.MAJOR_VERSION;
|
||||
import static org.briarproject.bramble.api.mailbox.MailboxUpdateManager.MINOR_VERSION;
|
||||
import static org.briarproject.bramble.mailbox.MailboxApi.CLIENT_SUPPORTS;
|
||||
|
||||
@Module
|
||||
public class MailboxModule {
|
||||
@@ -34,6 +35,8 @@ public class MailboxModule {
|
||||
MailboxUpdateValidator mailboxUpdateValidator;
|
||||
@Inject
|
||||
MailboxUpdateManager mailboxUpdateManager;
|
||||
@Inject
|
||||
MailboxFileManager mailboxFileManager;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@@ -101,4 +104,14 @@ public class MailboxModule {
|
||||
}
|
||||
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.inject.Inject;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
||||
|
||||
@Immutable
|
||||
@@ -60,15 +61,7 @@ class MailboxSettingsManagerImpl implements MailboxSettingsManager {
|
||||
String onion = s.get(SETTINGS_KEY_ONION);
|
||||
String token = s.get(SETTINGS_KEY_TOKEN);
|
||||
if (isNullOrEmpty(onion) || isNullOrEmpty(token)) return null;
|
||||
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]));
|
||||
}
|
||||
List<MailboxVersion> serverSupports = parseServerSupports(s);
|
||||
try {
|
||||
MailboxAuthToken tokenId = MailboxAuthToken.fromString(token);
|
||||
return new MailboxProperties(onion, tokenId, serverSupports);
|
||||
@@ -115,18 +108,28 @@ class MailboxSettingsManagerImpl implements MailboxSettingsManager {
|
||||
long lastAttempt = s.getLong(SETTINGS_KEY_LAST_ATTEMPT, -1);
|
||||
long lastSuccess = s.getLong(SETTINGS_KEY_LAST_SUCCESS, -1);
|
||||
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
|
||||
public void recordSuccessfulConnection(Transaction txn, long now)
|
||||
throws DbException {
|
||||
Settings oldSettings =
|
||||
settingsManager.getSettings(txn, SETTINGS_NAMESPACE);
|
||||
Settings s = new Settings();
|
||||
s.putLong(SETTINGS_KEY_LAST_ATTEMPT, now);
|
||||
s.putLong(SETTINGS_KEY_LAST_SUCCESS, now);
|
||||
s.putInt(SETTINGS_KEY_ATTEMPTS, 0);
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -141,7 +144,9 @@ class MailboxSettingsManagerImpl implements MailboxSettingsManager {
|
||||
newSettings.putLong(SETTINGS_KEY_LAST_ATTEMPT, now);
|
||||
newSettings.putInt(SETTINGS_KEY_ATTEMPTS, newAttempts);
|
||||
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));
|
||||
if (status.hasProblem(now)) txn.attach(new MailboxProblemEvent());
|
||||
}
|
||||
@@ -165,4 +170,19 @@ class MailboxSettingsManagerImpl implements MailboxSettingsManager {
|
||||
if (isNullOrEmpty(filename)) return null;
|
||||
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,45 @@
|
||||
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);
|
||||
|
||||
interface TorReachabilityObserver {
|
||||
|
||||
void onTorReachable();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
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 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();
|
||||
}
|
||||
}
|
||||
@@ -67,6 +67,7 @@ abstract class AbstractRemovableDrivePlugin implements SimplexPlugin {
|
||||
public void start() {
|
||||
callback.mergeLocalProperties(
|
||||
new TransportProperties(singletonMap(PROP_SUPPORTED, "true")));
|
||||
callback.pluginStateChanged(ACTIVE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -11,6 +11,7 @@ import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
@@ -29,11 +30,6 @@ abstract class FilePlugin implements SimplexPlugin {
|
||||
protected final PluginCallback callback;
|
||||
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) {
|
||||
this.callback = callback;
|
||||
this.maxLatency = maxLatency;
|
||||
@@ -50,9 +46,8 @@ abstract class FilePlugin implements SimplexPlugin {
|
||||
String path = p.get(PROP_PATH);
|
||||
if (isNullOrEmpty(path)) return null;
|
||||
try {
|
||||
File file = new File(path);
|
||||
FileInputStream in = new FileInputStream(file);
|
||||
return new FileTransportReader(file, in, this);
|
||||
FileInputStream in = new FileInputStream(path);
|
||||
return new TransportInputStreamReader(in);
|
||||
} catch (IOException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
return null;
|
||||
@@ -70,8 +65,8 @@ abstract class FilePlugin implements SimplexPlugin {
|
||||
LOG.info("Failed to create file");
|
||||
return null;
|
||||
}
|
||||
FileOutputStream out = new FileOutputStream(file);
|
||||
return new FileTransportWriter(file, out, this);
|
||||
OutputStream out = new FileOutputStream(file);
|
||||
return new TransportOutputStreamWriter(this, out);
|
||||
} catch (IOException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package org.briarproject.bramble.db;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.test.TestUtils;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
@@ -10,10 +11,18 @@ import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
|
||||
import static org.briarproject.bramble.test.TestUtils.isCryptoStrengthUnlimited;
|
||||
import static org.junit.Assume.assumeTrue;
|
||||
|
||||
public class BasicHyperSqlTest extends BasicDatabaseTest {
|
||||
|
||||
private final SecretKey key = TestUtils.getSecretKey();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
assumeTrue(isCryptoStrengthUnlimited());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getBinaryType() {
|
||||
return "BINARY(32)";
|
||||
|
||||
@@ -3,9 +3,18 @@ package org.briarproject.bramble.db;
|
||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.api.sync.MessageFactory;
|
||||
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 {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
assumeTrue(isCryptoStrengthUnlimited());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JdbcDatabase createDatabase(DatabaseConfig config,
|
||||
MessageFactory messageFactory, Clock clock) {
|
||||
|
||||
@@ -1,13 +1,22 @@
|
||||
package org.briarproject.bramble.db;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.util.List;
|
||||
|
||||
import static org.briarproject.bramble.test.TestUtils.isCryptoStrengthUnlimited;
|
||||
import static org.junit.Assume.assumeTrue;
|
||||
|
||||
@NotNullByDefault
|
||||
public class HyperSqlMigrationTest extends DatabaseMigrationTest {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
assumeTrue(isCryptoStrengthUnlimited());
|
||||
}
|
||||
|
||||
@Override
|
||||
Database<Connection> createDatabase(
|
||||
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.test.BrambleMockTestCase;
|
||||
import org.briarproject.bramble.test.DbExpectations;
|
||||
import org.jmock.Expectations;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
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.SUCCESS;
|
||||
import static org.briarproject.bramble.api.system.Clock.MAX_REASONABLE_TIME_MS;
|
||||
@@ -57,6 +61,7 @@ public class LifecycleManagerImplTest extends BrambleMockTestCase {
|
||||
lifecycleManager.registerOpenDatabaseHook(hook);
|
||||
|
||||
assertEquals(SUCCESS, lifecycleManager.startServices(dbKey));
|
||||
assertEquals(RUNNING, lifecycleManager.getLifecycleState());
|
||||
assertTrue(called.get());
|
||||
}
|
||||
|
||||
@@ -69,6 +74,7 @@ public class LifecycleManagerImplTest extends BrambleMockTestCase {
|
||||
}});
|
||||
|
||||
assertEquals(CLOCK_ERROR, lifecycleManager.startServices(dbKey));
|
||||
assertEquals(STARTING, lifecycleManager.getLifecycleState());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -80,5 +86,40 @@ public class LifecycleManagerImplTest extends BrambleMockTestCase {
|
||||
}});
|
||||
|
||||
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 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.MailboxApi.CLIENT_SUPPORTS;
|
||||
import static org.briarproject.bramble.test.TestUtils.getMailboxProperties;
|
||||
|
||||
public class ConnectivityCheckerImplTest extends BrambleMockTestCase {
|
||||
|
||||
@@ -13,7 +13,7 @@ import org.junit.Test;
|
||||
import java.io.IOException;
|
||||
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.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@@ -35,7 +35,7 @@ import okio.Buffer;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
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.getMailboxProperties;
|
||||
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
|
||||
public void testRecordsSuccess() throws Exception {
|
||||
Transaction txn = new Transaction(null, false);
|
||||
Settings oldSettings = new Settings();
|
||||
oldSettings
|
||||
.putIntArray(SETTINGS_KEY_SERVER_SUPPORTS, serverSupportsInts);
|
||||
Settings expectedSettings = new Settings();
|
||||
expectedSettings.putLong(SETTINGS_KEY_LAST_ATTEMPT, now);
|
||||
expectedSettings.putLong(SETTINGS_KEY_LAST_SUCCESS, now);
|
||||
expectedSettings.putInt(SETTINGS_KEY_ATTEMPTS, 0);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(settingsManager).getSettings(txn, SETTINGS_NAMESPACE);
|
||||
will(returnValue(oldSettings));
|
||||
oneOf(settingsManager).mergeSettings(txn, expectedSettings,
|
||||
SETTINGS_NAMESPACE);
|
||||
}});
|
||||
|
||||
@@ -17,7 +17,7 @@ import org.junit.Test;
|
||||
import java.io.IOException;
|
||||
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.junit.Assert.assertFalse;
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import org.briarproject.bramble.system.DefaultWakefulIoExecutorModule;
|
||||
import org.briarproject.bramble.system.TimeTravelModule;
|
||||
import org.briarproject.bramble.test.TestDatabaseConfigModule;
|
||||
import org.briarproject.bramble.test.TestFeatureFlagModule;
|
||||
import org.briarproject.bramble.test.TestMailboxDirectoryModule;
|
||||
import org.briarproject.bramble.test.TestSecureRandomModule;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
@@ -27,6 +28,7 @@ import dagger.Component;
|
||||
DefaultWakefulIoExecutorModule.class,
|
||||
TestDatabaseConfigModule.class,
|
||||
TestFeatureFlagModule.class,
|
||||
TestMailboxDirectoryModule.class,
|
||||
RemovableDriveIntegrationTestModule.class,
|
||||
RemovableDriveModule.class,
|
||||
TestSecureRandomModule.class,
|
||||
|
||||
@@ -13,6 +13,7 @@ import dagger.Module;
|
||||
DefaultWakefulIoExecutorModule.class,
|
||||
TestDatabaseConfigModule.class,
|
||||
TestFeatureFlagModule.class,
|
||||
TestMailboxDirectoryModule.class,
|
||||
TestPluginConfigModule.class,
|
||||
TestSecureRandomModule.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");
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
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.BluetoothConstants;
|
||||
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.plugin.bluetooth.AndroidBluetoothPluginFactory;
|
||||
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.tor.AndroidTorPluginFactory;
|
||||
import org.briarproject.bramble.util.AndroidUtils;
|
||||
@@ -63,6 +65,7 @@ import org.briarproject.briar.api.test.TestAvatarCreator;
|
||||
|
||||
import java.io.File;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -76,7 +79,6 @@ import dagger.Provides;
|
||||
import static android.content.Context.MODE_PRIVATE;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_CONTROL_PORT;
|
||||
@@ -156,6 +158,13 @@ public class AppModule {
|
||||
return new AndroidDatabaseConfig(dbDir, keyDir, keyStrengthener);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@MailboxDirectory
|
||||
File provideMailboxDirectory(Application app) {
|
||||
return app.getDir("mailbox", MODE_PRIVATE);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@TorDirectory
|
||||
@@ -190,7 +199,7 @@ public class AppModule {
|
||||
PluginConfig providePluginConfig(AndroidBluetoothPluginFactory bluetooth,
|
||||
AndroidTorPluginFactory tor, AndroidLanTcpPluginFactory lan,
|
||||
AndroidRemovableDrivePluginFactory drive,
|
||||
FeatureFlags featureFlags) {
|
||||
MailboxPluginFactory mailbox, FeatureFlags featureFlags) {
|
||||
@NotNullByDefault
|
||||
PluginConfig pluginConfig = new PluginConfig() {
|
||||
|
||||
@@ -201,8 +210,10 @@ public class AppModule {
|
||||
|
||||
@Override
|
||||
public Collection<SimplexPluginFactory> getSimplexFactories() {
|
||||
if (SDK_INT >= 19) return singletonList(drive);
|
||||
else return emptyList();
|
||||
List<SimplexPluginFactory> simplex = new ArrayList<>();
|
||||
if (featureFlags.shouldEnableMailbox()) simplex.add(mailbox);
|
||||
if (SDK_INT >= 19) simplex.add(drive);
|
||||
return simplex;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -36,6 +36,7 @@ import static android.view.View.VISIBLE;
|
||||
import static androidx.core.content.ContextCompat.getColor;
|
||||
import static androidx.core.widget.ImageViewCompat.setImageTintList;
|
||||
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.util.UiUtils.MIN_DATE_RESOLUTION;
|
||||
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 {
|
||||
|
||||
static final String TAG = MailboxStatusFragment.class.getName();
|
||||
private static final int NUM_FAILURES = 4;
|
||||
|
||||
@Inject
|
||||
ViewModelProvider.Factory viewModelFactory;
|
||||
@@ -59,8 +59,7 @@ public class MailboxStatusFragment extends Fragment {
|
||||
private boolean showUnlinkWarning = true;
|
||||
|
||||
private ImageView imageView;
|
||||
private TextView statusTitleView;
|
||||
private TextView statusInfoView;
|
||||
private TextView statusTitleView, statusMessageView, statusInfoView;
|
||||
private Button wizardButton;
|
||||
private Button unlinkButton;
|
||||
private ProgressBar unlinkProgress;
|
||||
@@ -95,6 +94,7 @@ public class MailboxStatusFragment extends Fragment {
|
||||
|
||||
imageView = v.findViewById(R.id.imageView);
|
||||
statusTitleView = v.findViewById(R.id.statusTitleView);
|
||||
statusMessageView = v.findViewById(R.id.statusMessageView);
|
||||
statusInfoView = v.findViewById(R.id.statusInfoView);
|
||||
viewModel.getStatus()
|
||||
.observe(getViewLifecycleOwner(), this::onMailboxStateChanged);
|
||||
@@ -133,29 +133,51 @@ public class MailboxStatusFragment extends Fragment {
|
||||
@ColorRes int tintRes;
|
||||
@DrawableRes int iconRes;
|
||||
String title;
|
||||
if (status.getAttemptsSinceSuccess() == 0) {
|
||||
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);
|
||||
} 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 {
|
||||
String message = null;
|
||||
if (status.hasProblem(System.currentTimeMillis())) {
|
||||
tintRes = R.color.briar_red_500;
|
||||
title = getString(R.string.mailbox_status_failure_title);
|
||||
iconRes = R.drawable.alerts_and_states_error;
|
||||
showUnlinkWarning = false;
|
||||
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);
|
||||
int color = getColor(requireContext(), tintRes);
|
||||
setImageTintList(imageView, ColorStateList.valueOf(color));
|
||||
statusTitleView.setText(title);
|
||||
if (message == null) {
|
||||
statusMessageView.setVisibility(GONE);
|
||||
} else {
|
||||
statusMessageView.setVisibility(VISIBLE);
|
||||
statusMessageView.setText(message);
|
||||
}
|
||||
|
||||
long lastSuccess = status.getTimeOfLastSuccess();
|
||||
String lastConnectionText;
|
||||
|
||||
@@ -208,16 +208,12 @@ class MailboxViewModel extends DbViewModel
|
||||
|
||||
LiveData<Boolean> checkConnection() {
|
||||
MutableLiveData<Boolean> liveData = new MutableLiveData<>();
|
||||
checkConnection(success -> {
|
||||
liveData.postValue(success);
|
||||
if (!success) onConnectionCheckFailure();
|
||||
});
|
||||
checkConnection(liveData::postValue);
|
||||
return liveData;
|
||||
}
|
||||
|
||||
void checkConnectionFromWizard() {
|
||||
checkConnection(success -> {
|
||||
if (!success) onConnectionCheckFailure();
|
||||
boolean isOnline = isTorActive();
|
||||
// make UI move back to status fragment by changing pairingState
|
||||
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
|
||||
void unlink() {
|
||||
ioExecutor.execute(() -> {
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
package org.briarproject.briar.android.reporting;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.ActivityManager;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.Context;
|
||||
import android.content.pm.FeatureInfo;
|
||||
@@ -19,7 +18,6 @@ import android.net.NetworkInfo;
|
||||
import android.net.wifi.WifiInfo;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.os.Build;
|
||||
import android.os.Debug;
|
||||
import android.os.Environment;
|
||||
|
||||
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.ReportItem;
|
||||
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.Metrics;
|
||||
|
||||
@@ -76,14 +75,14 @@ class BriarReportCollector {
|
||||
}
|
||||
|
||||
ReportData collectReportData(@Nullable Throwable t, long appStartTime,
|
||||
String logs) {
|
||||
String logs, MemoryStats memoryStats) {
|
||||
ReportData reportData = new ReportData()
|
||||
.add(getBasicInfo(t))
|
||||
.add(getDeviceInfo());
|
||||
if (t != null) reportData.add(getStacktrace(t));
|
||||
return reportData
|
||||
.add(getTimeInfo(appStartTime))
|
||||
.add(getMemory())
|
||||
.add(getMemory(memoryStats))
|
||||
.add(getStorage())
|
||||
.add(getConnectivity())
|
||||
.add(getNetworkUsage())
|
||||
@@ -154,26 +153,22 @@ class BriarReportCollector {
|
||||
return format.format(new Date(time));
|
||||
}
|
||||
|
||||
private ReportItem getMemory() {
|
||||
private ReportItem getMemory(MemoryStats stats) {
|
||||
MultiReportInfo memInfo = new MultiReportInfo();
|
||||
|
||||
// System memory
|
||||
ActivityManager am = getSystemService(ctx, ActivityManager.class);
|
||||
ActivityManager.MemoryInfo mem = new ActivityManager.MemoryInfo();
|
||||
requireNonNull(am).getMemoryInfo(mem);
|
||||
memInfo.add("SystemMemoryTotal", mem.totalMem);
|
||||
memInfo.add("SystemMemoryFree", mem.availMem);
|
||||
memInfo.add("SystemMemoryThreshold", mem.threshold);
|
||||
memInfo.add("SystemMemoryLow", mem.lowMemory);
|
||||
memInfo.add("SystemMemoryTotal", stats.systemMemoryTotal);
|
||||
memInfo.add("SystemMemoryFree", stats.systemMemoryFree);
|
||||
memInfo.add("SystemMemoryThreshold", stats.systemMemoryThreshold);
|
||||
memInfo.add("SystemMemoryLow", stats.systemMemoryLow);
|
||||
|
||||
// Virtual machine memory
|
||||
Runtime runtime = Runtime.getRuntime();
|
||||
memInfo.add("VirtualMachineMemoryTotal", runtime.totalMemory());
|
||||
memInfo.add("VirtualMachineMemoryFree", runtime.freeMemory());
|
||||
memInfo.add("VirtualMachineMemoryMaximum", runtime.maxMemory());
|
||||
memInfo.add("NativeHeapTotal", Debug.getNativeHeapSize());
|
||||
memInfo.add("NativeHeapAllocated", Debug.getNativeHeapAllocatedSize());
|
||||
memInfo.add("NativeHeapFree", Debug.getNativeHeapFreeSize());
|
||||
memInfo.add("VirtualMachineMemoryTotal", stats.vmMemoryTotal);
|
||||
memInfo.add("VirtualMachineMemoryFree", stats.vmMemoryFree);
|
||||
memInfo.add("VirtualMachineMemoryMaximum", stats.vmMemoryMax);
|
||||
memInfo.add("NativeHeapTotal", stats.nativeHeapTotal);
|
||||
memInfo.add("NativeHeapAllocated", stats.nativeHeapAllocated);
|
||||
memInfo.add("NativeHeapFree", stats.nativeHeapFree);
|
||||
|
||||
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.BaseFragmentListener;
|
||||
import org.briarproject.briar.android.logout.HideUiActivity;
|
||||
import org.briarproject.briar.api.android.MemoryStats;
|
||||
|
||||
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_APP_START_TIME = "appStartTime";
|
||||
public static final String EXTRA_APP_LOGCAT = "logcat";
|
||||
public static final String EXTRA_MEMORY_STATS = "memoryStats";
|
||||
|
||||
@Inject
|
||||
ViewModelProvider.Factory viewModelFactory;
|
||||
@@ -60,7 +62,9 @@ public class CrashReportActivity extends BaseActivity
|
||||
Throwable t = (Throwable) intent.getSerializableExtra(EXTRA_THROWABLE);
|
||||
long appStartTime = intent.getLongExtra(EXTRA_APP_START_TIME, -1);
|
||||
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 -> {
|
||||
if (show) displayFragment(true);
|
||||
});
|
||||
@@ -87,7 +91,7 @@ public class CrashReportActivity extends BaseActivity
|
||||
exit();
|
||||
}
|
||||
|
||||
void displayFragment(boolean showReportForm) {
|
||||
private void displayFragment(boolean showReportForm) {
|
||||
BaseFragment f;
|
||||
if (showReportForm) {
|
||||
f = new ReportFormFragment();
|
||||
@@ -101,7 +105,7 @@ public class CrashReportActivity extends BaseActivity
|
||||
.commit();
|
||||
}
|
||||
|
||||
void exit() {
|
||||
private void exit() {
|
||||
if (!viewModel.isFeedback()) {
|
||||
Intent i = new Intent(this, HideUiActivity.class);
|
||||
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.viewmodel.LiveEvent;
|
||||
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
|
||||
import org.briarproject.briar.api.android.MemoryStats;
|
||||
import org.briarproject.briar.api.android.NetworkUsageMetrics;
|
||||
import org.json.JSONException;
|
||||
|
||||
@@ -84,7 +85,8 @@ class ReportViewModel extends AndroidViewModel {
|
||||
}
|
||||
|
||||
void init(@Nullable Throwable t, long appStartTime,
|
||||
@Nullable byte[] logKey, @Nullable String initialComment) {
|
||||
@Nullable byte[] logKey, @Nullable String initialComment,
|
||||
MemoryStats memoryStats) {
|
||||
this.initialComment = initialComment;
|
||||
isFeedback = t == null;
|
||||
if (reportData.getValue() == null) new SingleShotAndroidExecutor(() -> {
|
||||
@@ -102,8 +104,8 @@ class ReportViewModel extends AndroidViewModel {
|
||||
logHandler.getRecentLogRecords());
|
||||
}
|
||||
}
|
||||
ReportData data =
|
||||
collector.collectReportData(t, appStartTime, decryptedLogs);
|
||||
ReportData data = collector.collectReportData(t, appStartTime,
|
||||
decryptedLogs, memoryStats);
|
||||
reportData.postValue(data);
|
||||
}).start();
|
||||
}
|
||||
@@ -150,7 +152,7 @@ class ReportViewModel extends AndroidViewModel {
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
return reportData;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package org.briarproject.briar.android.util;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManager.MemoryInfo;
|
||||
import android.app.KeyguardManager;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
@@ -10,6 +12,7 @@ import android.content.res.Resources;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.location.LocationManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Debug;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.SpannableStringBuilder;
|
||||
@@ -38,6 +41,7 @@ import org.briarproject.bramble.util.StringUtils;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.reporting.FeedbackActivity;
|
||||
import org.briarproject.briar.android.view.ArticleMovementMethod;
|
||||
import org.briarproject.briar.api.android.MemoryStats;
|
||||
|
||||
import java.util.Locale;
|
||||
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_START_TIME;
|
||||
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;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@@ -429,12 +434,27 @@ public class UiUtils {
|
||||
Class<? extends FragmentActivity> activity, @Nullable Throwable t,
|
||||
@Nullable Long appStartTime, @Nullable byte[] logKey, @Nullable
|
||||
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);
|
||||
dialogIntent.setFlags(FLAG_ACTIVITY_NEW_TASK);
|
||||
dialogIntent.putExtra(EXTRA_THROWABLE, t);
|
||||
dialogIntent.putExtra(EXTRA_APP_START_TIME, appStartTime);
|
||||
dialogIntent.putExtra(EXTRA_APP_LOGCAT, logKey);
|
||||
dialogIntent.putExtra(EXTRA_INITIAL_COMMENT, initialComment);
|
||||
dialogIntent.putExtra(EXTRA_MEMORY_STATS, memoryStats);
|
||||
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:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintBottom_toTopOf="@+id/checkButton"
|
||||
app:layout_constraintBottom_toTopOf="@+id/statusMessageView"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/imageView"
|
||||
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
|
||||
android:id="@+id/checkButton"
|
||||
android:layout_width="wrap_content"
|
||||
@@ -46,7 +63,7 @@
|
||||
app:layout_constraintBottom_toTopOf="@+id/statusInfoView"
|
||||
app:layout_constraintEnd_toEndOf="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" />
|
||||
|
||||
<TextView
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textMultiLine|textCapSentences"
|
||||
android:maxLines="5"
|
||||
tools:hint="@string/describe_crash" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
@@ -640,6 +640,10 @@
|
||||
<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_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>
|
||||
<!-- Example for string substitution: Last connection: 3min ago-->
|
||||
<string name="mailbox_status_connected_info">Last connection: %s</string>
|
||||
|
||||
@@ -6,6 +6,7 @@ import dagger.Provides
|
||||
import org.briarproject.bramble.account.AccountModule
|
||||
import org.briarproject.bramble.api.FeatureFlags
|
||||
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.TorConstants.DEFAULT_CONTROL_PORT
|
||||
import org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_SOCKS_PORT
|
||||
@@ -71,6 +72,12 @@ internal class HeadlessModule(private val appDir: File) {
|
||||
return HeadlessDatabaseConfig(dbDir, keyDir)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@MailboxDirectory
|
||||
internal fun provideMailboxDirectory(): File {
|
||||
return File(appDir, "mailbox")
|
||||
}
|
||||
|
||||
@Provides
|
||||
@TorDirectory
|
||||
internal fun provideTorDirectory(): File {
|
||||
|
||||
@@ -5,6 +5,7 @@ import dagger.Module
|
||||
import dagger.Provides
|
||||
import org.briarproject.bramble.account.AccountModule
|
||||
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.TorConstants.DEFAULT_CONTROL_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)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@MailboxDirectory
|
||||
internal fun provideMailboxDirectory(): File {
|
||||
return File(appDir, "mailbox")
|
||||
}
|
||||
|
||||
@Provides
|
||||
@TorSocksPort
|
||||
internal fun provideTorSocksPort(): Int = DEFAULT_SOCKS_PORT
|
||||
|
||||
Reference in New Issue
Block a user