diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageAccountRestoreJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageAccountRestoreJob.java deleted file mode 100644 index 904932acaf..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageAccountRestoreJob.java +++ /dev/null @@ -1,166 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.signal.core.util.logging.Log; -import org.thoughtcrime.securesms.database.SignalDatabase; -import org.thoughtcrime.securesms.dependencies.AppDependencies; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.JobManager; -import org.thoughtcrime.securesms.jobmanager.JobTracker; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.thoughtcrime.securesms.keyvalue.SignalStore; -import org.thoughtcrime.securesms.profiles.manage.UsernameRepository; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.storage.StorageSyncHelper; -import org.whispersystems.signalservice.api.SignalServiceAccountManager; -import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; -import org.whispersystems.signalservice.api.storage.SignalAccountRecord; -import org.whispersystems.signalservice.api.storage.SignalStorageManifest; -import org.whispersystems.signalservice.api.storage.SignalStorageRecord; -import org.whispersystems.signalservice.api.storage.StorageId; -import org.whispersystems.signalservice.api.storage.StorageKey; - -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.TimeUnit; - -/** - * Restored the AccountRecord present in the storage service, if any. This will overwrite any local - * data that is stored in AccountRecord, so this should only be done immediately after registration. - */ -public class StorageAccountRestoreJob extends BaseJob { - - public static String KEY = "StorageAccountRestoreJob"; - - public static long LIFESPAN = TimeUnit.SECONDS.toMillis(20); - - private static final String TAG = Log.tag(StorageAccountRestoreJob.class); - - public StorageAccountRestoreJob() { - this(new Parameters.Builder() - .setQueue(StorageSyncJob.QUEUE_KEY) - .addConstraint(NetworkConstraint.KEY) - .setMaxInstancesForFactory(1) - .setMaxAttempts(1) - .setLifespan(LIFESPAN) - .build()); - } - - private StorageAccountRestoreJob(@NonNull Parameters parameters) { - super(parameters); - } - - @Override - public @Nullable byte[] serialize() { - return null; - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - protected void onRun() throws Exception { - SignalServiceAccountManager accountManager = AppDependencies.getSignalServiceAccountManager(); - StorageKey storageServiceKey = SignalStore.storageService().getOrCreateStorageKey(); - - Log.i(TAG, "Retrieving manifest..."); - Optional manifest = accountManager.getStorageManifest(storageServiceKey); - - if (!manifest.isPresent()) { - Log.w(TAG, "Manifest did not exist or was undecryptable (bad key). Not restoring. Force-pushing."); - AppDependencies.getJobManager().add(new StorageForcePushJob()); - return; - } - - Log.i(TAG, "Resetting the local manifest to an empty state so that it will sync later."); - SignalStore.storageService().setManifest(SignalStorageManifest.EMPTY); - - Optional accountId = manifest.get().getAccountStorageId(); - - if (!accountId.isPresent()) { - Log.w(TAG, "Manifest had no account record! Not restoring."); - return; - } - - Log.i(TAG, "Retrieving account record..."); - List records = accountManager.readStorageRecords(storageServiceKey, Collections.singletonList(accountId.get())); - SignalStorageRecord record = records.size() > 0 ? records.get(0) : null; - - if (record == null) { - Log.w(TAG, "Could not find account record, even though we had an ID! Not restoring."); - return; - } - - if (record.getProto().account == null) { - Log.w(TAG, "The storage record didn't actually have an account on it! Not restoring."); - return; - } - - SignalAccountRecord accountRecord = new SignalAccountRecord(record.getId(), record.getProto().account); - - Log.i(TAG, "Applying changes locally..."); - SignalDatabase.getRawDatabase().beginTransaction(); - try { - StorageSyncHelper.applyAccountStorageSyncUpdates(context, Recipient.self().fresh(), accountRecord, false); - SignalDatabase.getRawDatabase().setTransactionSuccessful(); - } finally { - SignalDatabase.getRawDatabase().endTransaction(); - } - - // We will try to reclaim the username here, as early as possible, but the registration flow also enqueues a username restore job, - // so failing here isn't a huge deal - if (SignalStore.account().getUsername() != null) { - Log.i(TAG, "Attempting to reclaim username..."); - UsernameRepository.UsernameReclaimResult result = UsernameRepository.reclaimUsernameIfNecessary(); - Log.i(TAG, "Username reclaim result: " + result.name()); - } else { - Log.i(TAG, "No username to reclaim."); - } - - JobManager jobManager = AppDependencies.getJobManager(); - - if (!accountRecord.getProto().avatarUrlPath.isEmpty()) { - Log.i(TAG, "Fetching avatar..."); - Optional state = jobManager.runSynchronously(new RetrieveProfileAvatarJob(Recipient.self(), accountRecord.getProto().avatarUrlPath), LIFESPAN / 2); - - if (state.isPresent()) { - Log.i(TAG, "Avatar retrieved successfully. " + state.get()); - } else { - Log.w(TAG, "Avatar retrieval did not complete in time (or otherwise failed)."); - } - } else { - Log.i(TAG, "No avatar present. Not fetching."); - } - - Log.i(TAG, "Refreshing attributes..."); - Optional state = jobManager.runSynchronously(new RefreshAttributesJob(), LIFESPAN/2); - - if (state.isPresent()) { - Log.i(TAG, "Attributes refreshed successfully. " + state.get()); - } else { - Log.w(TAG, "Attribute refresh did not complete in time (or otherwise failed)."); - } - } - - @Override - protected boolean onShouldRetry(@NonNull Exception e) { - return e instanceof PushNetworkException; - } - - @Override - public void onFailure() { - } - - public static class Factory implements Job.Factory { - @Override - public @NonNull - StorageAccountRestoreJob create(@NonNull Parameters parameters, @Nullable byte[] serializedData) { - return new StorageAccountRestoreJob(parameters); - } - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageAccountRestoreJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageAccountRestoreJob.kt new file mode 100644 index 0000000000..52f0e540da --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageAccountRestoreJob.kt @@ -0,0 +1,138 @@ +package org.thoughtcrime.securesms.jobs + +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.dependencies.AppDependencies +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.profiles.manage.UsernameRepository.reclaimUsernameIfNecessary +import org.thoughtcrime.securesms.recipients.Recipient.Companion.self +import org.thoughtcrime.securesms.storage.StorageSyncHelper.applyAccountStorageSyncUpdates +import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException +import org.whispersystems.signalservice.api.storage.SignalAccountRecord +import org.whispersystems.signalservice.api.storage.SignalStorageManifest +import java.util.concurrent.TimeUnit + +/** + * Restored the AccountRecord present in the storage service, if any. This will overwrite any local + * data that is stored in AccountRecord, so this should only be done immediately after registration. + */ +class StorageAccountRestoreJob private constructor(parameters: Parameters) : BaseJob(parameters) { + companion object { + const val KEY: String = "StorageAccountRestoreJob" + + val LIFESPAN: Long = TimeUnit.SECONDS.toMillis(20) + + private val TAG = Log.tag(StorageAccountRestoreJob::class.java) + } + + constructor() : this( + Parameters.Builder() + .setQueue(StorageSyncJob.QUEUE_KEY) + .addConstraint(NetworkConstraint.KEY) + .setMaxInstancesForFactory(1) + .setMaxAttempts(1) + .setLifespan(LIFESPAN) + .build() + ) + + override fun serialize(): ByteArray? = null + + override fun getFactoryKey(): String = KEY + + @Throws(Exception::class) + override fun onRun() { + val accountManager = AppDependencies.signalServiceAccountManager + val storageServiceKey = SignalStore.storageService.getOrCreateStorageKey() + + Log.i(TAG, "Retrieving manifest...") + val manifest = accountManager.getStorageManifest(storageServiceKey) + + if (!manifest.isPresent) { + Log.w(TAG, "Manifest did not exist or was undecryptable (bad key). Not restoring. Force-pushing.") + AppDependencies.jobManager.add(StorageForcePushJob()) + return + } + + Log.i(TAG, "Resetting the local manifest to an empty state so that it will sync later.") + SignalStore.storageService.manifest = SignalStorageManifest.EMPTY + + val accountId = manifest.get().accountStorageId + + if (!accountId.isPresent) { + Log.w(TAG, "Manifest had no account record! Not restoring.") + return + } + + Log.i(TAG, "Retrieving account record...") + val records = accountManager.readStorageRecords(storageServiceKey, listOf(accountId.get())) + val record = if (records.size > 0) records[0] else null + + if (record == null) { + Log.w(TAG, "Could not find account record, even though we had an ID! Not restoring.") + return + } + + if (record.proto.account == null) { + Log.w(TAG, "The storage record didn't actually have an account on it! Not restoring.") + return + } + + val accountRecord = SignalAccountRecord(record.id, record.proto.account!!) + + Log.i(TAG, "Applying changes locally...") + SignalDatabase.rawDatabase.beginTransaction() + try { + applyAccountStorageSyncUpdates(context, self().fresh(), accountRecord, false) + SignalDatabase.rawDatabase.setTransactionSuccessful() + } finally { + SignalDatabase.rawDatabase.endTransaction() + } + + // We will try to reclaim the username here, as early as possible, but the registration flow also enqueues a username restore job, + // so failing here isn't a huge deal + if (SignalStore.account.username != null) { + Log.i(TAG, "Attempting to reclaim username...") + val result = reclaimUsernameIfNecessary() + Log.i(TAG, "Username reclaim result: " + result.name) + } else { + Log.i(TAG, "No username to reclaim.") + } + + if (accountRecord.proto.avatarUrlPath.isNotEmpty()) { + Log.i(TAG, "Fetching avatar...") + val state = AppDependencies.jobManager.runSynchronously(RetrieveProfileAvatarJob(self(), accountRecord.proto.avatarUrlPath), LIFESPAN / 2) + + if (state.isPresent) { + Log.i(TAG, "Avatar retrieved successfully. ${state.get()}") + } else { + Log.w(TAG, "Avatar retrieval did not complete in time (or otherwise failed).") + } + } else { + Log.i(TAG, "No avatar present. Not fetching.") + } + + Log.i(TAG, "Refreshing attributes...") + val state = AppDependencies.jobManager.runSynchronously(RefreshAttributesJob(), LIFESPAN / 2) + + if (state.isPresent) { + Log.i(TAG, "Attributes refreshed successfully. ${state.get()}") + } else { + Log.w(TAG, "Attribute refresh did not complete in time (or otherwise failed).") + } + } + + override fun onShouldRetry(e: Exception): Boolean { + return e is PushNetworkException + } + + override fun onFailure() { + } + + class Factory : Job.Factory { + override fun create(parameters: Parameters, serializedData: ByteArray?): StorageAccountRestoreJob { + return StorageAccountRestoreJob(parameters) + } + } +}