From 5c22d233ef6a2d7154be267537f41d3ddf816a5b Mon Sep 17 00:00:00 2001 From: ameba23 Date: Wed, 21 Apr 2021 16:34:56 +0200 Subject: [PATCH] Restore account activity/view model --- .../android/activity/ActivityComponent.java | 4 + .../recover/OwnerReturnShardViewModel.java | 46 ++----- .../recover/RestoreAccountActivity.java | 81 +++++++++++- .../recover/RestoreAccountViewModel.java | 120 +++++++++++++++++- 4 files changed, 212 insertions(+), 39 deletions(-) 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 c7aeb94c5..b97ee15ff 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 @@ -94,8 +94,10 @@ import org.briarproject.briar.android.socialbackup.recover.OwnerReturnShardFragm import org.briarproject.briar.android.socialbackup.ShardsSentFragment; import org.briarproject.briar.android.socialbackup.ThresholdSelectorFragment; import org.briarproject.briar.android.socialbackup.creation.CreateBackupModule; +import org.briarproject.briar.android.socialbackup.recover.RestoreAccountActivity; import org.briarproject.briar.android.splash.SplashScreenActivity; import org.briarproject.briar.android.test.TestDataActivity; +import org.briarproject.briar.api.socialbackup.recovery.RestoreAccount; import dagger.Component; @@ -206,6 +208,8 @@ public interface ActivityComponent { void inject(OwnerRecoveryModeMainFragment ownerRecoveryModeMainFragment); + void inject(RestoreAccountActivity restoreAccountActivity); + // Fragments void inject(AuthorNameFragment fragment); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardViewModel.java index 91a4cc7af..e5b02dfc6 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/OwnerReturnShardViewModel.java @@ -15,12 +15,12 @@ import org.briarproject.briar.android.contact.add.nearby.QrCodeUtils; import org.briarproject.briar.android.viewmodel.LiveEvent; import org.briarproject.briar.android.viewmodel.MutableLiveEvent; import org.briarproject.briar.api.socialbackup.BackupPayload; -import org.briarproject.briar.api.socialbackup.DarkCrystal; import org.briarproject.briar.api.socialbackup.ReturnShardPayload; import org.briarproject.briar.api.socialbackup.Shard; +import org.briarproject.briar.api.socialbackup.recovery.RestoreAccount; import org.briarproject.briar.api.socialbackup.recovery.SecretOwnerTask; import org.briarproject.briar.socialbackup.BackupPayloadDecoder; -import org.briarproject.briar.socialbackup.SocialBackup; +import org.briarproject.briar.api.socialbackup.SocialBackup; import java.net.InetAddress; import java.net.UnknownHostException; @@ -56,8 +56,7 @@ class OwnerReturnShardViewModel extends AndroidViewModel private final AndroidExecutor androidExecutor; private final Executor ioExecutor; private final SecretOwnerTask task; - private final DarkCrystal darkCrystal; - private final BackupPayloadDecoder backupPayloadDecoder; + private final RestoreAccount restoreAccount; private final MutableLiveEvent showQrCodeFragment = new MutableLiveEvent<>(); @@ -67,7 +66,6 @@ class OwnerReturnShardViewModel extends AndroidViewModel new MutableLiveEvent<>(); private boolean wasContinueClicked = false; private boolean isActivityResumed = false; - private ArrayList recoveredShards = new ArrayList<>(); private Bitmap qrCodeBitmap; private WifiManager wifiManager; private SecretKey secretKey; @@ -76,14 +74,12 @@ class OwnerReturnShardViewModel extends AndroidViewModel OwnerReturnShardViewModel(Application app, AndroidExecutor androidExecutor, SecretOwnerTask task, - DarkCrystal darkCrystal, - BackupPayloadDecoder backupPayloadDecoder, + RestoreAccount restoreAccount, @IoExecutor Executor ioExecutor) { super(app); this.androidExecutor = androidExecutor; this.ioExecutor = ioExecutor; - this.backupPayloadDecoder = backupPayloadDecoder; - this.darkCrystal = darkCrystal; + this.restoreAccount = restoreAccount; this.task = task; wifiManager = (WifiManager) app.getSystemService(WIFI_SERVICE); @@ -196,7 +192,7 @@ class OwnerReturnShardViewModel extends AndroidViewModel } public int getNumberOfShards() { - return recoveredShards.size(); + return restoreAccount.getNumberOfShards(); } @Override @@ -227,38 +223,14 @@ class OwnerReturnShardViewModel extends AndroidViewModel // TODO figure out how to actually use a hash set for these objects public boolean addToShardSet(ReturnShardPayload toAdd) { - boolean found = false; - for (ReturnShardPayload returnShardPayload : recoveredShards) { - if (toAdd.equals(returnShardPayload)) { - found = true; - break; - } - } - if (!found) recoveredShards.add(toAdd); - return !found; + return restoreAccount.addReturnShardPayload(toAdd); } public boolean canRecover() { - ArrayList shards = new ArrayList(); - for (ReturnShardPayload returnShardPayload : recoveredShards) { - // TODO check shards all have same secret id - shards.add(returnShardPayload.getShard()); - } - try { - secretKey = darkCrystal.combineShards(shards); - } catch (GeneralSecurityException e) { - // TODO handle error message - return false; - } - return true; + return restoreAccount.canRecover(); } public int recover() throws FormatException, GeneralSecurityException { - if (secretKey == null) throw new GeneralSecurityException(); - // TODO find backup with highest version number - BackupPayload backupPayload = recoveredShards.get(0).getBackupPayload(); - SocialBackup decodedBackup = backupPayloadDecoder.decodeBackupPayload(secretKey, backupPayload); - int version = decodedBackup.getVersion(); - return version; + return restoreAccount.recover(); } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountActivity.java index cd3aceb7f..5031e0855 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountActivity.java @@ -1,4 +1,83 @@ package org.briarproject.briar.android.socialbackup.recover; -public class RestoreAccountActivity { +import android.annotation.TargetApi; +import android.content.Intent; +import android.os.Bundle; + +import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; +import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; +import org.briarproject.briar.R; +import org.briarproject.briar.android.account.DozeFragment; +import org.briarproject.briar.android.account.SetPasswordFragment; +import org.briarproject.briar.android.activity.ActivityComponent; +import org.briarproject.briar.android.activity.BaseActivity; +import org.briarproject.briar.android.fragment.BaseFragment; + +import javax.annotation.Nullable; +import javax.inject.Inject; + +import androidx.lifecycle.ViewModelProvider; + +import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK; +import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; +import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; +import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME; +import static org.briarproject.briar.android.BriarApplication.ENTRY_ACTIVITY; +import static org.briarproject.briar.android.socialbackup.recover.RestoreAccountViewModel.State; + +@MethodsNotNullByDefault +@ParametersNotNullByDefault +public class RestoreAccountActivity extends BaseActivity + implements BaseFragment.BaseFragmentListener { + + @Inject + ViewModelProvider.Factory viewModelFactory; + RestoreAccountViewModel viewModel; + + @Override + public void injectActivity(ActivityComponent component) { + component.inject(this); + + viewModel = new ViewModelProvider(this, viewModelFactory) + .get(RestoreAccountViewModel.class); + viewModel.getState().observeEvent(this, this::onStateChanged); + } + + @Override + public void onCreate(@Nullable Bundle state) { + super.onCreate(state); + setContentView(R.layout.activity_fragment_container); + } + + private void onStateChanged(RestoreAccountViewModel.State state) { + if (state == RestoreAccountViewModel.State.SET_PASSWORD) { + showInitialFragment(SetPasswordFragment.newInstance()); + } else if (state == State.DOZE) { + showDozeFragment(); + } else if (state == State.CREATED || state == State.FAILED) { + // TODO: Show an error if failed + showApp(); + } + } + + @TargetApi(23) + void showDozeFragment() { + showNextFragment(DozeFragment.newInstance()); + } + + void showApp() { + Intent i = new Intent(this, ENTRY_ACTIVITY); + i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME | + FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_CLEAR_TOP); + startActivity(i); + supportFinishAfterTransition(); + overridePendingTransition(R.anim.screen_new_in, R.anim.screen_old_out); + } + + @Override + @Deprecated + public void runOnDbThread(Runnable runnable) { + throw new RuntimeException("Don't use this deprecated method here."); + } + } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountViewModel.java index 3f9f020af..521a97552 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/socialbackup/recover/RestoreAccountViewModel.java @@ -1,4 +1,122 @@ package org.briarproject.briar.android.socialbackup.recover; -public class RestoreAccountViewModel { +import android.app.Application; + +import org.briarproject.bramble.api.account.AccountManager; +import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator; +import org.briarproject.bramble.api.identity.Identity; +import org.briarproject.bramble.api.lifecycle.IoExecutor; +import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; +import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; +import org.briarproject.briar.R; +import org.briarproject.briar.android.account.DozeHelper; +import org.briarproject.briar.android.viewmodel.LiveEvent; +import org.briarproject.briar.android.viewmodel.MutableLiveEvent; +import org.briarproject.briar.api.socialbackup.recovery.RestoreAccount; + +import java.util.concurrent.Executor; +import java.util.logging.Logger; + +import javax.inject.Inject; + +import androidx.annotation.Nullable; +import androidx.lifecycle.AndroidViewModel; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; + +import static java.util.logging.Logger.getLogger; + +@MethodsNotNullByDefault +@ParametersNotNullByDefault +class RestoreAccountViewModel extends AndroidViewModel { + enum State {SET_PASSWORD, DOZE, CREATED, FAILED} + + private static final Logger LOG = + getLogger(RestoreAccountViewModel.class.getName()); + + @Nullable + private String password; + private final MutableLiveEvent + state = new MutableLiveEvent<>(); + private final MutableLiveData isCreatingAccount = + new MutableLiveData<>(false); + + private final AccountManager accountManager; + private final Executor ioExecutor; + private final PasswordStrengthEstimator strengthEstimator; + private final DozeHelper dozeHelper; + private final RestoreAccount restoreAccount; + + @Inject + RestoreAccountViewModel(Application app, + AccountManager accountManager, + RestoreAccount restoreAccount, + @IoExecutor Executor ioExecutor, + PasswordStrengthEstimator strengthEstimator, + DozeHelper dozeHelper) { + super(app); + this.accountManager = accountManager; + this.ioExecutor = ioExecutor; + this.strengthEstimator = strengthEstimator; + this.dozeHelper = dozeHelper; + this.restoreAccount = restoreAccount; + + ioExecutor.execute(() -> { + if (accountManager.accountExists()) { + throw new AssertionError(); + } else { + state.postEvent(State.SET_PASSWORD); + } + }); + } + + LiveEvent getState() { + return state; + } + + LiveData getIsCreatingAccount() { + return isCreatingAccount; + } + +// void setAuthorName(String authorName) { +// this.authorName = authorName; +// state.setEvent(SET_PASSWORD); +// } + + void setPassword(String password) { + this.password = password; + if (needToShowDozeFragment()) { + state.setEvent(State.DOZE); + } else { + createAccount(); + } + } + + float estimatePasswordStrength(String password) { + return strengthEstimator.estimateStrength(password); + } + + boolean needToShowDozeFragment() { + return dozeHelper.needToShowDozeFragment(getApplication()); + } + + void dozeExceptionConfirmed() { + createAccount(); + } + + private void createAccount() { +// if (authorName == null) throw new IllegalStateException(); + if (password == null) throw new IllegalStateException(); + isCreatingAccount.setValue(true); + Identity identity = restoreAccount.getSocialBackup().getIdentity(); + ioExecutor.execute(() -> { + if (accountManager.restoreAccount(identity, password)) { + LOG.info("Restore account"); + state.postEvent(State.CREATED); + } else { + LOG.warning("Failed to create account"); + state.postEvent(State.FAILED); + } + }); + } }