diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java b/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java index a76138c1f..123ce4396 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java @@ -43,7 +43,7 @@ public interface DatabaseComponent { * @throws DataTooOldException if the data uses an older schema than the * current code and cannot be migrated */ - boolean open() throws DbException; + boolean open(@Nullable MigrationListener listener) throws DbException; /** * Waits for any open transactions to finish and closes the database. diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/db/MigrationListener.java b/bramble-api/src/main/java/org/briarproject/bramble/api/db/MigrationListener.java new file mode 100644 index 000000000..79e292a75 --- /dev/null +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/db/MigrationListener.java @@ -0,0 +1,11 @@ +package org.briarproject.bramble.api.db; + +public interface MigrationListener { + + /** + * This is called when a migration is started while opening the database. + * It will be called once for each migration being applied. + */ + void onMigrationRun(); + +} diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/lifecycle/LifecycleManager.java b/bramble-api/src/main/java/org/briarproject/bramble/api/lifecycle/LifecycleManager.java index b70c9dec9..5a036a3b6 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/lifecycle/LifecycleManager.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/lifecycle/LifecycleManager.java @@ -29,6 +29,19 @@ public interface LifecycleManager { SUCCESS } + /** + * The state the lifecycle can be in. + * Returned by {@link #getLifecycleState()} + */ + enum LifecycleState { + + STARTING, MIGRATING_DATABASE, STARTING_SERVICES, RUNNING, STOPPING; + + public boolean isAfter(LifecycleState state) { + return ordinal() > state.ordinal(); + } + } + /** * Registers a {@link Service} to be started and stopped. */ @@ -76,4 +89,10 @@ public interface LifecycleManager { * the {@link DatabaseComponent} to be closed before returning. */ void waitForShutdown() throws InterruptedException; + + /** + * Returns the current state of the lifecycle. + */ + LifecycleState getLifecycleState(); + } \ No newline at end of file diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/lifecycle/event/LifecycleEvent.java b/bramble-api/src/main/java/org/briarproject/bramble/api/lifecycle/event/LifecycleEvent.java new file mode 100644 index 000000000..4e6370bcf --- /dev/null +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/lifecycle/event/LifecycleEvent.java @@ -0,0 +1,20 @@ +package org.briarproject.bramble.api.lifecycle.event; + +import org.briarproject.bramble.api.event.Event; +import org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState; + +/** + * An event that is broadcast when the app enters a new lifecycle state. + */ +public class LifecycleEvent extends Event { + + private final LifecycleState state; + + public LifecycleEvent(LifecycleState state) { + this.state = state; + } + + public LifecycleState getLifecycleState() { + return state; + } +} diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/lifecycle/event/ShutdownEvent.java b/bramble-api/src/main/java/org/briarproject/bramble/api/lifecycle/event/ShutdownEvent.java deleted file mode 100644 index ba0c4d37f..000000000 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/lifecycle/event/ShutdownEvent.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.briarproject.bramble.api.lifecycle.event; - -import org.briarproject.bramble.api.event.Event; - -/** - * An event that is broadcast when the app is shutting down. - */ -public class ShutdownEvent extends Event { -} diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java b/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java index 9d1c76364..0a6e54fbd 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java @@ -6,6 +6,7 @@ import org.briarproject.bramble.api.db.DataTooNewException; import org.briarproject.bramble.api.db.DataTooOldException; import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.Metadata; +import org.briarproject.bramble.api.db.MigrationListener; import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.LocalAuthor; @@ -45,7 +46,7 @@ interface Database { * @throws DataTooOldException if the data uses an older schema than the * current code and cannot be migrated */ - boolean open() throws DbException; + boolean open(@Nullable MigrationListener listener) throws DbException; /** * Prevents new transactions from starting, waits for all current diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java index af80da92b..5809c09ee 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java @@ -10,6 +10,7 @@ import org.briarproject.bramble.api.db.ContactExistsException; import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.Metadata; +import org.briarproject.bramble.api.db.MigrationListener; import org.briarproject.bramble.api.db.NoSuchContactException; import org.briarproject.bramble.api.db.NoSuchGroupException; import org.briarproject.bramble.api.db.NoSuchLocalAuthorException; @@ -100,8 +101,9 @@ class DatabaseComponentImpl implements DatabaseComponent { } @Override - public boolean open() throws DbException { - boolean reopened = db.open(); + public boolean open(@Nullable MigrationListener listener) + throws DbException { + boolean reopened = db.open(listener); shutdown.addShutdownHook(() -> { try { close(); diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/H2Database.java b/bramble-core/src/main/java/org/briarproject/bramble/db/H2Database.java index 6a8196926..1c1983e67 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/db/H2Database.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/db/H2Database.java @@ -3,6 +3,7 @@ package org.briarproject.bramble.db; import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.db.DatabaseConfig; import org.briarproject.bramble.api.db.DbException; +import org.briarproject.bramble.api.db.MigrationListener; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.util.StringUtils; @@ -13,6 +14,7 @@ import java.sql.DriverManager; import java.sql.SQLException; import java.util.Properties; +import javax.annotation.Nullable; import javax.inject.Inject; /** @@ -42,10 +44,11 @@ class H2Database extends JdbcDatabase { } @Override - public boolean open() throws DbException { + public boolean open(@Nullable MigrationListener listener) + throws DbException { boolean reopen = config.databaseExists(); if (!reopen) config.getDatabaseDirectory().mkdirs(); - super.open("org.h2.Driver", reopen); + super.open("org.h2.Driver", reopen, listener); return reopen; } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/HyperSqlDatabase.java b/bramble-core/src/main/java/org/briarproject/bramble/db/HyperSqlDatabase.java index 1db9248cb..6a87ededa 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/db/HyperSqlDatabase.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/db/HyperSqlDatabase.java @@ -3,6 +3,7 @@ package org.briarproject.bramble.db; import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.db.DatabaseConfig; import org.briarproject.bramble.api.db.DbException; +import org.briarproject.bramble.api.db.MigrationListener; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.util.StringUtils; @@ -13,6 +14,7 @@ import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; +import javax.annotation.Nullable; import javax.inject.Inject; /** @@ -44,10 +46,10 @@ class HyperSqlDatabase extends JdbcDatabase { } @Override - public boolean open() throws DbException { + public boolean open(@Nullable MigrationListener listener) throws DbException { boolean reopen = config.databaseExists(); if (!reopen) config.getDatabaseDirectory().mkdirs(); - super.open("org.hsqldb.jdbc.JDBCDriver", reopen); + super.open("org.hsqldb.jdbc.JDBCDriver", reopen, listener); return reopen; } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java b/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java index f1f795a8c..e71f30c58 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java @@ -8,6 +8,7 @@ import org.briarproject.bramble.api.db.DataTooOldException; import org.briarproject.bramble.api.db.DbClosedException; import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.Metadata; +import org.briarproject.bramble.api.db.MigrationListener; import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.LocalAuthor; @@ -301,7 +302,8 @@ abstract class JdbcDatabase implements Database { this.clock = clock; } - protected void open(String driverClass, boolean reopen) throws DbException { + protected void open(String driverClass, boolean reopen, + @Nullable MigrationListener listener) throws DbException { // Load the JDBC driver try { Class.forName(driverClass); @@ -312,7 +314,7 @@ abstract class JdbcDatabase implements Database { Connection txn = startTransaction(); try { if (reopen) { - checkSchemaVersion(txn); + checkSchemaVersion(txn, listener); } else { createTables(txn); storeSchemaVersion(txn, CODE_SCHEMA_VERSION); @@ -335,7 +337,8 @@ abstract class JdbcDatabase implements Database { * @throws DataTooOldException if the data uses an older schema than the * current code and cannot be migrated */ - private void checkSchemaVersion(Connection txn) throws DbException { + private void checkSchemaVersion(Connection txn, + @Nullable MigrationListener listener) throws DbException { Settings s = getSettings(txn, DB_SETTINGS_NAMESPACE); int dataSchemaVersion = s.getInt(SCHEMA_VERSION_KEY, -1); if (dataSchemaVersion == -1) throw new DbException(); @@ -348,6 +351,7 @@ abstract class JdbcDatabase implements Database { if (start == dataSchemaVersion) { if (LOG.isLoggable(INFO)) LOG.info("Migrating from schema " + start + " to " + end); + if (listener != null) listener.onMigrationRun(); // Apply the migration m.migrate(txn); // Store the new schema version diff --git a/bramble-core/src/main/java/org/briarproject/bramble/lifecycle/LifecycleManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/lifecycle/LifecycleManagerImpl.java index 029170c91..0f41cbf21 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/lifecycle/LifecycleManagerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/lifecycle/LifecycleManagerImpl.java @@ -6,6 +6,7 @@ import org.briarproject.bramble.api.db.DataTooNewException; import org.briarproject.bramble.api.db.DataTooOldException; import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DbException; +import org.briarproject.bramble.api.db.MigrationListener; import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.identity.AuthorFactory; @@ -14,7 +15,7 @@ import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.Service; import org.briarproject.bramble.api.lifecycle.ServiceException; -import org.briarproject.bramble.api.lifecycle.event.ShutdownEvent; +import org.briarproject.bramble.api.lifecycle.event.LifecycleEvent; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.sync.Client; @@ -31,6 +32,11 @@ import javax.inject.Inject; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; +import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.MIGRATING_DATABASE; +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.STARTING_SERVICES; +import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STOPPING; import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.ALREADY_RUNNING; import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.DATA_TOO_NEW_ERROR; import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.DATA_TOO_OLD_ERROR; @@ -40,7 +46,7 @@ import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResul @ThreadSafe @NotNullByDefault -class LifecycleManagerImpl implements LifecycleManager { +class LifecycleManagerImpl implements LifecycleManager, MigrationListener { private static final Logger LOG = Logger.getLogger(LifecycleManagerImpl.class.getName()); @@ -58,6 +64,8 @@ class LifecycleManagerImpl implements LifecycleManager { private final CountDownLatch startupLatch = new CountDownLatch(1); private final CountDownLatch shutdownLatch = new CountDownLatch(1); + private volatile LifecycleState state = STARTING; + @Inject LifecycleManagerImpl(DatabaseComponent db, EventBus eventBus, CryptoComponent crypto, AuthorFactory authorFactory, @@ -123,7 +131,7 @@ class LifecycleManagerImpl implements LifecycleManager { LOG.info("Starting services"); long start = System.currentTimeMillis(); - boolean reopened = db.open(); + boolean reopened = db.open(this); long duration = System.currentTimeMillis() - start; if (LOG.isLoggable(INFO)) { if (reopened) @@ -135,7 +143,10 @@ class LifecycleManagerImpl implements LifecycleManager { registerLocalAuthor(createLocalAuthor(nickname)); } + state = STARTING_SERVICES; dbLatch.countDown(); + eventBus.broadcast(new LifecycleEvent(STARTING_SERVICES)); + Transaction txn = db.startTransaction(false); try { for (Client c : clients) { @@ -161,7 +172,10 @@ class LifecycleManagerImpl implements LifecycleManager { + " took " + duration + " ms"); } } + + state = RUNNING; startupLatch.countDown(); + eventBus.broadcast(new LifecycleEvent(RUNNING)); return SUCCESS; } catch (DataTooOldException e) { if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); @@ -180,6 +194,12 @@ class LifecycleManagerImpl implements LifecycleManager { } } + @Override + public void onMigrationRun() { + state = MIGRATING_DATABASE; + eventBus.broadcast(new LifecycleEvent(MIGRATING_DATABASE)); + } + @Override public void stopServices() { try { @@ -190,7 +210,8 @@ class LifecycleManagerImpl implements LifecycleManager { } try { LOG.info("Stopping services"); - eventBus.broadcast(new ShutdownEvent()); + state = STOPPING; + eventBus.broadcast(new LifecycleEvent(STOPPING)); for (Service s : services) { long start = System.currentTimeMillis(); s.stopService(); @@ -235,4 +256,8 @@ class LifecycleManagerImpl implements LifecycleManager { shutdownLatch.await(); } + @Override + public LifecycleState getLifecycleState() { + return state; + } } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/sync/DuplexOutgoingSession.java b/bramble-core/src/main/java/org/briarproject/bramble/sync/DuplexOutgoingSession.java index 3fdabc01d..80f699a1f 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/sync/DuplexOutgoingSession.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/sync/DuplexOutgoingSession.java @@ -10,7 +10,7 @@ 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.event.ShutdownEvent; +import org.briarproject.bramble.api.lifecycle.event.LifecycleEvent; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.sync.Ack; import org.briarproject.bramble.api.sync.Offer; @@ -38,6 +38,7 @@ import javax.annotation.concurrent.ThreadSafe; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; +import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STOPPING; import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_IDS; import static org.briarproject.bramble.api.sync.SyncConstants.MAX_RECORD_PAYLOAD_LENGTH; @@ -209,8 +210,9 @@ class DuplexOutgoingSession implements SyncSession, EventListener { } else if (e instanceof MessageToRequestEvent) { if (((MessageToRequestEvent) e).getContactId().equals(contactId)) generateRequest(); - } else if (e instanceof ShutdownEvent) { - interrupt(); + } else if (e instanceof LifecycleEvent) { + LifecycleEvent l = (LifecycleEvent) e; + if (l.getLifecycleState() == STOPPING) interrupt(); } } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/sync/IncomingSession.java b/bramble-core/src/main/java/org/briarproject/bramble/sync/IncomingSession.java index 249d1f2c6..32eb58df4 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/sync/IncomingSession.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/sync/IncomingSession.java @@ -11,7 +11,7 @@ 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.event.ShutdownEvent; +import org.briarproject.bramble.api.lifecycle.event.LifecycleEvent; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.sync.Ack; import org.briarproject.bramble.api.sync.Message; @@ -27,6 +27,7 @@ import java.util.logging.Logger; import javax.annotation.concurrent.ThreadSafe; import static java.util.logging.Level.WARNING; +import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STOPPING; /** * An incoming {@link SyncSession}. @@ -96,8 +97,9 @@ class IncomingSession implements SyncSession, EventListener { if (e instanceof ContactRemovedEvent) { ContactRemovedEvent c = (ContactRemovedEvent) e; if (c.getContactId().equals(contactId)) interrupt(); - } else if (e instanceof ShutdownEvent) { - interrupt(); + } else if (e instanceof LifecycleEvent) { + LifecycleEvent l = (LifecycleEvent) e; + if (l.getLifecycleState() == STOPPING) interrupt(); } } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/sync/SimplexOutgoingSession.java b/bramble-core/src/main/java/org/briarproject/bramble/sync/SimplexOutgoingSession.java index 3d0ecae22..b1f8198f8 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/sync/SimplexOutgoingSession.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/sync/SimplexOutgoingSession.java @@ -10,7 +10,7 @@ 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.event.ShutdownEvent; +import org.briarproject.bramble.api.lifecycle.event.LifecycleEvent; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.sync.Ack; import org.briarproject.bramble.api.sync.RecordWriter; @@ -28,6 +28,7 @@ import javax.annotation.concurrent.ThreadSafe; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; +import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STOPPING; import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_IDS; import static org.briarproject.bramble.api.sync.SyncConstants.MAX_RECORD_PAYLOAD_LENGTH; @@ -109,8 +110,9 @@ class SimplexOutgoingSession implements SyncSession, EventListener { if (e instanceof ContactRemovedEvent) { ContactRemovedEvent c = (ContactRemovedEvent) e; if (c.getContactId().equals(contactId)) interrupt(); - } else if (e instanceof ShutdownEvent) { - interrupt(); + } else if (e instanceof LifecycleEvent) { + LifecycleEvent l = (LifecycleEvent) e; + if (l.getLifecycleState() == STOPPING) interrupt(); } } diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseComponentImplTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseComponentImplTest.java index b2e4842db..18de0d7e8 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseComponentImplTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseComponentImplTest.java @@ -134,7 +134,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase { int shutdownHandle = 12345; context.checking(new Expectations() {{ // open() - oneOf(database).open(); + oneOf(database).open(null); will(returnValue(false)); oneOf(shutdown).addShutdownHook(with(any(Runnable.class))); will(returnValue(shutdownHandle)); @@ -201,7 +201,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase { DatabaseComponent db = createDatabaseComponent(database, eventBus, shutdown); - assertFalse(db.open()); + assertFalse(db.open(null)); Transaction transaction = db.startTransaction(false); try { db.addLocalAuthor(transaction, localAuthor); @@ -1501,7 +1501,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase { MessageId messageId2 = new MessageId(TestUtils.getRandomId()); context.checking(new Expectations() {{ // open() - oneOf(database).open(); + oneOf(database).open(null); will(returnValue(false)); oneOf(shutdown).addShutdownHook(with(any(Runnable.class))); will(returnValue(shutdownHandle)); @@ -1543,7 +1543,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase { DatabaseComponent db = createDatabaseComponent(database, eventBus, shutdown); - assertFalse(db.open()); + assertFalse(db.open(null)); Transaction transaction = db.startTransaction(false); try { db.addLocalMessage(transaction, message, metadata, true); diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseMigrationTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseMigrationTest.java index 203f4dfd2..89bdf8a4c 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseMigrationTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseMigrationTest.java @@ -62,7 +62,7 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase { public void testDoesNotRunMigrationsWhenCreatingDatabase() throws Exception { Database db = createDatabase(singletonList(migration)); - assertFalse(db.open()); + assertFalse(db.open(null)); assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db)); db.close(); } @@ -72,14 +72,14 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase { throws Exception { // Open the DB for the first time Database db = createDatabase(asList(migration, migration1)); - assertFalse(db.open()); + assertFalse(db.open(null)); assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db)); // Override the data schema version setDataSchemaVersion(db, -1); db.close(); // Reopen the DB - an exception should be thrown db = createDatabase(asList(migration, migration1)); - db.open(); + db.open(null); } @Test @@ -87,12 +87,12 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase { throws Exception { // Open the DB for the first time Database db = createDatabase(asList(migration, migration1)); - assertFalse(db.open()); + assertFalse(db.open(null)); assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db)); db.close(); // Reopen the DB - migrations should not be run db = createDatabase(asList(migration, migration1)); - assertTrue(db.open()); + assertTrue(db.open(null)); assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db)); db.close(); } @@ -101,14 +101,14 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase { public void testThrowsExceptionIfDataIsNewerThanCode() throws Exception { // Open the DB for the first time Database db = createDatabase(asList(migration, migration1)); - assertFalse(db.open()); + assertFalse(db.open(null)); assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db)); // Override the data schema version setDataSchemaVersion(db, CODE_SCHEMA_VERSION + 1); db.close(); // Reopen the DB - an exception should be thrown db = createDatabase(asList(migration, migration1)); - db.open(); + db.open(null); } @Test(expected = DataTooOldException.class) @@ -116,13 +116,13 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase { throws Exception { // Open the DB for the first time Database db = createDatabase(emptyList()); - assertFalse(db.open()); + assertFalse(db.open(null)); assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db)); setDataSchemaVersion(db, CODE_SCHEMA_VERSION - 1); db.close(); // Reopen the DB - an exception should be thrown db = createDatabase(emptyList()); - db.open(); + db.open(null); } @Test(expected = DataTooOldException.class) @@ -141,14 +141,14 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase { // Open the DB for the first time Database db = createDatabase(asList(migration, migration1)); - assertFalse(db.open()); + assertFalse(db.open(null)); assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db)); // Override the data schema version setDataSchemaVersion(db, CODE_SCHEMA_VERSION - 3); db.close(); // Reopen the DB - an exception should be thrown db = createDatabase(asList(migration, migration1)); - db.open(); + db.open(null); } @Test @@ -170,14 +170,14 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase { // Open the DB for the first time Database db = createDatabase(asList(migration, migration1)); - assertFalse(db.open()); + assertFalse(db.open(null)); assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db)); // Override the data schema version setDataSchemaVersion(db, CODE_SCHEMA_VERSION - 2); db.close(); // Reopen the DB - the first migration should be run db = createDatabase(asList(migration, migration1)); - assertTrue(db.open()); + assertTrue(db.open(null)); assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db)); db.close(); } @@ -202,14 +202,14 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase { // Open the DB for the first time Database db = createDatabase(asList(migration, migration1)); - assertFalse(db.open()); + assertFalse(db.open(null)); assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db)); // Override the data schema version setDataSchemaVersion(db, CODE_SCHEMA_VERSION - 2); db.close(); // Reopen the DB - both migrations should be run db = createDatabase(asList(migration, migration1)); - assertTrue(db.open()); + assertTrue(db.open(null)); assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db)); db.close(); } diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/DatabasePerformanceComparisonTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/DatabasePerformanceComparisonTest.java index c2a124bb2..1807d30dc 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/db/DatabasePerformanceComparisonTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/db/DatabasePerformanceComparisonTest.java @@ -71,7 +71,7 @@ public abstract class DatabasePerformanceComparisonTest throws DbException { Database db = createDatabase(conditionA, new TestDatabaseConfig(testDir, MAX_SIZE), new SystemClock()); - db.open(); + db.open(null); return db; } diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseTraceTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseTraceTest.java index a78fb5f55..f4e0daa19 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseTraceTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseTraceTest.java @@ -43,7 +43,7 @@ public abstract class DatabaseTraceTest extends DatabasePerformanceTest { private Database openDatabase() throws DbException { Database db = createDatabase( new TestDatabaseConfig(testDir, MAX_SIZE), new SystemClock()); - db.open(); + db.open(null); return db; } diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java index 32d4e5faa..81d1c9025 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java @@ -1700,7 +1700,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase { Database db = createDatabase( new TestDatabaseConfig(testDir, MAX_SIZE), clock); if (!resume) TestUtils.deleteTestDirectory(testDir); - db.open(); + db.open(null); return db; } diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/SingleDatabasePerformanceTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/SingleDatabasePerformanceTest.java index 5a8364767..670c7cddb 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/db/SingleDatabasePerformanceTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/db/SingleDatabasePerformanceTest.java @@ -40,7 +40,7 @@ public abstract class SingleDatabasePerformanceTest private Database openDatabase() throws DbException { Database db = createDatabase( new TestDatabaseConfig(testDir, MAX_SIZE), new SystemClock()); - db.open(); + db.open(null); return db; } diff --git a/bramble-core/src/test/java/org/briarproject/bramble/test/TestLifecycleModule.java b/bramble-core/src/test/java/org/briarproject/bramble/test/TestLifecycleModule.java index ee8d5115e..15943adc2 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/test/TestLifecycleModule.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/test/TestLifecycleModule.java @@ -17,6 +17,8 @@ import javax.inject.Singleton; import dagger.Module; import dagger.Provides; +import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.RUNNING; + @Module public class TestLifecycleModule { @@ -57,6 +59,11 @@ public class TestLifecycleModule { @Override public void waitForShutdown() throws InterruptedException { } + + @Override + public LifecycleState getLifecycleState() { + return RUNNING; + } }; return lifecycleManager; } diff --git a/briar-android/src/main/AndroidManifest.xml b/briar-android/src/main/AndroidManifest.xml index a2fed381f..102de1290 100644 --- a/briar-android/src/main/AndroidManifest.xml +++ b/briar-android/src/main/AndroidManifest.xml @@ -77,6 +77,11 @@ + + 0; diff --git a/briar-android/src/main/java/org/briarproject/briar/android/BriarService.java b/briar-android/src/main/java/org/briarproject/briar/android/BriarService.java index abd9a20a7..8e1abd66a 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/BriarService.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/BriarService.java @@ -88,6 +88,7 @@ public class BriarService extends Service { stopSelf(); return; } + // Create notification channels if (SDK_INT >= 26) { NotificationManager nm = (NotificationManager) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java index 7403d01e0..2f8ca83fa 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java @@ -32,6 +32,7 @@ import org.briarproject.briar.android.keyagreement.ShowQrCodeFragment; import org.briarproject.briar.android.login.AuthorNameFragment; import org.briarproject.briar.android.login.ChangePasswordActivity; import org.briarproject.briar.android.login.DozeFragment; +import org.briarproject.briar.android.login.OpenDatabaseActivity; import org.briarproject.briar.android.login.PasswordActivity; import org.briarproject.briar.android.login.PasswordFragment; import org.briarproject.briar.android.login.SetupActivity; @@ -88,6 +89,8 @@ public interface ActivityComponent { void inject(SetupActivity activity); + void inject(OpenDatabaseActivity activity); + void inject(NavDrawerActivity activity); void inject(PasswordActivity activity); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/controller/ConfigController.java b/briar-android/src/main/java/org/briarproject/briar/android/controller/ConfigController.java index d6bc554f6..07b32b0e2 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/controller/ConfigController.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/controller/ConfigController.java @@ -17,4 +17,7 @@ public interface ConfigController { void deleteAccount(Context ctx); boolean accountExists(); + + boolean accountSignedIn(); + } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/controller/ConfigControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/controller/ConfigControllerImpl.java index 0af60bb98..3c1979444 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/controller/ConfigControllerImpl.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/controller/ConfigControllerImpl.java @@ -52,4 +52,10 @@ public class ConfigControllerImpl implements ConfigController { String hex = getEncryptedDatabaseKey(); return hex != null && databaseConfig.databaseExists(); } + + @Override + public boolean accountSignedIn() { + return databaseConfig.getEncryptionKey() != null; + } + } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/OpenDatabaseActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/login/OpenDatabaseActivity.java new file mode 100644 index 000000000..d24196d32 --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/login/OpenDatabaseActivity.java @@ -0,0 +1,93 @@ +package org.briarproject.briar.android.login; + +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.widget.ImageView; +import android.widget.TextView; + +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.LifecycleManager; +import org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState; +import org.briarproject.bramble.api.lifecycle.event.LifecycleEvent; +import org.briarproject.briar.R; +import org.briarproject.briar.android.activity.ActivityComponent; +import org.briarproject.briar.android.activity.BriarActivity; +import org.briarproject.briar.android.navdrawer.NavDrawerActivity; + +import javax.annotation.ParametersAreNonnullByDefault; +import javax.inject.Inject; + +import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.MIGRATING_DATABASE; +import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STARTING_SERVICES; + +@ParametersAreNonnullByDefault +public class OpenDatabaseActivity extends BriarActivity + implements EventListener { + + @Inject + LifecycleManager lifecycleManager; + @Inject + EventBus eventBus; + + private TextView textView; + private ImageView imageView; + private boolean showingMigration = false; + + @Override + public void onCreate(@Nullable Bundle state) { + super.onCreate(state); + setContentView(R.layout.activity_open_database); + textView = findViewById(R.id.textView); + imageView = findViewById(R.id.imageView); + } + + @Override + public void injectActivity(ActivityComponent component) { + component.inject(this); + } + + @Override + public void onStart() { + super.onStart(); + LifecycleState state = lifecycleManager.getLifecycleState(); + if (state.isAfter(STARTING_SERVICES)) { + finishAndStartApp(); + } else { + if (state == MIGRATING_DATABASE) showMigration(); + eventBus.addListener(this); + } + } + + @Override + protected void onStop() { + super.onStop(); + eventBus.removeListener(this); + } + + @Override + public void eventOccurred(Event e) { + if (e instanceof LifecycleEvent) { + LifecycleState state = ((LifecycleEvent) e).getLifecycleState(); + if (state.isAfter(STARTING_SERVICES)) + runOnUiThreadUnlessDestroyed(this::finishAndStartApp); + else if (state == MIGRATING_DATABASE) + runOnUiThreadUnlessDestroyed(this::showMigration); + } + } + + private void showMigration() { + if (showingMigration) return; + textView.setText(R.string.startup_migrate_database); + imageView.setImageResource(R.drawable.startup_migration); + showingMigration = true; + } + + private void finishAndStartApp() { + startActivity(new Intent(this, NavDrawerActivity.class)); + supportFinishAfterTransition(); + } + +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/SetupActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/login/SetupActivity.java index 17c6f522f..2b17df6ea 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/login/SetupActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/login/SetupActivity.java @@ -8,7 +8,6 @@ import org.briarproject.briar.R; import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.BaseActivity; import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener; -import org.briarproject.briar.android.navdrawer.NavDrawerActivity; import javax.inject.Inject; @@ -48,7 +47,7 @@ public class SetupActivity extends BaseActivity } public void showApp() { - Intent i = new Intent(this, NavDrawerActivity.class); + Intent i = new Intent(this, OpenDatabaseActivity.class); i.setFlags(FLAG_ACTIVITY_NEW_TASK); startActivity(i); supportFinishAfterTransition(); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java index d083343e9..dcc42f6c3 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java @@ -46,8 +46,6 @@ import java.util.logging.Logger; import javax.inject.Inject; -import static android.os.Build.MANUFACTURER; -import static android.os.Build.VERSION.SDK_INT; import static android.support.v4.app.FragmentManager.POP_BACK_STACK_INCLUSIVE; import static android.support.v4.view.GravityCompat.START; import static android.support.v4.widget.DrawerLayout.LOCK_MODE_LOCKED_CLOSED; @@ -214,18 +212,6 @@ public class NavDrawerActivity extends BriarActivity implements public void onBackPressed() { if (drawerLayout.isDrawerOpen(START)) { drawerLayout.closeDrawer(START); - } else if (getSupportFragmentManager().getBackStackEntryCount() == 0 && - getSupportFragmentManager() - .findFragmentByTag(ContactListFragment.TAG) != null) { - if (SDK_INT == 19 && MANUFACTURER.equalsIgnoreCase("Samsung")) { - // workaround for #1116 causes splash screen to show again - super.onBackPressed(); - } else { - Intent i = new Intent(Intent.ACTION_MAIN); - i.addCategory(Intent.CATEGORY_HOME); - i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(i); - } } else if (getSupportFragmentManager().getBackStackEntryCount() == 0 && getSupportFragmentManager() .findFragmentByTag(ContactListFragment.TAG) == null) { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/splash/SplashScreenActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/splash/SplashScreenActivity.java index 7c19f3c78..f21f460aa 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/splash/SplashScreenActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/splash/SplashScreenActivity.java @@ -12,8 +12,8 @@ import org.briarproject.briar.R; import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.BaseActivity; import org.briarproject.briar.android.controller.ConfigController; +import org.briarproject.briar.android.login.OpenDatabaseActivity; import org.briarproject.briar.android.login.SetupActivity; -import org.briarproject.briar.android.navdrawer.NavDrawerActivity; import java.util.logging.Logger; @@ -43,10 +43,15 @@ public class SplashScreenActivity extends BaseActivity { setContentView(R.layout.splash); - new Handler().postDelayed(() -> { - startNextActivity(); - supportFinishAfterTransition(); - }, 500); + if (configController.accountSignedIn()) { + startActivity(new Intent(this, OpenDatabaseActivity.class)); + finish(); + } else { + new Handler().postDelayed(() -> { + startNextActivity(); + supportFinishAfterTransition(); + }, 500); + } } @Override @@ -60,7 +65,7 @@ public class SplashScreenActivity extends BaseActivity { startActivity(new Intent(this, ExpiredActivity.class)); } else { if (configController.accountExists()) { - startActivity(new Intent(this, NavDrawerActivity.class)); + startActivity(new Intent(this, OpenDatabaseActivity.class)); } else { configController.deleteAccount(this); startActivity(new Intent(this, SetupActivity.class)); diff --git a/briar-android/src/main/res/drawable/startup_lock.xml b/briar-android/src/main/res/drawable/startup_lock.xml new file mode 100644 index 000000000..181a64cc9 --- /dev/null +++ b/briar-android/src/main/res/drawable/startup_lock.xml @@ -0,0 +1,9 @@ + + + diff --git a/briar-android/src/main/res/drawable/startup_migration.xml b/briar-android/src/main/res/drawable/startup_migration.xml new file mode 100644 index 000000000..a5021ef0d --- /dev/null +++ b/briar-android/src/main/res/drawable/startup_migration.xml @@ -0,0 +1,9 @@ + + + diff --git a/briar-android/src/main/res/layout/activity_open_database.xml b/briar-android/src/main/res/layout/activity_open_database.xml new file mode 100644 index 000000000..ab20f17cb --- /dev/null +++ b/briar-android/src/main/res/layout/activity_open_database.xml @@ -0,0 +1,46 @@ + + + + + + + + + + \ No newline at end of file diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml index c682069f7..14de9071b 100644 --- a/briar-android/src/main/res/values/strings.xml +++ b/briar-android/src/main/res/values/strings.xml @@ -46,6 +46,8 @@ The testing expiry date has been extended. Your account will now expire in %d days. This software has expired.\nThank you for testing! + Decrypting Database… + Upgrading Database… Open the navigation drawer