From 93d78b3b2ea1dbf8e6e1357cf04c05c8d4d4c078 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Tue, 9 May 2023 15:35:48 -0400 Subject: [PATCH] Improve conditional logic around prekey refresh schedule. --- .../crypto/storage/PreKeyMetadataStore.kt | 2 +- .../securesms/jobs/JobManagerFactories.java | 2 + .../securesms/jobs/PreKeysSyncJob.kt | 133 ++++++++---------- .../securesms/jobs/PushSendJob.java | 17 ++- .../securesms/keyvalue/AccountValues.kt | 10 +- .../keyvalue/MiscellaneousValues.java | 7 +- .../migrations/ApplicationMigrations.java | 7 +- .../migrations/PreKeysSyncMigrationJob.kt | 36 +++++ .../service/RotateSignedPreKeyListener.java | 6 +- 9 files changed, 128 insertions(+), 92 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/migrations/PreKeysSyncMigrationJob.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/PreKeyMetadataStore.kt b/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/PreKeyMetadataStore.kt index 82f71c7223..efba5b20a2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/PreKeyMetadataStore.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/PreKeyMetadataStore.kt @@ -7,6 +7,6 @@ interface PreKeyMetadataStore { var nextSignedPreKeyId: Int var activeSignedPreKeyId: Int var isSignedPreKeyRegistered: Boolean - var signedPreKeyFailureCount: Int + var lastSignedPreKeyRotationTime: Long var nextOneTimePreKeyId: Int } 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 b8bf2922f3..3a1d0ab29e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java @@ -57,6 +57,7 @@ import org.thoughtcrime.securesms.migrations.PinOptOutMigration; import org.thoughtcrime.securesms.migrations.PinReminderMigrationJob; import org.thoughtcrime.securesms.migrations.PniAccountInitializationMigrationJob; import org.thoughtcrime.securesms.migrations.PniMigrationJob; +import org.thoughtcrime.securesms.migrations.PreKeysSyncMigrationJob; import org.thoughtcrime.securesms.migrations.ProfileMigrationJob; import org.thoughtcrime.securesms.migrations.ProfileSharingUpdateMigrationJob; import org.thoughtcrime.securesms.migrations.RebuildMessageSearchIndexMigrationJob; @@ -239,6 +240,7 @@ public final class JobManagerFactories { put(PinReminderMigrationJob.KEY, new PinReminderMigrationJob.Factory()); put(PniAccountInitializationMigrationJob.KEY, new PniAccountInitializationMigrationJob.Factory()); put(PniMigrationJob.KEY, new PniMigrationJob.Factory()); + put(PreKeysSyncMigrationJob.KEY, new PreKeysSyncMigrationJob.Factory()); put(ProfileMigrationJob.KEY, new ProfileMigrationJob.Factory()); put(ProfileSharingUpdateMigrationJob.KEY, new ProfileSharingUpdateMigrationJob.Factory()); put(RebuildMessageSearchIndexMigrationJob.KEY, new RebuildMessageSearchIndexMigrationJob.Factory()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PreKeysSyncJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/PreKeysSyncJob.kt index da0bcd8d60..138149ec03 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PreKeysSyncJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PreKeysSyncJob.kt @@ -3,46 +3,55 @@ package org.thoughtcrime.securesms.jobs import androidx.annotation.VisibleForTesting import org.signal.core.util.logging.Log import org.signal.libsignal.protocol.state.SignalProtocolStore +import org.signal.libsignal.protocol.state.SignedPreKeyRecord import org.thoughtcrime.securesms.crypto.PreKeyUtil import org.thoughtcrime.securesms.crypto.storage.PreKeyMetadataStore import org.thoughtcrime.securesms.dependencies.ApplicationDependencies import org.thoughtcrime.securesms.jobmanager.Job -import org.thoughtcrime.securesms.jobmanager.JsonJobData import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint import org.thoughtcrime.securesms.keyvalue.SignalStore -import org.thoughtcrime.securesms.util.TextSecurePreferences import org.whispersystems.signalservice.api.push.ServiceId import org.whispersystems.signalservice.api.push.ServiceIdType import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException import java.util.concurrent.TimeUnit +import kotlin.time.Duration.Companion.days +import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.DurationUnit /** * Regardless of the current state of affairs with respect to prekeys for either ACI or PNI identities, will * attempt to make the state valid. * - * If prekeys aren't registered for an identity they will be created. - * - * If prekeys are registered but the count is below the minimum threshold, then new ones will be uploaded. + * It will rotate/create signed prekeys for both ACI and PNI identities, as well as ensure that the user + * has a sufficient number of one-time prekeys available on the service. */ -class PreKeysSyncJob private constructor(private val forceRotate: Boolean = false, parameters: Parameters) : BaseJob(parameters) { +class PreKeysSyncJob private constructor(parameters: Parameters) : BaseJob(parameters) { companion object { const val KEY = "PreKeysSyncJob" private val TAG = Log.tag(PreKeysSyncJob::class.java) - private val KEY_FORCE_ROTATE = "force_rotate" - private const val PREKEY_MINIMUM = 10 - private val REFRESH_INTERVAL = TimeUnit.DAYS.toMillis(3) - fun create(forceRotate: Boolean = false): PreKeysSyncJob { - return PreKeysSyncJob(forceRotate) + /** The minimum number of one-time prekeys we want to the service to have. If we have less than this, refill. */ + private const val ONE_TIME_PREKEY_MINIMUM = 10 + + /** How often we want to rotate signed prekeys. */ + @JvmField + val REFRESH_INTERVAL = 2.days.inWholeMilliseconds + + /** If signed prekeys are older than this, we will require rotation before sending messages. */ + @JvmField + val MAXIMUM_ALLOWED_SIGNED_PREKEY_AGE = 14.days.inWholeMilliseconds + + @JvmStatic + fun create(): PreKeysSyncJob { + return PreKeysSyncJob() } @JvmStatic - @JvmOverloads - fun enqueue(forceRotate: Boolean = false) { - ApplicationDependencies.getJobManager().add(create(forceRotate)) + fun enqueue() { + ApplicationDependencies.getJobManager().add(create()) } @JvmStatic @@ -54,19 +63,20 @@ class PreKeysSyncJob private constructor(private val forceRotate: Boolean = fals Log.i(TAG, "Some signed prekeys aren't active yet. Enqueuing a job. ACI: ${SignalStore.account().aciPreKeys.activeSignedPreKeyId >= 0} PNI: ${SignalStore.account().pniPreKeys.activeSignedPreKeyId >= 0}") ApplicationDependencies.getJobManager().add(PreKeysSyncJob()) } else { - val timeSinceLastRefresh = System.currentTimeMillis() - SignalStore.misc().lastPrekeyRefreshTime + val timeSinceLastFullRefresh = System.currentTimeMillis() - SignalStore.misc().lastFullPrekeyRefreshTime - if (timeSinceLastRefresh > REFRESH_INTERVAL) { - Log.i(TAG, "Scheduling a prekey refresh. Time since last schedule: $timeSinceLastRefresh ms") + if (timeSinceLastFullRefresh >= REFRESH_INTERVAL || timeSinceLastFullRefresh < 0) { + Log.i(TAG, "Scheduling a prekey refresh. Time since last full refresh: $timeSinceLastFullRefresh ms") ApplicationDependencies.getJobManager().add(PreKeysSyncJob()) + } else { + Log.d(TAG, "No prekey job needed. Time since last full refresh: $timeSinceLastFullRefresh ms") } } } } @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - constructor(forceRotate: Boolean = false) : this( - forceRotate, + constructor() : this( Parameters.Builder() .setQueue("PreKeysSyncJob") .addConstraint(NetworkConstraint.KEY) @@ -78,11 +88,7 @@ class PreKeysSyncJob private constructor(private val forceRotate: Boolean = fals override fun getFactoryKey(): String = KEY - override fun serialize(): ByteArray? { - return JsonJobData.Builder() - .putBoolean(KEY_FORCE_ROTATE, forceRotate) - .serialize() - } + override fun serialize(): ByteArray? = null override fun onRun() { if (!SignalStore.account().isRegistered || SignalStore.account().aci == null || SignalStore.account().pni == null) { @@ -92,7 +98,7 @@ class PreKeysSyncJob private constructor(private val forceRotate: Boolean = fals syncPreKeys(ServiceIdType.ACI, SignalStore.account().aci, ApplicationDependencies.getProtocolStore().aci(), SignalStore.account().aciPreKeys) syncPreKeys(ServiceIdType.PNI, SignalStore.account().pni, ApplicationDependencies.getProtocolStore().pni(), SignalStore.account().pniPreKeys) - SignalStore.misc().lastPrekeyRefreshTime = System.currentTimeMillis() + SignalStore.misc().lastFullPrekeyRefreshTime = System.currentTimeMillis() } private fun syncPreKeys(serviceIdType: ServiceIdType, serviceId: ServiceId?, protocolStore: SignalProtocolStore, metadataStore: PreKeyMetadataStore) { @@ -101,55 +107,41 @@ class PreKeysSyncJob private constructor(private val forceRotate: Boolean = fals return } - if (metadataStore.isSignedPreKeyRegistered && metadataStore.activeSignedPreKeyId >= 0) { - if (forceRotate || System.currentTimeMillis() > TextSecurePreferences.getSignedPreKeyRotationTime(context) || metadataStore.signedPreKeyFailureCount > 5) { - log(serviceIdType, "Rotating signed prekey...") - rotateSignedPreKey(serviceIdType, protocolStore, metadataStore) - } else { - log(serviceIdType, "Refreshing prekeys...") - refreshKeys(serviceIdType, protocolStore, metadataStore) - } - } else { - log(serviceIdType, "Creating signed prekey...") - rotateSignedPreKey(serviceIdType, protocolStore, metadataStore) - } - } - - private fun rotateSignedPreKey(serviceIdType: ServiceIdType, protocolStore: SignalProtocolStore, metadataStore: PreKeyMetadataStore) { - val signedPreKeyRecord = PreKeyUtil.generateAndStoreSignedPreKey(protocolStore, metadataStore) - ApplicationDependencies.getSignalServiceAccountManager().setSignedPreKey(serviceIdType, signedPreKeyRecord) - - metadataStore.activeSignedPreKeyId = signedPreKeyRecord.id - metadataStore.isSignedPreKeyRegistered = true - metadataStore.signedPreKeyFailureCount = 0 - } - - private fun refreshKeys(serviceIdType: ServiceIdType, protocolStore: SignalProtocolStore, metadataStore: PreKeyMetadataStore) { val accountManager = ApplicationDependencies.getSignalServiceAccountManager() - val availableKeys = accountManager.getPreKeysCount(serviceIdType) - log(serviceIdType, "Available keys: $availableKeys") + val signedPreKeyRegistered = metadataStore.isSignedPreKeyRegistered && metadataStore.activeSignedPreKeyId >= 0 + val timeSinceLastSignedPreKeyRotation = System.currentTimeMillis() - metadataStore.lastSignedPreKeyRotationTime - if (availableKeys >= PREKEY_MINIMUM && metadataStore.isSignedPreKeyRegistered) { - log(serviceIdType, "Available keys sufficient.") - return + val activeSignedPreKeyRecord: SignedPreKeyRecord = if (!signedPreKeyRegistered || timeSinceLastSignedPreKeyRotation >= REFRESH_INTERVAL) { + log(serviceIdType, "Rotating signed prekey. SignedPreKeyRegistered: $signedPreKeyRegistered, TimeSinceLastRotation: $timeSinceLastSignedPreKeyRotation ms (${timeSinceLastSignedPreKeyRotation.milliseconds.toDouble(DurationUnit.DAYS)} days)") + + val signedPreKeyRecord: SignedPreKeyRecord = PreKeyUtil.generateAndStoreSignedPreKey(protocolStore, metadataStore) + accountManager.setSignedPreKey(serviceIdType, signedPreKeyRecord) + + metadataStore.activeSignedPreKeyId = signedPreKeyRecord.id + metadataStore.isSignedPreKeyRegistered = true + metadataStore.lastSignedPreKeyRotationTime = System.currentTimeMillis() + + signedPreKeyRecord + } else { + log(serviceIdType, "No need to rotate signed prekey. TimeSinceLastRotation: $timeSinceLastSignedPreKeyRotation ms (${timeSinceLastSignedPreKeyRotation.milliseconds.toDouble(DurationUnit.DAYS)} days)") + protocolStore.loadSignedPreKey(metadataStore.activeSignedPreKeyId) } - val preKeyRecords = PreKeyUtil.generateAndStoreOneTimePreKeys(protocolStore, metadataStore) - val signedPreKeyRecord = PreKeyUtil.generateAndStoreSignedPreKey(protocolStore, metadataStore) - val identityKey = protocolStore.identityKeyPair + val availableOneTimePreKeys = accountManager.getPreKeysCount(serviceIdType) - log(serviceIdType, "Registering new prekeys...") + if (availableOneTimePreKeys < ONE_TIME_PREKEY_MINIMUM) { + log(serviceIdType, "There are $availableOneTimePreKeys one-time prekeys available, which is not sufficient. Uploading more.") - accountManager.setPreKeys(serviceIdType, identityKey.publicKey, signedPreKeyRecord, preKeyRecords) - metadataStore.activeSignedPreKeyId = signedPreKeyRecord.id - metadataStore.isSignedPreKeyRegistered = true + val preKeyRecords = PreKeyUtil.generateAndStoreOneTimePreKeys(protocolStore, metadataStore) + val identityKey = protocolStore.identityKeyPair + accountManager.setPreKeys(serviceIdType, identityKey.publicKey, activeSignedPreKeyRecord, preKeyRecords) + } else { + log(serviceIdType, "There are $availableOneTimePreKeys one-time prekeys available, which is sufficient. No need to upload.") + } log(serviceIdType, "Cleaning prekeys...") PreKeyUtil.cleanSignedPreKeys(protocolStore, metadataStore) - - SignalStore.misc().lastPrekeyRefreshTime = System.currentTimeMillis() - log(serviceIdType, "Successfully refreshed prekeys.") } override fun onShouldRetry(e: Exception): Boolean { @@ -160,15 +152,7 @@ class PreKeysSyncJob private constructor(private val forceRotate: Boolean = fals } } - override fun onFailure() { - val aciStore = SignalStore.account().aciPreKeys - val pniStore = SignalStore.account().pniPreKeys - - if ((aciStore.isSignedPreKeyRegistered || pniStore.isSignedPreKeyRegistered) && forceRotate) { - aciStore.signedPreKeyFailureCount++ - pniStore.signedPreKeyFailureCount++ - } - } + override fun onFailure() = Unit private fun log(serviceIdType: ServiceIdType, message: String) { Log.i(TAG, "[$serviceIdType] $message") @@ -176,8 +160,7 @@ class PreKeysSyncJob private constructor(private val forceRotate: Boolean = fals class Factory : Job.Factory { override fun create(parameters: Parameters, serializedData: ByteArray?): PreKeysSyncJob { - val data = JsonJobData.deserialize(serializedData) - return PreKeysSyncJob(data.getBooleanOrDefault(KEY_FORCE_ROTATE, false), parameters) + return PreKeysSyncJob(parameters) } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java index 25da3c80f5..27c08e26dd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java @@ -39,12 +39,12 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.events.PartProgressEvent; import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.JobManager; +import org.thoughtcrime.securesms.jobmanager.JobTracker; import org.thoughtcrime.securesms.jobmanager.impl.BackoffUtil; import org.thoughtcrime.securesms.keyvalue.CertificateType; import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.linkpreview.LinkPreview; import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri; -import org.thoughtcrime.securesms.mms.MmsException; import org.thoughtcrime.securesms.mms.OutgoingMessage; import org.thoughtcrime.securesms.mms.PartAuthority; import org.thoughtcrime.securesms.mms.QuoteModel; @@ -99,9 +99,18 @@ public abstract class PushSendJob extends SendJob { @Override protected final void onSend() throws Exception { - if (SignalStore.account().aciPreKeys().getSignedPreKeyFailureCount() > 5) { - PreKeysSyncJob.enqueue(true); - throw new TextSecureExpiredException("Too many signed prekey rotation failures"); + long timeSinceSignedPreKeyRotation = System.currentTimeMillis() - SignalStore.account().aciPreKeys().getLastSignedPreKeyRotationTime(); + + if (timeSinceSignedPreKeyRotation > PreKeysSyncJob.MAXIMUM_ALLOWED_SIGNED_PREKEY_AGE || timeSinceSignedPreKeyRotation < 0) { + warn(TAG, "It's been too long since rotating our signed prekey (" + timeSinceSignedPreKeyRotation + " ms)! Attempting to rotate now."); + + Optional state = ApplicationDependencies.getJobManager().runSynchronously(PreKeysSyncJob.create(), TimeUnit.SECONDS.toMillis(30)); + + if (state.isPresent() && state.get() == JobTracker.JobState.SUCCESS) { + log(TAG, "Successfully refreshed prekeys. Continuing."); + } else { + throw new RetryLaterException(new TextSecureExpiredException("Failed to refresh prekeys! State: " + (state.isEmpty() ? "" : state.get()))); + } } if (!Recipient.self().isRegistered()) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/AccountValues.kt b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/AccountValues.kt index 55db4a89c5..a170b658c9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/AccountValues.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/AccountValues.kt @@ -16,6 +16,7 @@ import org.thoughtcrime.securesms.crypto.ProfileKeyUtil import org.thoughtcrime.securesms.crypto.storage.PreKeyMetadataStore import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.jobs.PreKeysSyncJob import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.service.KeyCachingService import org.thoughtcrime.securesms.util.Base64 @@ -46,7 +47,7 @@ internal class AccountValues internal constructor(store: KeyValueStore) : Signal private const val KEY_ACI_SIGNED_PREKEY_REGISTERED = "account.aci_signed_prekey_registered" private const val KEY_ACI_NEXT_SIGNED_PREKEY_ID = "account.aci_next_signed_prekey_id" private const val KEY_ACI_ACTIVE_SIGNED_PREKEY_ID = "account.aci_active_signed_prekey_id" - private const val KEY_ACI_SIGNED_PREKEY_FAILURE_COUNT = "account.aci_signed_prekey_failure_count" + private const val KEY_ACI_LAST_SIGNED_PREKEY_ROTATION_TIME = "account.aci_last_signed_prekey_rotation_time" private const val KEY_ACI_NEXT_ONE_TIME_PREKEY_ID = "account.aci_next_one_time_prekey_id" private const val KEY_PNI_IDENTITY_PUBLIC_KEY = "account.pni_identity_public_key" @@ -54,7 +55,7 @@ internal class AccountValues internal constructor(store: KeyValueStore) : Signal private const val KEY_PNI_SIGNED_PREKEY_REGISTERED = "account.pni_signed_prekey_registered" private const val KEY_PNI_NEXT_SIGNED_PREKEY_ID = "account.pni_next_signed_prekey_id" private const val KEY_PNI_ACTIVE_SIGNED_PREKEY_ID = "account.pni_active_signed_prekey_id" - private const val KEY_PNI_SIGNED_PREKEY_FAILURE_COUNT = "account.pni_signed_prekey_failure_count" + private const val KEY_PNI_LAST_SIGNED_PREKEY_ROTATION_TIME = "account.pni_last_signed_prekey_rotation_time" private const val KEY_PNI_NEXT_ONE_TIME_PREKEY_ID = "account.pni_next_one_time_prekey_id" @VisibleForTesting @@ -256,7 +257,7 @@ internal class AccountValues internal constructor(store: KeyValueStore) : Signal override var nextSignedPreKeyId: Int by integerValue(KEY_ACI_NEXT_SIGNED_PREKEY_ID, SecureRandom().nextInt(Medium.MAX_VALUE)) override var activeSignedPreKeyId: Int by integerValue(KEY_ACI_ACTIVE_SIGNED_PREKEY_ID, -1) override var isSignedPreKeyRegistered: Boolean by booleanValue(KEY_ACI_SIGNED_PREKEY_REGISTERED, false) - override var signedPreKeyFailureCount: Int by integerValue(KEY_ACI_SIGNED_PREKEY_FAILURE_COUNT, 0) + override var lastSignedPreKeyRotationTime: Long by longValue(KEY_ACI_LAST_SIGNED_PREKEY_ROTATION_TIME, System.currentTimeMillis() - PreKeysSyncJob.REFRESH_INTERVAL) override var nextOneTimePreKeyId: Int by integerValue(KEY_ACI_NEXT_ONE_TIME_PREKEY_ID, SecureRandom().nextInt(Medium.MAX_VALUE)) } @@ -265,7 +266,7 @@ internal class AccountValues internal constructor(store: KeyValueStore) : Signal override var nextSignedPreKeyId: Int by integerValue(KEY_PNI_NEXT_SIGNED_PREKEY_ID, SecureRandom().nextInt(Medium.MAX_VALUE)) override var activeSignedPreKeyId: Int by integerValue(KEY_PNI_ACTIVE_SIGNED_PREKEY_ID, -1) override var isSignedPreKeyRegistered: Boolean by booleanValue(KEY_PNI_SIGNED_PREKEY_REGISTERED, false) - override var signedPreKeyFailureCount: Int by integerValue(KEY_PNI_SIGNED_PREKEY_FAILURE_COUNT, 0) + override var lastSignedPreKeyRotationTime: Long by longValue(KEY_PNI_LAST_SIGNED_PREKEY_ROTATION_TIME, System.currentTimeMillis() - PreKeysSyncJob.REFRESH_INTERVAL) override var nextOneTimePreKeyId: Int by integerValue(KEY_PNI_NEXT_ONE_TIME_PREKEY_ID, SecureRandom().nextInt(Medium.MAX_VALUE)) } @@ -395,7 +396,6 @@ internal class AccountValues internal constructor(store: KeyValueStore) : Signal .putInteger(KEY_ACI_NEXT_SIGNED_PREKEY_ID, defaultPrefs.getInt("pref_next_signed_pre_key_id", SecureRandom().nextInt(Medium.MAX_VALUE))) .putInteger(KEY_ACI_ACTIVE_SIGNED_PREKEY_ID, defaultPrefs.getInt("pref_active_signed_pre_key_id", -1)) .putInteger(KEY_ACI_NEXT_ONE_TIME_PREKEY_ID, defaultPrefs.getInt("pref_next_pre_key_id", SecureRandom().nextInt(Medium.MAX_VALUE))) - .putInteger(KEY_ACI_SIGNED_PREKEY_FAILURE_COUNT, defaultPrefs.getInt("pref_signed_prekey_failure_count", 0)) .putBoolean(KEY_ACI_SIGNED_PREKEY_REGISTERED, defaultPrefs.getBoolean("pref_signed_prekey_registered", false)) .commit() diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/MiscellaneousValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/MiscellaneousValues.java index dbadde95e8..a50028fd06 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/MiscellaneousValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/MiscellaneousValues.java @@ -47,11 +47,14 @@ public final class MiscellaneousValues extends SignalStoreValues { return Collections.singletonList(SMS_PHASE_1_START_MS); } - public long getLastPrekeyRefreshTime() { + /** + * Represents the last time a _full_ prekey refreshed finished. That means signed+one-time prekeys for both ACI and PNI. + */ + public long getLastFullPrekeyRefreshTime() { return getLong(LAST_PREKEY_REFRESH_TIME, 0); } - public void setLastPrekeyRefreshTime(long time) { + public void setLastFullPrekeyRefreshTime(long time) { putLong(LAST_PREKEY_REFRESH_TIME, time); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java b/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java index 5732799960..7bf44921b2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java +++ b/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java @@ -128,9 +128,10 @@ public class ApplicationMigrations { static final int INDEX_DATABASE_MIGRATION = 84; static final int ACCOUNT_CONSISTENCY_CHECK = 85; static final int BACKUP_JITTER = 86; + static final int PREKEY_SYNC = 87; } - public static final int CURRENT_VERSION = 86; + public static final int CURRENT_VERSION = 87; /** * This *must* be called after the {@link JobManager} has been instantiated, but *before* the call @@ -576,6 +577,10 @@ public class ApplicationMigrations { jobs.put(Version.BACKUP_JITTER, new BackupJitterMigrationJob()); } + if (lastSeenVersion < Version.PREKEY_SYNC) { + jobs.put(Version.PREKEY_SYNC, new PreKeysSyncMigrationJob()); + } + return jobs; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/migrations/PreKeysSyncMigrationJob.kt b/app/src/main/java/org/thoughtcrime/securesms/migrations/PreKeysSyncMigrationJob.kt new file mode 100644 index 0000000000..37204996d3 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/migrations/PreKeysSyncMigrationJob.kt @@ -0,0 +1,36 @@ +package org.thoughtcrime.securesms.migrations + +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.jobs.PreKeysSyncJob +import org.thoughtcrime.securesms.keyvalue.SignalStore + +/** + * Schedules a prekey sync. + */ +internal class PreKeysSyncMigrationJob( + parameters: Parameters = Parameters.Builder().build() +) : MigrationJob(parameters) { + + companion object { + val TAG = Log.tag(PreKeysSyncMigrationJob::class.java) + const val KEY = "PreKeysSyncIndexMigrationJob" + } + + override fun getFactoryKey(): String = KEY + + override fun isUiBlocking(): Boolean = false + + override fun performMigration() { + SignalStore.misc().lastFullPrekeyRefreshTime = 0 + PreKeysSyncJob.enqueue() + } + + override fun shouldRetry(e: Exception): Boolean = false + + class Factory : Job.Factory { + override fun create(parameters: Parameters, serializedData: ByteArray?): PreKeysSyncMigrationJob { + return PreKeysSyncMigrationJob(parameters) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/RotateSignedPreKeyListener.java b/app/src/main/java/org/thoughtcrime/securesms/service/RotateSignedPreKeyListener.java index ec6e394ab6..c308a432c2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/RotateSignedPreKeyListener.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/RotateSignedPreKeyListener.java @@ -13,8 +13,6 @@ import java.util.concurrent.TimeUnit; public class RotateSignedPreKeyListener extends PersistentAlarmManagerListener { - private static final long INTERVAL = TimeUnit.DAYS.toMillis(2); - @Override protected long getNextScheduledExecutionTime(Context context) { return TextSecurePreferences.getSignedPreKeyRotationTime(context); @@ -23,10 +21,10 @@ public class RotateSignedPreKeyListener extends PersistentAlarmManagerListener { @Override protected long onAlarm(Context context, long scheduledTime) { if (scheduledTime != 0 && SignalStore.account().isRegistered()) { - PreKeysSyncJob.enqueue(true); + PreKeysSyncJob.enqueue(); } - long nextTime = System.currentTimeMillis() + INTERVAL; + long nextTime = System.currentTimeMillis() + PreKeysSyncJob.REFRESH_INTERVAL; TextSecurePreferences.setSignedPreKeyRotationTime(context, nextTime); return nextTime;