From e222f9631091b63d514c16feb73791db427b015e Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Mon, 6 Mar 2023 14:23:30 -0400 Subject: [PATCH] Add username sync job to be run after new registrations. --- .../securesms/jobmanager/JobManager.java | 28 +++++++++ .../securesms/jobs/JobManagerFactories.java | 1 + .../jobs/NewRegistrationUsernameSyncJob.kt | 57 +++++++++++++++++++ .../securesms/jobs/RefreshOwnProfileJob.java | 1 - .../securesms/pin/PinRestoreRepository.java | 7 ++- .../fragments/RegistrationLockFragment.java | 7 ++- .../viewmodel/RegistrationViewModel.java | 7 ++- 7 files changed, 104 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/jobs/NewRegistrationUsernameSyncJob.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/JobManager.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/JobManager.java index 997e489a62..07507e5b8d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/JobManager.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/JobManager.java @@ -497,6 +497,34 @@ public class JobManager implements ConstraintObserver.Notifier { enqueue(); } + public Optional enqueueAndBlockUntilCompletion(long timeout) { + CountDownLatch latch = new CountDownLatch(1); + AtomicReference resultState = new AtomicReference<>(); + JobTracker.JobListener listener = new JobTracker.JobListener() { + @Override + public void onStateChanged(@NonNull Job job, @NonNull JobTracker.JobState jobState) { + if (jobState.isComplete()) { + jobManager.removeListener(this); + resultState.set(jobState); + latch.countDown(); + } + } + }; + + enqueue(listener); + + try { + if (!latch.await(timeout, TimeUnit.MILLISECONDS)) { + return Optional.empty(); + } + } catch (InterruptedException e) { + Log.w(TAG, "Interrupted during enqueueSynchronously()", e); + return Optional.empty(); + } + + return Optional.ofNullable(resultState.get()); + } + @VisibleForTesting public List> getJobListChain() { return jobs; diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java index 0ac83927b6..1e7724e132 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java @@ -141,6 +141,7 @@ public final class JobManagerFactories { put(MultiDeviceVerifiedUpdateJob.KEY, new MultiDeviceVerifiedUpdateJob.Factory()); put(MultiDeviceViewOnceOpenJob.KEY, new MultiDeviceViewOnceOpenJob.Factory()); put(MultiDeviceViewedUpdateJob.KEY, new MultiDeviceViewedUpdateJob.Factory()); + put(NewRegistrationUsernameSyncJob.KEY, new NewRegistrationUsernameSyncJob.Factory()); put(NullMessageSendJob.KEY, new NullMessageSendJob.Factory()); put(OptimizeMessageSearchIndexJob.KEY, new OptimizeMessageSearchIndexJob.Factory()); put(PaymentLedgerUpdateJob.KEY, new PaymentLedgerUpdateJob.Factory()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/NewRegistrationUsernameSyncJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/NewRegistrationUsernameSyncJob.kt new file mode 100644 index 0000000000..e21928cd48 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/NewRegistrationUsernameSyncJob.kt @@ -0,0 +1,57 @@ +package org.thoughtcrime.securesms.jobs + +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.jobmanager.Data +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint +import org.thoughtcrime.securesms.recipients.Recipient +import java.io.IOException + +/** + * If a user registers and the storage sync service doesn't contain a username, + * then we should delete our username from the server. + */ +class NewRegistrationUsernameSyncJob private constructor(parameters: Parameters) : BaseJob(parameters) { + + companion object { + private val TAG = Log.tag(NewRegistrationUsernameSyncJob::class.java) + + const val KEY = "NewRegistrationUsernameSyncJob" + } + + constructor() : this( + Parameters.Builder() + .setQueue(StorageSyncJob.QUEUE_KEY) + .setMaxInstancesForFactory(1) + .addConstraint(NetworkConstraint.KEY) + .build() + ) + + override fun serialize(): Data = Data.EMPTY + + override fun getFactoryKey(): String = KEY + + override fun onFailure() = Unit + + override fun onRun() { + if (SignalDatabase.recipients.getUsername(Recipient.self().id).isNullOrEmpty()) { + Log.i(TAG, "Clearing username from server.") + ApplicationDependencies.getSignalServiceAccountManager().deleteUsername() + } else { + Log.i(TAG, "Local user has a username, attempting username synchronization.") + RefreshOwnProfileJob.checkUsernameIsInSync() + } + } + + override fun onShouldRetry(e: Exception): Boolean { + return e is IOException + } + + class Factory : Job.Factory { + override fun create(parameters: Parameters, data: Data): NewRegistrationUsernameSyncJob { + return NewRegistrationUsernameSyncJob(parameters) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.java index e646146edd..6349e674a8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.java @@ -253,7 +253,6 @@ public class RefreshOwnProfileJob extends BaseJob { } } - @VisibleForTesting static void checkUsernameIsInSync() { try { String localUsername = SignalDatabase.recipients().getUsername(Recipient.self().getId()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/pin/PinRestoreRepository.java b/app/src/main/java/org/thoughtcrime/securesms/pin/PinRestoreRepository.java index cb275fd72a..82a13c9f73 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/pin/PinRestoreRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/pin/PinRestoreRepository.java @@ -5,6 +5,7 @@ import androidx.annotation.NonNull; import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; +import org.thoughtcrime.securesms.jobs.NewRegistrationUsernameSyncJob; import org.thoughtcrime.securesms.jobs.StorageAccountRestoreJob; import org.thoughtcrime.securesms.jobs.StorageSyncJob; import org.signal.core.util.Stopwatch; @@ -34,7 +35,11 @@ public class PinRestoreRepository { ApplicationDependencies.getJobManager().runSynchronously(new StorageAccountRestoreJob(), StorageAccountRestoreJob.LIFESPAN); stopwatch.split("AccountRestore"); - ApplicationDependencies.getJobManager().runSynchronously(new StorageSyncJob(), TimeUnit.SECONDS.toMillis(10)); + ApplicationDependencies + .getJobManager() + .startChain(new StorageSyncJob()) + .then(new NewRegistrationUsernameSyncJob()) + .enqueueAndBlockUntilCompletion(TimeUnit.SECONDS.toMillis(10)); stopwatch.split("ContactRestore"); stopwatch.stop(TAG); diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/RegistrationLockFragment.java b/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/RegistrationLockFragment.java index 71a2ac2249..4869c41bae 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/RegistrationLockFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/RegistrationLockFragment.java @@ -8,6 +8,7 @@ import org.signal.core.util.concurrent.SimpleTask; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; +import org.thoughtcrime.securesms.jobs.NewRegistrationUsernameSyncJob; import org.thoughtcrime.securesms.jobs.StorageAccountRestoreJob; import org.thoughtcrime.securesms.jobs.StorageSyncJob; import org.thoughtcrime.securesms.keyvalue.SignalStore; @@ -52,7 +53,11 @@ public final class RegistrationLockFragment extends BaseRegistrationLockFragment ApplicationDependencies.getJobManager().runSynchronously(new StorageAccountRestoreJob(), StorageAccountRestoreJob.LIFESPAN); stopwatch.split("AccountRestore"); - ApplicationDependencies.getJobManager().runSynchronously(new StorageSyncJob(), TimeUnit.SECONDS.toMillis(10)); + ApplicationDependencies + .getJobManager() + .startChain(new StorageSyncJob()) + .then(new NewRegistrationUsernameSyncJob()) + .enqueueAndBlockUntilCompletion(TimeUnit.SECONDS.toMillis(10)); stopwatch.split("ContactRestore"); try { diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/viewmodel/RegistrationViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/registration/viewmodel/RegistrationViewModel.java index fc30e5ec8d..b4f7acef1f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/viewmodel/RegistrationViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/viewmodel/RegistrationViewModel.java @@ -12,6 +12,7 @@ import androidx.savedstate.SavedStateRegistryOwner; import org.signal.core.util.Stopwatch; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; +import org.thoughtcrime.securesms.jobs.NewRegistrationUsernameSyncJob; import org.thoughtcrime.securesms.jobs.StorageAccountRestoreJob; import org.thoughtcrime.securesms.jobs.StorageSyncJob; import org.thoughtcrime.securesms.keyvalue.SignalStore; @@ -383,7 +384,11 @@ public final class RegistrationViewModel extends BaseRegistrationViewModel { ApplicationDependencies.getJobManager().runSynchronously(new StorageAccountRestoreJob(), StorageAccountRestoreJob.LIFESPAN); stopwatch.split("AccountRestore"); - ApplicationDependencies.getJobManager().runSynchronously(new StorageSyncJob(), TimeUnit.SECONDS.toMillis(10)); + ApplicationDependencies + .getJobManager() + .startChain(new StorageSyncJob()) + .then(new NewRegistrationUsernameSyncJob()) + .enqueueAndBlockUntilCompletion(TimeUnit.SECONDS.toMillis(10)); stopwatch.split("ContactRestore"); try {