From abaefacb69314086326f0d3239486027e1eba930 Mon Sep 17 00:00:00 2001
From: akwizgran <michael@briarproject.org>
Date: Fri, 27 Jul 2018 12:06:55 +0100
Subject: [PATCH] Add javadocs.

---
 .../account/AndroidAccountManager.java        | 23 ++++---
 .../bramble/api/account/AccountManager.java   | 40 +++++++++++
 .../bramble/account/AccountManagerImpl.java   | 67 ++++++++++++-------
 .../briar/android/login/PasswordActivity.java |  1 +
 4 files changed, 99 insertions(+), 32 deletions(-)

diff --git a/bramble-android/src/main/java/org/briarproject/bramble/account/AndroidAccountManager.java b/bramble-android/src/main/java/org/briarproject/bramble/account/AndroidAccountManager.java
index d792672c4e..22c279ecaa 100644
--- a/bramble-android/src/main/java/org/briarproject/bramble/account/AndroidAccountManager.java
+++ b/bramble-android/src/main/java/org/briarproject/bramble/account/AndroidAccountManager.java
@@ -38,12 +38,15 @@ class AndroidAccountManager extends AccountManagerImpl
 	@Override
 	@Nullable
 	protected String loadEncryptedDatabaseKey() {
-		String key = getDatabaseKeyFromPreferences();
-		if (key == null) key = super.loadEncryptedDatabaseKey();
-		else migrateDatabaseKeyToFile(key);
-		return key;
+		synchronized (stateChangeLock) {
+			String key = getDatabaseKeyFromPreferences();
+			if (key == null) key = super.loadEncryptedDatabaseKey();
+			else migrateDatabaseKeyToFile(key);
+			return key;
+		}
 	}
 
+	// Locking: stateChangeLock
 	@Nullable
 	private String getDatabaseKeyFromPreferences() {
 		String key = prefs.getString(PREF_DB_KEY, null);
@@ -52,6 +55,7 @@ class AndroidAccountManager extends AccountManagerImpl
 		return key;
 	}
 
+	// Locking: stateChangeLock
 	private void migrateDatabaseKeyToFile(String key) {
 		if (storeEncryptedDatabaseKey(key)) {
 			if (prefs.edit().remove(PREF_DB_KEY).commit())
@@ -64,12 +68,15 @@ class AndroidAccountManager extends AccountManagerImpl
 
 	@Override
 	public void deleteAccount() {
-		super.deleteAccount();
-		SharedPreferences defaultPrefs =
-				PreferenceManager.getDefaultSharedPreferences(appContext);
-		deleteAppData(prefs, defaultPrefs);
+		synchronized (stateChangeLock) {
+			super.deleteAccount();
+			SharedPreferences defaultPrefs =
+					PreferenceManager.getDefaultSharedPreferences(appContext);
+			deleteAppData(prefs, defaultPrefs);
+		}
 	}
 
+	// Locking: stateChangeLock
 	private void deleteAppData(SharedPreferences... clear) {
 		// Clear and commit shared preferences
 		for (SharedPreferences prefs : clear) {
diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/account/AccountManager.java b/bramble-api/src/main/java/org/briarproject/bramble/api/account/AccountManager.java
index 15d5312203..e2784c1458 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/account/AccountManager.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/account/AccountManager.java
@@ -8,18 +8,58 @@ import javax.annotation.Nullable;
 @NotNullByDefault
 public interface AccountManager {
 
+	/**
+	 * Returns true if the manager has the database key. This will be false
+	 * before {@link #createAccount(String)} or {@link #signIn(String)} has
+	 * been called, and true after {@link #createAccount(String)} or
+	 * {@link #signIn(String)} has returned, until the process exits.
+	 */
 	boolean hasDatabaseKey();
 
+	/**
+	 * Returns the database key if the manager has it. This will be null
+	 * before {@link #createAccount(String)} or {@link #signIn(String)} has
+	 * been called, and non-null after {@link #createAccount(String)} or
+	 * {@link #signIn(String)} has returned, until the process exits.
+	 */
 	@Nullable
 	SecretKey getDatabaseKey();
 
+	/**
+	 * Returns true if the encrypted database key can be loaded from disk and
+	 * the database directory exists.
+	 */
 	boolean accountExists();
 
+	/**
+	 * Creates a database key, encrypts it with the given password and stores
+	 * it on disk. This method does not create the database directory, so
+	 * {@link #accountExists()} will continue to return false until the
+	 * database directory is created.
+	 */
 	boolean createAccount(String password);
 
+	/**
+	 * Deletes all account state from disk. {@link #accountExists()} will
+	 * return false after this method returns.
+	 */
 	void deleteAccount();
 
+	/**
+	 * Loads the encrypted database key from disk and decrypts it with the
+	 * given password.
+	 *
+	 * @return true if the database key was successfully loaded and decrypted.
+	 */
 	boolean signIn(String password);
 
+	/**
+	 * Loads the encrypted database key from disk, decrypts it with the old
+	 * password, encrypts it with the new password, and stores it on disk,
+	 * replacing the old key.
+	 *
+	 * @return true if the database key was successfully loaded, re-encrypted
+	 * and stored.
+	 */
 	boolean changePassword(String oldPassword, String newPassword);
 }
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/account/AccountManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/account/AccountManagerImpl.java
index 4d076fd2e8..6162b3e427 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/account/AccountManagerImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/account/AccountManagerImpl.java
@@ -38,6 +38,8 @@ class AccountManagerImpl implements AccountManager {
 	private final CryptoComponent crypto;
 	private final File dbKeyFile, dbKeyBackupFile;
 
+	protected final Object stateChangeLock = new Object();
+
 	@Nullable
 	private volatile SecretKey databaseKey = null;
 
@@ -63,18 +65,21 @@ class AccountManagerImpl implements AccountManager {
 
 	@Nullable
 	protected String loadEncryptedDatabaseKey() {
-		String key = readDbKeyFromFile(dbKeyFile);
-		if (key == null) {
-			LOG.info("No database key in primary file");
-			key = readDbKeyFromFile(dbKeyBackupFile);
-			if (key == null) LOG.info("No database key in backup file");
-			else LOG.warning("Found database key in backup file");
-		} else {
-			LOG.info("Found database key in primary file");
+		synchronized (stateChangeLock) {
+			String key = readDbKeyFromFile(dbKeyFile);
+			if (key == null) {
+				LOG.info("No database key in primary file");
+				key = readDbKeyFromFile(dbKeyBackupFile);
+				if (key == null) LOG.info("No database key in backup file");
+				else LOG.warning("Found database key in backup file");
+			} else {
+				LOG.info("Found database key in primary file");
+			}
+			return key;
 		}
-		return key;
 	}
 
+	// Locking: stateChangeLock
 	@Nullable
 	private String readDbKeyFromFile(File f) {
 		if (!f.exists()) {
@@ -93,6 +98,7 @@ class AccountManagerImpl implements AccountManager {
 		}
 	}
 
+	// Locking: stateChangeLock
 	protected boolean storeEncryptedDatabaseKey(String hex) {
 		LOG.info("Storing database key in file");
 		// Create the directory if necessary
@@ -130,6 +136,7 @@ class AccountManagerImpl implements AccountManager {
 		}
 	}
 
+	// Locking: stateChangeLock
 	private void writeDbKeyToFile(String key, File f) throws IOException {
 		FileOutputStream out = new FileOutputStream(f);
 		out.write(key.getBytes("UTF-8"));
@@ -139,18 +146,23 @@ class AccountManagerImpl implements AccountManager {
 
 	@Override
 	public boolean accountExists() {
-		return loadEncryptedDatabaseKey() != null
-				&& databaseConfig.getDatabaseDirectory().isDirectory();
+		synchronized (stateChangeLock) {
+			return loadEncryptedDatabaseKey() != null
+					&& databaseConfig.getDatabaseDirectory().isDirectory();
+		}
 	}
 
 	@Override
 	public boolean createAccount(String password) {
-		SecretKey key = crypto.generateSecretKey();
-		if (!encryptAndStoreDatabaseKey(key, password)) return false;
-		databaseKey = key;
-		return true;
+		synchronized (stateChangeLock) {
+			SecretKey key = crypto.generateSecretKey();
+			if (!encryptAndStoreDatabaseKey(key, password)) return false;
+			databaseKey = key;
+			return true;
+		}
 	}
 
+	// Locking: stateChangeLock
 	private boolean encryptAndStoreDatabaseKey(SecretKey key, String password) {
 		byte[] plaintext = key.getBytes();
 		byte[] ciphertext = crypto.encryptWithPassword(plaintext, password);
@@ -159,19 +171,24 @@ class AccountManagerImpl implements AccountManager {
 
 	@Override
 	public void deleteAccount() {
-		LOG.info("Deleting account");
-		IoUtils.deleteFileOrDir(databaseConfig.getDatabaseKeyDirectory());
-		IoUtils.deleteFileOrDir(databaseConfig.getDatabaseDirectory());
+		synchronized (stateChangeLock) {
+			LOG.info("Deleting account");
+			IoUtils.deleteFileOrDir(databaseConfig.getDatabaseKeyDirectory());
+			IoUtils.deleteFileOrDir(databaseConfig.getDatabaseDirectory());
+		}
 	}
 
 	@Override
 	public boolean signIn(String password) {
-		SecretKey key = loadAndDecryptDatabaseKey(password);
-		if (key == null) return false;
-		databaseKey = key;
-		return true;
+		synchronized (stateChangeLock) {
+			SecretKey key = loadAndDecryptDatabaseKey(password);
+			if (key == null) return false;
+			databaseKey = key;
+			return true;
+		}
 	}
 
+	// Locking: stateChangeLock
 	@Nullable
 	private SecretKey loadAndDecryptDatabaseKey(String password) {
 		String hex = loadEncryptedDatabaseKey();
@@ -190,7 +207,9 @@ class AccountManagerImpl implements AccountManager {
 
 	@Override
 	public boolean changePassword(String oldPassword, String newPassword) {
-		SecretKey key = loadAndDecryptDatabaseKey(oldPassword);
-		return key != null && encryptAndStoreDatabaseKey(key, newPassword);
+		synchronized (stateChangeLock) {
+			SecretKey key = loadAndDecryptDatabaseKey(oldPassword);
+			return key != null && encryptAndStoreDatabaseKey(key, newPassword);
+		}
 	}
 }
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordActivity.java
index 537d00d08e..762744e8ae 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordActivity.java
@@ -53,6 +53,7 @@ public class PasswordActivity extends BaseActivity {
 		overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
 
 		if (!accountManager.accountExists()) {
+			// TODO: Finish instead of deleting account?
 			deleteAccount();
 			return;
 		}
-- 
GitLab