From 305edf192858787f349f56e620975bb2e1a56147 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Wed, 14 Jun 2023 10:28:34 -0400 Subject: [PATCH] Fix SQL crash in backup restore by preventing job from running until restore complete. --- .../newdevice/NewDeviceServerTask.java | 3 ++ .../jobmanager/impl/DataRestoreConstraint.kt | 34 +++++++++++++++++++ .../impl/DataRestoreConstraintObserver.kt | 27 +++++++++++++++ .../securesms/jobs/JobManagerFactories.java | 8 ++++- .../jobs/RebuildMessageSearchIndexJob.kt | 2 ++ .../fragments/RestoreBackupFragment.java | 5 +++ 6 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/DataRestoreConstraint.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/DataRestoreConstraintObserver.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/devicetransfer/newdevice/NewDeviceServerTask.java b/app/src/main/java/org/thoughtcrime/securesms/devicetransfer/newdevice/NewDeviceServerTask.java index 1c07ae452a..80d8f9dcae 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/devicetransfer/newdevice/NewDeviceServerTask.java +++ b/app/src/main/java/org/thoughtcrime/securesms/devicetransfer/newdevice/NewDeviceServerTask.java @@ -17,6 +17,7 @@ import org.thoughtcrime.securesms.backup.BackupPassphrase; import org.thoughtcrime.securesms.backup.FullBackupImporter; import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider; import org.thoughtcrime.securesms.database.SignalDatabase; +import org.thoughtcrime.securesms.jobmanager.impl.DataRestoreConstraint; import org.thoughtcrime.securesms.notifications.NotificationChannels; import java.io.IOException; @@ -38,6 +39,7 @@ final class NewDeviceServerTask implements ServerTask { EventBus.getDefault().register(this); try { + DataRestoreConstraint.setRestoringData(true); SQLiteDatabase database = SignalDatabase.getBackupDatabase(); String passphrase = "deadbeef"; @@ -66,6 +68,7 @@ final class NewDeviceServerTask implements ServerTask { EventBus.getDefault().post(new Status(0, Status.State.FAILURE_UNKNOWN)); } finally { EventBus.getDefault().unregister(this); + DataRestoreConstraint.setRestoringData(false); } long end = System.currentTimeMillis(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/DataRestoreConstraint.kt b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/DataRestoreConstraint.kt new file mode 100644 index 0000000000..8188d853c7 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/DataRestoreConstraint.kt @@ -0,0 +1,34 @@ +package org.thoughtcrime.securesms.jobmanager.impl + +import android.app.job.JobInfo +import org.thoughtcrime.securesms.jobmanager.Constraint + +/** + * Constraint that, when added, means that a job cannot be performed while a backup restore or device transfer + * is occurring. + */ +object DataRestoreConstraint : Constraint { + + const val KEY = "DataRestoreConstraint" + + @JvmStatic + var isRestoringData: Boolean = false + set(value) { + field = value + DataRestoreConstraintObserver.onChange() + } + + override fun isMet(): Boolean { + return !isRestoringData + } + + override fun getFactoryKey(): String = KEY + + override fun applyToJobInfo(jobInfoBuilder: JobInfo.Builder) = Unit + + class Factory : Constraint.Factory { + override fun create(): DataRestoreConstraint { + return DataRestoreConstraint + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/DataRestoreConstraintObserver.kt b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/DataRestoreConstraintObserver.kt new file mode 100644 index 0000000000..8def7bedc2 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/DataRestoreConstraintObserver.kt @@ -0,0 +1,27 @@ +package org.thoughtcrime.securesms.jobmanager.impl + +import org.thoughtcrime.securesms.jobmanager.ConstraintObserver + +/** + * An observer for the [DataRestoreConstraint]. This class expects to be told when a change happens, + * since the points at which it happens are triggered by application code. + */ +object DataRestoreConstraintObserver : ConstraintObserver { + + private const val REASON = "DataRestoreConstraint" + + private var notifier: ConstraintObserver.Notifier? = null + + override fun register(notifier: ConstraintObserver.Notifier) { + this.notifier = notifier + } + + /** + * Let the observer know that the change number state has changed. + */ + fun onChange() { + if (DataRestoreConstraint.isMet) { + notifier?.onConstraintMet(REASON) + } + } +} 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 f20f76f9ac..09fb4f7b41 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java @@ -12,8 +12,11 @@ import org.thoughtcrime.securesms.jobmanager.JobMigration; import org.thoughtcrime.securesms.jobmanager.impl.AutoDownloadEmojiConstraint; import org.thoughtcrime.securesms.jobmanager.impl.CellServiceConstraintObserver; import org.thoughtcrime.securesms.jobmanager.impl.ChangeNumberConstraint; +import org.thoughtcrime.securesms.jobmanager.impl.ChangeNumberConstraintObserver; import org.thoughtcrime.securesms.jobmanager.impl.ChargingConstraint; import org.thoughtcrime.securesms.jobmanager.impl.ChargingConstraintObserver; +import org.thoughtcrime.securesms.jobmanager.impl.DataRestoreConstraint; +import org.thoughtcrime.securesms.jobmanager.impl.DataRestoreConstraintObserver; import org.thoughtcrime.securesms.jobmanager.impl.DecryptionsDrainedConstraint; import org.thoughtcrime.securesms.jobmanager.impl.DecryptionsDrainedConstraintObserver; import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; @@ -292,6 +295,7 @@ public final class JobManagerFactories { put(AutoDownloadEmojiConstraint.KEY, new AutoDownloadEmojiConstraint.Factory(application)); put(ChangeNumberConstraint.KEY, new ChangeNumberConstraint.Factory()); put(ChargingConstraint.KEY, new ChargingConstraint.Factory()); + put(DataRestoreConstraint.KEY, new DataRestoreConstraint.Factory()); put(DecryptionsDrainedConstraint.KEY, new DecryptionsDrainedConstraint.Factory()); put(NetworkConstraint.KEY, new NetworkConstraint.Factory(application)); put(NetworkOrCellServiceConstraint.KEY, new NetworkOrCellServiceConstraint.Factory(application)); @@ -307,7 +311,9 @@ public final class JobManagerFactories { new NetworkConstraintObserver(application), new SqlCipherMigrationConstraintObserver(), new DecryptionsDrainedConstraintObserver(), - new NotInCallConstraintObserver()); + new NotInCallConstraintObserver(), + ChangeNumberConstraintObserver.INSTANCE, + DataRestoreConstraintObserver.INSTANCE); } public static List getJobMigrations(@NonNull Application application) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RebuildMessageSearchIndexJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/RebuildMessageSearchIndexJob.kt index 1f5ca73401..f393801670 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RebuildMessageSearchIndexJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RebuildMessageSearchIndexJob.kt @@ -4,6 +4,7 @@ import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.dependencies.ApplicationDependencies import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.jobmanager.impl.DataRestoreConstraint import org.thoughtcrime.securesms.transport.RetryLaterException import java.lang.Exception import java.lang.IllegalStateException @@ -24,6 +25,7 @@ class RebuildMessageSearchIndexJob private constructor(params: Parameters) : Bas private constructor() : this( Parameters.Builder() .setQueue("RebuildMessageSearchIndex") + .addConstraint(DataRestoreConstraint.KEY) .setMaxAttempts(3) .build() ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/RestoreBackupFragment.java b/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/RestoreBackupFragment.java index 8f60b1a217..6231942226 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/RestoreBackupFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/RestoreBackupFragment.java @@ -49,6 +49,8 @@ import org.thoughtcrime.securesms.backup.FullBackupImporter; import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider; import org.thoughtcrime.securesms.database.NoExternalStorageException; import org.thoughtcrime.securesms.database.SignalDatabase; +import org.thoughtcrime.securesms.jobmanager.impl.DataRestoreConstraint; +import org.thoughtcrime.securesms.jobmanager.impl.DataRestoreConstraintObserver; import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.registration.viewmodel.RegistrationViewModel; @@ -282,6 +284,7 @@ public final class RestoreBackupFragment extends LoggingFragment { protected BackupImportResult doInBackground(Void... voids) { try { Log.i(TAG, "Starting backup restore."); + DataRestoreConstraint.setRestoringData(true); SQLiteDatabase database = SignalDatabase.getBackupDatabase(); @@ -310,6 +313,8 @@ public final class RestoreBackupFragment extends LoggingFragment { } catch (IOException e) { Log.w(TAG, e); return BackupImportResult.FAILURE_UNKNOWN; + } finally { + DataRestoreConstraint.setRestoringData(false); } }