Merge branch '427-local-author-caching' into 'master'

Cache the local author and load before the db latch is released

Closes #427, #588 

See merge request !354
This commit is contained in:
akwizgran
2016-11-01 17:21:14 +00:00
45 changed files with 294 additions and 665 deletions

View File

@@ -77,6 +77,7 @@ public class AppModule {
return new DatabaseConfig() {
private volatile SecretKey key = null;
private volatile String nickname;
@Override
public boolean databaseExists() {
@@ -95,6 +96,16 @@ public class AppModule {
this.key = key;
}
@Override
public void setLocalAuthorName(String nickname) {
this.nickname = nickname;
}
@Override
public String getLocalAuthorName() {
return nickname;
}
@Override
public SecretKey getEncryptionKey() {
return key;

View File

@@ -22,8 +22,6 @@ import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
@SuppressLint("Registered")
public abstract class BriarActivity extends BaseActivity {
public static final String KEY_LOCAL_AUTHOR_HANDLE =
"briar.LOCAL_AUTHOR_HANDLE";
public static final String KEY_STARTUP_FAILED = "briar.STARTUP_FAILED";
public static final String GROUP_ID = "briar.GROUP_ID";
public static final String GROUP_NAME = "briar.GROUP_NAME";

View File

@@ -45,11 +45,13 @@ public class BriarService extends Service {
private final AtomicBoolean created = new AtomicBoolean(false);
private final Binder binder = new BriarBinder();
@Inject protected DatabaseConfig databaseConfig;
@Inject
protected DatabaseConfig databaseConfig;
// Fields that are accessed from background threads must be volatile
@Inject protected volatile LifecycleManager lifecycleManager;
@Inject protected volatile AndroidExecutor androidExecutor;
@Inject
protected volatile LifecycleManager lifecycleManager;
@Inject
protected volatile AndroidExecutor androidExecutor;
private volatile boolean started = false;
@Override
@@ -91,7 +93,8 @@ public class BriarService extends Service {
new Thread() {
@Override
public void run() {
StartResult result = lifecycleManager.startServices();
String nickname = databaseConfig.getLocalAuthorName();
StartResult result = lifecycleManager.startServices(nickname);
if (result == SUCCESS) {
started = true;
} else if (result == ALREADY_RUNNING) {
@@ -169,24 +172,32 @@ public class BriarService extends Service {
// FIXME: Work out what to do about it
}
/** Waits for all services to start before returning. */
/**
* Waits for all services to start before returning.
*/
public void waitForStartup() throws InterruptedException {
lifecycleManager.waitForStartup();
}
/** Waits for all services to stop before returning. */
/**
* Waits for all services to stop before returning.
*/
public void waitForShutdown() throws InterruptedException {
lifecycleManager.waitForShutdown();
}
/** Starts the shutdown process. */
/**
* Starts the shutdown process.
*/
public void shutdown() {
stopSelf(); // This will call onDestroy()
}
public class BriarBinder extends Binder {
/** Returns the bound service. */
/**
* Returns the bound service.
*/
public BriarService getService() {
return BriarService.this;
}
@@ -205,9 +216,12 @@ public class BriarService extends Service {
}
@Override
public void onServiceDisconnected(ComponentName name) {}
public void onServiceDisconnected(ComponentName name) {
}
/** Waits for the service to connect and returns its binder. */
/**
* Waits for the service to connect and returns its binder.
*/
public IBinder waitForBinder() throws InterruptedException {
binderLatch.await();
return binder;

View File

@@ -25,12 +25,10 @@ import org.briarproject.android.blogs.FeedFragment;
import org.briarproject.android.contact.ContactListFragment;
import org.briarproject.android.controller.NavDrawerController;
import org.briarproject.android.controller.TransportStateListener;
import org.briarproject.android.controller.handler.UiResultHandler;
import org.briarproject.android.forum.ForumListFragment;
import org.briarproject.android.fragment.BaseFragment.BaseFragmentListener;
import org.briarproject.android.privategroup.list.GroupListFragment;
import org.briarproject.api.TransportId;
import org.briarproject.api.identity.LocalAuthor;
import java.util.ArrayList;
import java.util.List;
@@ -72,7 +70,6 @@ public class NavDrawerActivity extends BriarFragmentActivity implements
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
exitIfStartupFailed(intent);
checkAuthorHandle(intent);
// FIXME why was the stack cleared here?
// This prevents state from being restored properly
// clearBackStack();
@@ -115,7 +112,6 @@ public class NavDrawerActivity extends BriarFragmentActivity implements
R.string.nav_drawer_close_description);
drawerLayout.addDrawerListener(drawerToggle);
navigation.setNavigationItemSelectedListener(this);
checkAuthorHandle(getIntent());
initializeTransports(getLayoutInflater());
transportsView.setAdapter(transportsAdapter);
@@ -146,18 +142,6 @@ public class NavDrawerActivity extends BriarFragmentActivity implements
updateTransports();
}
private void checkAuthorHandle(Intent intent) {
long handle = intent.getLongExtra(KEY_LOCAL_AUTHOR_HANDLE, -1);
if (handle != -1) {
LocalAuthor a = controller.removeAuthorHandle(handle);
// The activity was launched from the setup wizard
if (a != null) {
showLoadingScreen(true, R.string.progress_title_please_wait);
storeLocalAuthor(a);
}
}
}
private void exitIfStartupFailed(Intent intent) {
if (intent.getBooleanExtra(KEY_STARTUP_FAILED, false)) {
finish();
@@ -166,15 +150,6 @@ public class NavDrawerActivity extends BriarFragmentActivity implements
}
}
private void storeLocalAuthor(LocalAuthor a) {
controller.storeLocalAuthor(a, new UiResultHandler<Void>(this) {
@Override
public void onResultUi(Void result) {
hideLoadingScreen();
}
});
}
private void loadFragment(int fragmentId) {
// TODO re-use fragments from the manager when possible
switch (fragmentId) {

View File

@@ -2,7 +2,6 @@ package org.briarproject.android;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.TextInputLayout;
import android.text.Editable;
import android.text.TextWatcher;
@@ -135,18 +134,18 @@ public class SetupActivity extends BaseActivity implements OnClickListener,
progress.setVisibility(VISIBLE);
String nickname = nicknameEntry.getText().toString();
String password = passwordEntry.getText().toString();
setupController.createIdentity(nickname, password,
new UiResultHandler<Long>(this) {
setupController.storeAuthorInfo(nickname, password,
new UiResultHandler<Void>(this) {
@Override
public void onResultUi(@NonNull Long result) {
showMain(result);
public void onResultUi(Void result) {
showMain();
}
});
}
private void showMain(final long handle) {
private void showMain() {
Intent i = new Intent(this, NavDrawerActivity.class);
i.putExtra(BriarActivity.KEY_LOCAL_AUTHOR_HANDLE, handle);
i.setFlags(FLAG_ACTIVITY_NEW_TASK);
startActivity(i);
finish();

View File

@@ -27,7 +27,6 @@ import org.briarproject.api.sync.GroupId;
import org.briarproject.util.StringUtils;
import java.security.GeneralSecurityException;
import java.util.Collection;
import java.util.logging.Logger;
import javax.inject.Inject;
@@ -147,9 +146,7 @@ public class WriteBlogPostActivity extends BriarActivity
public void run() {
long now = System.currentTimeMillis();
try {
Collection<LocalAuthor> authors =
identityManager.getLocalAuthors();
LocalAuthor author = authors.iterator().next();
LocalAuthor author = identityManager.getLocalAuthor();
BlogPost p = blogPostFactory
.createBlogPost(groupId, now, null, author, body);
blogManager.addLocalPost(p);

View File

@@ -6,7 +6,7 @@ public interface ConfigController {
String getEncryptedDatabaseKey();
void setEncryptedDatabaseKey(String hex);
void storeEncryptedDatabaseKey(String hex);
void deleteAccount(Context ctx);

View File

@@ -29,7 +29,7 @@ public class ConfigControllerImpl implements ConfigController {
}
@Override
public void setEncryptedDatabaseKey(String hex) {
public void storeEncryptedDatabaseKey(String hex) {
SharedPreferences.Editor editor = briarPrefs.edit();
editor.putString(PREF_DB_KEY, hex);
editor.apply();

View File

@@ -1,15 +1,9 @@
package org.briarproject.android.controller;
import org.briarproject.android.controller.handler.ResultHandler;
import org.briarproject.api.TransportId;
import org.briarproject.api.identity.LocalAuthor;
public interface NavDrawerController extends ActivityLifecycleController {
boolean isTransportRunning(TransportId transportId);
void storeLocalAuthor(LocalAuthor author,
ResultHandler<Void> resultHandler);
LocalAuthor removeAuthorHandle(long handle);
}

View File

@@ -2,18 +2,13 @@ package org.briarproject.android.controller;
import android.app.Activity;
import org.briarproject.android.api.ReferenceManager;
import org.briarproject.android.controller.handler.ResultHandler;
import org.briarproject.api.TransportId;
import org.briarproject.api.db.DatabaseExecutor;
import org.briarproject.api.db.DbException;
import org.briarproject.api.event.Event;
import org.briarproject.api.event.EventBus;
import org.briarproject.api.event.EventListener;
import org.briarproject.api.event.TransportDisabledEvent;
import org.briarproject.api.event.TransportEnabledEvent;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.plugins.Plugin;
import org.briarproject.api.plugins.PluginManager;
@@ -24,7 +19,6 @@ import java.util.logging.Logger;
import javax.inject.Inject;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
public class NavDrawerControllerImpl extends DbControllerImpl
implements NavDrawerController, EventListener {
@@ -32,23 +26,18 @@ public class NavDrawerControllerImpl extends DbControllerImpl
private static final Logger LOG =
Logger.getLogger(NavDrawerControllerImpl.class.getName());
private final ReferenceManager referenceManager;
private final PluginManager pluginManager;
private final EventBus eventBus;
private final IdentityManager identityManager;
private volatile TransportStateListener listener;
@Inject
NavDrawerControllerImpl(@DatabaseExecutor Executor dbExecutor,
LifecycleManager lifecycleManager,
ReferenceManager referenceManager, PluginManager pluginManager,
EventBus eventBus, IdentityManager identityManager) {
PluginManager pluginManager, EventBus eventBus) {
super(dbExecutor, lifecycleManager);
this.referenceManager = referenceManager;
this.pluginManager = pluginManager;
this.eventBus = eventBus;
this.identityManager = identityManager;
}
@Override
@@ -101,33 +90,8 @@ public class NavDrawerControllerImpl extends DbControllerImpl
@Override
public boolean isTransportRunning(TransportId transportId) {
Plugin plugin = pluginManager.getPlugin(transportId);
return plugin != null && plugin.isRunning();
}
@Override
public void storeLocalAuthor(final LocalAuthor author,
final ResultHandler<Void> resultHandler) {
runOnDbThread(new Runnable() {
@Override
public void run() {
try {
long now = System.currentTimeMillis();
identityManager.addLocalAuthor(author);
long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO))
LOG.info("Storing author took " + duration + " ms");
resultHandler.onResult(null);
} catch (final DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
@Override
public LocalAuthor removeAuthorHandle(long handle) {
return referenceManager.removeReference(handle, LocalAuthor.class);
}
}

View File

@@ -65,7 +65,7 @@ public class PasswordControllerImpl extends ConfigControllerImpl
} else {
String hex =
encryptDatabaseKey(new SecretKey(key), newPassword);
setEncryptedDatabaseKey(hex);
storeEncryptedDatabaseKey(hex);
resultHandler.onResult(true);
}
}

View File

@@ -6,6 +6,7 @@ public interface SetupController {
float estimatePasswordStrength(String password);
void createIdentity(String nickname, String password,
ResultHandler<Long> resultHandler);
void storeAuthorInfo(String nickname, String password,
ResultHandler<Void> resultHandler);
}

View File

@@ -2,58 +2,29 @@ package org.briarproject.android.controller;
import android.content.SharedPreferences;
import org.briarproject.android.api.ReferenceManager;
import org.briarproject.android.controller.handler.ResultHandler;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.crypto.CryptoExecutor;
import org.briarproject.api.crypto.KeyPair;
import org.briarproject.api.crypto.PasswordStrengthEstimator;
import org.briarproject.api.crypto.SecretKey;
import org.briarproject.api.db.DatabaseConfig;
import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.identity.LocalAuthor;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.inject.Inject;
import static java.util.logging.Level.INFO;
public class SetupControllerImpl extends PasswordControllerImpl
implements SetupController {
private static final Logger LOG =
Logger.getLogger(SetupControllerImpl.class.getName());
private final PasswordStrengthEstimator strengthEstimator;
private final AuthorFactory authorFactory;
private final ReferenceManager referenceManager;
@Inject
SetupControllerImpl(SharedPreferences briarPrefs,
DatabaseConfig databaseConfig,
@CryptoExecutor Executor cryptoExecutor, CryptoComponent crypto,
PasswordStrengthEstimator strengthEstimator,
AuthorFactory authorFactory, ReferenceManager referenceManager) {
PasswordStrengthEstimator strengthEstimator) {
super(briarPrefs, databaseConfig, cryptoExecutor, crypto);
this.strengthEstimator = strengthEstimator;
this.authorFactory = authorFactory;
this.referenceManager = referenceManager;
}
private LocalAuthor createLocalAuthor(String nickname) {
long now = System.currentTimeMillis();
KeyPair keyPair = crypto.generateSignatureKeyPair();
byte[] publicKey = keyPair.getPublic().getEncoded();
byte[] privateKey = keyPair.getPrivate().getEncoded();
LocalAuthor localAuthor = authorFactory.createLocalAuthor(nickname,
publicKey, privateKey);
long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO))
LOG.info("Identity creation took " + duration + " ms");
return localAuthor;
}
@Override
@@ -62,20 +33,19 @@ public class SetupControllerImpl extends PasswordControllerImpl
}
@Override
public void createIdentity(final String nickname, final String password,
final ResultHandler<Long> resultHandler) {
public void storeAuthorInfo(final String nickname, final String password,
final ResultHandler<Void> resultHandler) {
cryptoExecutor.execute(new Runnable() {
@Override
public void run() {
databaseConfig.setLocalAuthorName(nickname);
SecretKey key = crypto.generateSecretKey();
databaseConfig.setEncryptionKey(key);
String hex = encryptDatabaseKey(key, password);
setEncryptedDatabaseKey(hex);
LocalAuthor localAuthor = createLocalAuthor(nickname);
long handle = referenceManager.putReference(localAuthor,
LocalAuthor.class);
resultHandler.onResult(handle);
storeEncryptedDatabaseKey(hex);
resultHandler.onResult(null);
}
});
}
}

View File

@@ -254,8 +254,7 @@ public class AddContactActivity extends BriarActivity
// change UI to show a progress indicator
setView(new InvitationCodeView(this, true));
task = invitationTaskFactory.createTask(localAuthorId,
localInvitationCode, code);
task = invitationTaskFactory.createTask(localInvitationCode, code);
taskHandle = referenceManager.putReference(task, InvitationTask.class);
task.addListener(AddContactActivity.this);
// Add a second listener so we can remove the first in onDestroy(),

View File

@@ -114,7 +114,6 @@ public class ChangePasswordActivityTest {
@Test
public void testChangePasswordUI() {
PasswordController mockedPasswordController = this.passwordController;
SetupController mockedSetupController = this.setupController;
changePasswordActivity.setPasswordController(mockedPasswordController);
@@ -135,7 +134,7 @@ public class ChangePasswordActivityTest {
verify(mockedPasswordController, times(1))
.changePassword(eq(curPass), eq(safePass),
resultCaptor.capture());
// execute the callback
// execute the callbacks
resultCaptor.getValue().onResult(true);
assertEquals(changePasswordActivity.isFinishing(), true);
}
@@ -147,12 +146,14 @@ public class ChangePasswordActivityTest {
SetupController setupController =
changePasswordActivity.getSetupController();
// mock a resulthandler
ResultHandler<Long> resultHandler =
(ResultHandler<Long>) mock(ResultHandler.class);
setupController.createIdentity("nick", "some.old.pass", resultHandler);
ResultHandler<Void> resultHandler =
(ResultHandler<Void>) mock(ResultHandler.class);
setupController
.storeAuthorInfo("nick", "some.old.pass", resultHandler);
// blocking verification call with timeout that waits until the mocked
// result gets called with handle 0L, the expected value
verify(resultHandler, timeout(2000).times(1)).onResult(0L);
verify(resultHandler, timeout(2000).times(1))
.onResult(null);
SharedPreferences prefs =
changePasswordActivity
.getSharedPreferences("db", Context.MODE_PRIVATE);

View File

@@ -52,7 +52,6 @@ public class SetupActivityTest {
private TestSetupActivity setupActivity;
private TextInputLayout nicknameEntryWrapper;
private TextInputLayout passwordEntryWrapper;
private TextInputLayout passwordConfirmationWrapper;
private EditText nicknameEntry;
private EditText passwordEntry;
@@ -63,7 +62,7 @@ public class SetupActivityTest {
@Mock
private SetupController setupController;
@Captor
private ArgumentCaptor<ResultHandler<Long>> resultCaptor;
private ArgumentCaptor<ResultHandler<Void>> authorCaptor;
@Before
public void setUp() {
@@ -71,8 +70,6 @@ public class SetupActivityTest {
setupActivity = Robolectric.setupActivity(TestSetupActivity.class);
nicknameEntryWrapper = (TextInputLayout) setupActivity
.findViewById(R.id.nickname_entry_wrapper);
passwordEntryWrapper = (TextInputLayout) setupActivity
.findViewById(R.id.password_entry_wrapper);
passwordConfirmationWrapper = (TextInputLayout) setupActivity
.findViewById(R.id.password_confirm_wrapper);
nicknameEntry =
@@ -114,7 +111,6 @@ public class SetupActivityTest {
@Test
public void testCreateAccountUI() {
SetupController mockedController = this.setupController;
setupActivity.setController(mockedController);
// Mock strong password strength answer
@@ -131,9 +127,10 @@ public class SetupActivityTest {
// Verify that the controller's method was called with the correct
// params and get the callback
verify(mockedController, times(1))
.createIdentity(eq(nick), eq(safePass), resultCaptor.capture());
.storeAuthorInfo(eq(nick), eq(safePass),
authorCaptor.capture());
authorCaptor.getValue().onResult(null);
// execute the callback
resultCaptor.getValue().onResult(1L);
assertEquals(setupActivity.isFinishing(), true);
// Confirm that the correct Activity has been started
ShadowActivity shadowActivity = shadowOf(setupActivity);
@@ -158,13 +155,13 @@ public class SetupActivityTest {
public void testAccountCreation() {
SetupController controller = setupActivity.getController();
// mock a resulthandler
ResultHandler<Long> resultHandler =
(ResultHandler<Long>) mock(ResultHandler.class);
ResultHandler<Void> resultHandler =
(ResultHandler<Void>) mock(ResultHandler.class);
controller
.createIdentity("nick", "some.strong.pass", resultHandler);
.storeAuthorInfo("nick", "some.strong.pass", resultHandler);
// blocking verification call with timeout that waits until the mocked
// result gets called with handle 0L, the expected value
verify(resultHandler, timeout(2000).times(1)).onResult(0L);
verify(resultHandler, timeout(2000).times(1)).onResult(null);
SharedPreferences prefs =
setupActivity.getSharedPreferences("db", Context.MODE_PRIVATE);
// Confirm database key