kopia lustrzana https://github.com/ryukoposting/Signal-Android
Replace prekey jobs with one overall sync job.
rodzic
2740b5e300
commit
3252871ed5
|
@ -0,0 +1,212 @@
|
||||||
|
package org.thoughtcrime.securesms.jobs
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import okhttp3.mockwebserver.MockResponse
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.signal.libsignal.protocol.ecc.Curve
|
||||||
|
import org.thoughtcrime.securesms.crypto.storage.PreKeyMetadataStore
|
||||||
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||||
|
import org.thoughtcrime.securesms.dependencies.InstrumentationApplicationDependencyProvider
|
||||||
|
import org.thoughtcrime.securesms.jobmanager.Job
|
||||||
|
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||||
|
import org.thoughtcrime.securesms.testing.Get
|
||||||
|
import org.thoughtcrime.securesms.testing.Put
|
||||||
|
import org.thoughtcrime.securesms.testing.SignalActivityRule
|
||||||
|
import org.thoughtcrime.securesms.testing.assertIs
|
||||||
|
import org.thoughtcrime.securesms.testing.assertIsNot
|
||||||
|
import org.thoughtcrime.securesms.testing.parsedRequestBody
|
||||||
|
import org.thoughtcrime.securesms.testing.success
|
||||||
|
import org.whispersystems.signalservice.api.push.SignedPreKeyEntity
|
||||||
|
import org.whispersystems.signalservice.internal.push.PreKeyState
|
||||||
|
import org.whispersystems.signalservice.internal.push.PreKeyStatus
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class PreKeysSyncJobTest {
|
||||||
|
|
||||||
|
@get:Rule
|
||||||
|
val harness = SignalActivityRule()
|
||||||
|
|
||||||
|
private val aciPreKeyMeta: PreKeyMetadataStore
|
||||||
|
get() = SignalStore.account().aciPreKeys
|
||||||
|
|
||||||
|
private val pniPreKeyMeta: PreKeyMetadataStore
|
||||||
|
get() = SignalStore.account().pniPreKeys
|
||||||
|
|
||||||
|
private lateinit var job: PreKeysSyncJob
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
job = PreKeysSyncJob()
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
InstrumentationApplicationDependencyProvider.clearHandlers()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create signed prekeys for both identities when both do not have registered prekeys according
|
||||||
|
* to our local state.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun runWithoutRegisteredKeysForBothIdentities() {
|
||||||
|
// GIVEN
|
||||||
|
aciPreKeyMeta.isSignedPreKeyRegistered = false
|
||||||
|
pniPreKeyMeta.isSignedPreKeyRegistered = false
|
||||||
|
|
||||||
|
lateinit var aciSignedPreKey: SignedPreKeyEntity
|
||||||
|
lateinit var pniSignedPreKey: SignedPreKeyEntity
|
||||||
|
|
||||||
|
InstrumentationApplicationDependencyProvider.addMockWebRequestHandlers(
|
||||||
|
Put("/v2/keys/signed?identity=aci") { r ->
|
||||||
|
aciSignedPreKey = r.parsedRequestBody()
|
||||||
|
MockResponse().success()
|
||||||
|
},
|
||||||
|
Put("/v2/keys/signed?identity=pni") { r ->
|
||||||
|
pniSignedPreKey = r.parsedRequestBody()
|
||||||
|
MockResponse().success()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
val result: Job.Result = job.run()
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
result.isSuccess assertIs true
|
||||||
|
|
||||||
|
aciPreKeyMeta.isSignedPreKeyRegistered assertIs true
|
||||||
|
pniPreKeyMeta.isSignedPreKeyRegistered assertIs true
|
||||||
|
|
||||||
|
val aciVerifySignatureResult = Curve.verifySignature(
|
||||||
|
ApplicationDependencies.getProtocolStore().aci().identityKeyPair.publicKey.publicKey,
|
||||||
|
aciSignedPreKey.publicKey.serialize(),
|
||||||
|
aciSignedPreKey.signature
|
||||||
|
)
|
||||||
|
aciVerifySignatureResult assertIs true
|
||||||
|
|
||||||
|
val pniVerifySignatureResult = Curve.verifySignature(
|
||||||
|
ApplicationDependencies.getProtocolStore().pni().identityKeyPair.publicKey.publicKey,
|
||||||
|
pniSignedPreKey.publicKey.serialize(),
|
||||||
|
pniSignedPreKey.signature
|
||||||
|
)
|
||||||
|
pniVerifySignatureResult assertIs true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* With 100 prekeys registered for each identity, do nothing.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun runWithRegisteredKeysForBothIdentities() {
|
||||||
|
// GIVEN
|
||||||
|
val currentAciKeyId = aciPreKeyMeta.activeSignedPreKeyId
|
||||||
|
val currentPniKeyId = pniPreKeyMeta.activeSignedPreKeyId
|
||||||
|
|
||||||
|
InstrumentationApplicationDependencyProvider.addMockWebRequestHandlers(
|
||||||
|
Get("/v2/keys?identity=aci") { MockResponse().success(PreKeyStatus(100)) },
|
||||||
|
Get("/v2/keys?identity=pni") { MockResponse().success(PreKeyStatus(100)) },
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
val result: Job.Result = job.run()
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
result.isSuccess assertIs true
|
||||||
|
|
||||||
|
aciPreKeyMeta.activeSignedPreKeyId assertIs currentAciKeyId
|
||||||
|
pniPreKeyMeta.activeSignedPreKeyId assertIs currentPniKeyId
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* With 100 prekeys registered for ACI, but no PNI prekeys registered according to local state,
|
||||||
|
* do nothing for ACI but create PNI prekeys and update local state.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun runWithRegisteredKeysForAciIdentityOnly() {
|
||||||
|
// GIVEN
|
||||||
|
pniPreKeyMeta.isSignedPreKeyRegistered = false
|
||||||
|
|
||||||
|
val currentAciKeyId = aciPreKeyMeta.activeSignedPreKeyId
|
||||||
|
val currentPniKeyId = pniPreKeyMeta.activeSignedPreKeyId
|
||||||
|
|
||||||
|
InstrumentationApplicationDependencyProvider.addMockWebRequestHandlers(
|
||||||
|
Get("/v2/keys?identity=aci") { MockResponse().success(PreKeyStatus(100)) },
|
||||||
|
Put("/v2/keys/signed?identity=pni") { MockResponse().success() },
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
val result: Job.Result = job.run()
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
result.isSuccess assertIs true
|
||||||
|
|
||||||
|
pniPreKeyMeta.isSignedPreKeyRegistered assertIs true
|
||||||
|
aciPreKeyMeta.activeSignedPreKeyId assertIs currentAciKeyId
|
||||||
|
pniPreKeyMeta.activeSignedPreKeyId assertIsNot currentPniKeyId
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* With <10 prekeys registered for each identity, upload new.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun runWithLowNumberOfRegisteredKeysForBothIdentities() {
|
||||||
|
// GIVEN
|
||||||
|
val currentAciKeyId = aciPreKeyMeta.activeSignedPreKeyId
|
||||||
|
val currentPniKeyId = pniPreKeyMeta.activeSignedPreKeyId
|
||||||
|
|
||||||
|
val currentNextAciPreKeyId = aciPreKeyMeta.nextOneTimePreKeyId
|
||||||
|
val currentNextPniPreKeyId = pniPreKeyMeta.nextOneTimePreKeyId
|
||||||
|
|
||||||
|
lateinit var aciPreKeyStateRequest: PreKeyState
|
||||||
|
lateinit var pniPreKeyStateRequest: PreKeyState
|
||||||
|
|
||||||
|
InstrumentationApplicationDependencyProvider.addMockWebRequestHandlers(
|
||||||
|
Get("/v2/keys?identity=aci") { MockResponse().success(PreKeyStatus(5)) },
|
||||||
|
Get("/v2/keys?identity=pni") { MockResponse().success(PreKeyStatus(5)) },
|
||||||
|
Put("/v2/keys/?identity=aci") { r ->
|
||||||
|
aciPreKeyStateRequest = r.parsedRequestBody()
|
||||||
|
MockResponse().success()
|
||||||
|
},
|
||||||
|
Put("/v2/keys/?identity=pni") { r ->
|
||||||
|
pniPreKeyStateRequest = r.parsedRequestBody()
|
||||||
|
MockResponse().success()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
val result: Job.Result = job.run()
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
result.isSuccess assertIs true
|
||||||
|
aciPreKeyMeta.activeSignedPreKeyId assertIsNot currentAciKeyId
|
||||||
|
pniPreKeyMeta.activeSignedPreKeyId assertIsNot currentPniKeyId
|
||||||
|
|
||||||
|
aciPreKeyMeta.nextOneTimePreKeyId assertIsNot currentNextAciPreKeyId
|
||||||
|
pniPreKeyMeta.nextOneTimePreKeyId assertIsNot currentNextPniPreKeyId
|
||||||
|
|
||||||
|
ApplicationDependencies.getProtocolStore().aci().identityKeyPair.publicKey.let { aciIdentityKey ->
|
||||||
|
aciPreKeyStateRequest.identityKey assertIs aciIdentityKey
|
||||||
|
|
||||||
|
val verifySignatureResult = Curve.verifySignature(
|
||||||
|
aciIdentityKey.publicKey,
|
||||||
|
aciPreKeyStateRequest.signedPreKey.publicKey.serialize(),
|
||||||
|
aciPreKeyStateRequest.signedPreKey.signature
|
||||||
|
)
|
||||||
|
verifySignatureResult assertIs true
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplicationDependencies.getProtocolStore().pni().identityKeyPair.publicKey.let { pniIdentityKey ->
|
||||||
|
pniPreKeyStateRequest.identityKey assertIs pniIdentityKey
|
||||||
|
|
||||||
|
val verifySignatureResult = Curve.verifySignature(
|
||||||
|
pniIdentityKey.publicKey,
|
||||||
|
pniPreKeyStateRequest.signedPreKey.publicKey.serialize(),
|
||||||
|
pniPreKeyStateRequest.signedPreKey.signature
|
||||||
|
)
|
||||||
|
verifySignatureResult assertIs true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -50,16 +50,15 @@ import org.thoughtcrime.securesms.emoji.EmojiSource;
|
||||||
import org.thoughtcrime.securesms.emoji.JumboEmoji;
|
import org.thoughtcrime.securesms.emoji.JumboEmoji;
|
||||||
import org.thoughtcrime.securesms.gcm.FcmJobService;
|
import org.thoughtcrime.securesms.gcm.FcmJobService;
|
||||||
import org.thoughtcrime.securesms.jobs.CheckServiceReachabilityJob;
|
import org.thoughtcrime.securesms.jobs.CheckServiceReachabilityJob;
|
||||||
import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.DownloadLatestEmojiDataJob;
|
import org.thoughtcrime.securesms.jobs.DownloadLatestEmojiDataJob;
|
||||||
import org.thoughtcrime.securesms.jobs.EmojiSearchIndexDownloadJob;
|
import org.thoughtcrime.securesms.jobs.EmojiSearchIndexDownloadJob;
|
||||||
import org.thoughtcrime.securesms.jobs.FcmRefreshJob;
|
import org.thoughtcrime.securesms.jobs.FcmRefreshJob;
|
||||||
import org.thoughtcrime.securesms.jobs.FontDownloaderJob;
|
import org.thoughtcrime.securesms.jobs.FontDownloaderJob;
|
||||||
import org.thoughtcrime.securesms.jobs.GroupV2UpdateSelfProfileKeyJob;
|
import org.thoughtcrime.securesms.jobs.GroupV2UpdateSelfProfileKeyJob;
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
|
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
|
||||||
|
import org.thoughtcrime.securesms.jobs.PreKeysSyncJob;
|
||||||
import org.thoughtcrime.securesms.jobs.ProfileUploadJob;
|
import org.thoughtcrime.securesms.jobs.ProfileUploadJob;
|
||||||
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
|
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
|
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RetrieveRemoteAnnouncementsJob;
|
import org.thoughtcrime.securesms.jobs.RetrieveRemoteAnnouncementsJob;
|
||||||
import org.thoughtcrime.securesms.jobs.StoryOnboardingDownloadJob;
|
import org.thoughtcrime.securesms.jobs.StoryOnboardingDownloadJob;
|
||||||
|
@ -180,13 +179,12 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
|
||||||
.addNonBlocking(this::initializeRevealableMessageManager)
|
.addNonBlocking(this::initializeRevealableMessageManager)
|
||||||
.addNonBlocking(this::initializePendingRetryReceiptManager)
|
.addNonBlocking(this::initializePendingRetryReceiptManager)
|
||||||
.addNonBlocking(this::initializeFcmCheck)
|
.addNonBlocking(this::initializeFcmCheck)
|
||||||
.addNonBlocking(CreateSignedPreKeyJob::enqueueIfNeeded)
|
.addNonBlocking(PreKeysSyncJob::enqueueIfNeeded)
|
||||||
.addNonBlocking(this::initializePeriodicTasks)
|
.addNonBlocking(this::initializePeriodicTasks)
|
||||||
.addNonBlocking(this::initializeCircumvention)
|
.addNonBlocking(this::initializeCircumvention)
|
||||||
.addNonBlocking(this::initializePendingMessages)
|
.addNonBlocking(this::initializePendingMessages)
|
||||||
.addNonBlocking(this::initializeCleanup)
|
.addNonBlocking(this::initializeCleanup)
|
||||||
.addNonBlocking(this::initializeGlideCodecs)
|
.addNonBlocking(this::initializeGlideCodecs)
|
||||||
.addNonBlocking(RefreshPreKeysJob::scheduleIfNecessary)
|
|
||||||
.addNonBlocking(StorageSyncHelper::scheduleRoutineSync)
|
.addNonBlocking(StorageSyncHelper::scheduleRoutineSync)
|
||||||
.addNonBlocking(() -> ApplicationDependencies.getJobManager().beginJobLoop())
|
.addNonBlocking(() -> ApplicationDependencies.getJobManager().beginJobLoop())
|
||||||
.addNonBlocking(EmojiSource::refresh)
|
.addNonBlocking(EmojiSource::refresh)
|
||||||
|
|
|
@ -217,7 +217,7 @@ class ChangeNumberRepository(private val accountManager: SignalServiceAccountMan
|
||||||
|
|
||||||
// Signed Prekeys
|
// Signed Prekeys
|
||||||
val signedPreKeyRecord = if (deviceId == primaryDeviceId) {
|
val signedPreKeyRecord = if (deviceId == primaryDeviceId) {
|
||||||
PreKeyUtil.generateAndStoreSignedPreKey(pniProtocolStore, pniMetadataStore, pniIdentity.privateKey, false)
|
PreKeyUtil.generateAndStoreSignedPreKey(pniProtocolStore, pniMetadataStore, pniIdentity.privateKey)
|
||||||
} else {
|
} else {
|
||||||
PreKeyUtil.generateSignedPreKey(SecureRandom().nextInt(Medium.MAX_VALUE), pniIdentity.privateKey)
|
PreKeyUtil.generateSignedPreKey(SecureRandom().nextInt(Medium.MAX_VALUE), pniIdentity.privateKey)
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,14 +64,13 @@ public class PreKeyUtil {
|
||||||
return records;
|
return records;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized static @NonNull SignedPreKeyRecord generateAndStoreSignedPreKey(@NonNull SignalProtocolStore protocolStore, @NonNull PreKeyMetadataStore metadataStore, boolean setAsActive) {
|
public synchronized static @NonNull SignedPreKeyRecord generateAndStoreSignedPreKey(@NonNull SignalProtocolStore protocolStore, @NonNull PreKeyMetadataStore metadataStore) {
|
||||||
return generateAndStoreSignedPreKey(protocolStore, metadataStore, protocolStore.getIdentityKeyPair().getPrivateKey(), setAsActive);
|
return generateAndStoreSignedPreKey(protocolStore, metadataStore, protocolStore.getIdentityKeyPair().getPrivateKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized static @NonNull SignedPreKeyRecord generateAndStoreSignedPreKey(@NonNull SignalProtocolStore protocolStore,
|
public synchronized static @NonNull SignedPreKeyRecord generateAndStoreSignedPreKey(@NonNull SignalProtocolStore protocolStore,
|
||||||
@NonNull PreKeyMetadataStore metadataStore,
|
@NonNull PreKeyMetadataStore metadataStore,
|
||||||
@NonNull ECPrivateKey privateKey,
|
@NonNull ECPrivateKey privateKey)
|
||||||
boolean setAsActive)
|
|
||||||
{
|
{
|
||||||
Log.i(TAG, "Generating signed prekeys...");
|
Log.i(TAG, "Generating signed prekeys...");
|
||||||
|
|
||||||
|
@ -81,10 +80,6 @@ public class PreKeyUtil {
|
||||||
protocolStore.storeSignedPreKey(signedPreKeyId, record);
|
protocolStore.storeSignedPreKey(signedPreKeyId, record);
|
||||||
metadataStore.setNextSignedPreKeyId((signedPreKeyId + 1) % Medium.MAX_VALUE);
|
metadataStore.setNextSignedPreKeyId((signedPreKeyId + 1) % Medium.MAX_VALUE);
|
||||||
|
|
||||||
if (setAsActive) {
|
|
||||||
metadataStore.setActiveSignedPreKeyId(signedPreKeyId);
|
|
||||||
}
|
|
||||||
|
|
||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,7 @@ import org.thoughtcrime.securesms.database.helpers.SignalDatabaseMigrations
|
||||||
import org.thoughtcrime.securesms.database.helpers.SignalDatabaseMigrations.migrate
|
import org.thoughtcrime.securesms.database.helpers.SignalDatabaseMigrations.migrate
|
||||||
import org.thoughtcrime.securesms.database.helpers.SignalDatabaseMigrations.migratePostTransaction
|
import org.thoughtcrime.securesms.database.helpers.SignalDatabaseMigrations.migratePostTransaction
|
||||||
import org.thoughtcrime.securesms.database.model.AvatarPickerDatabase
|
import org.thoughtcrime.securesms.database.model.AvatarPickerDatabase
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
import org.thoughtcrime.securesms.jobs.PreKeysSyncJob
|
||||||
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob
|
|
||||||
import org.thoughtcrime.securesms.migrations.LegacyMigrationJob
|
import org.thoughtcrime.securesms.migrations.LegacyMigrationJob
|
||||||
import org.thoughtcrime.securesms.migrations.LegacyMigrationJob.DatabaseUpgradeListener
|
import org.thoughtcrime.securesms.migrations.LegacyMigrationJob.DatabaseUpgradeListener
|
||||||
import org.thoughtcrime.securesms.service.KeyCachingService
|
import org.thoughtcrime.securesms.service.KeyCachingService
|
||||||
|
@ -145,7 +144,7 @@ open class SignalDatabase(private val context: Application, databaseSecret: Data
|
||||||
val masterSecret = KeyCachingService.getMasterSecret(context)
|
val masterSecret = KeyCachingService.getMasterSecret(context)
|
||||||
if (masterSecret != null) SQLCipherMigrationHelper.migrateCiphertext(context, masterSecret, legacyDb, db, null) else TextSecurePreferences.setNeedsSqlCipherMigration(context, true)
|
if (masterSecret != null) SQLCipherMigrationHelper.migrateCiphertext(context, masterSecret, legacyDb, db, null) else TextSecurePreferences.setNeedsSqlCipherMigration(context, true)
|
||||||
if (!PreKeyMigrationHelper.migratePreKeys(context, db)) {
|
if (!PreKeyMigrationHelper.migratePreKeys(context, db)) {
|
||||||
ApplicationDependencies.getJobManager().add(RefreshPreKeysJob())
|
PreKeysSyncJob.enqueue()
|
||||||
}
|
}
|
||||||
SessionStoreMigrationHelper.migrateSessions(context, db)
|
SessionStoreMigrationHelper.migrateSessions(context, db)
|
||||||
PreKeyMigrationHelper.cleanUpPreKeys(context)
|
PreKeyMigrationHelper.cleanUpPreKeys(context)
|
||||||
|
|
|
@ -30,9 +30,8 @@ import org.thoughtcrime.securesms.database.RecipientDatabase
|
||||||
import org.thoughtcrime.securesms.database.helpers.migration.MyStoryMigration
|
import org.thoughtcrime.securesms.database.helpers.migration.MyStoryMigration
|
||||||
import org.thoughtcrime.securesms.database.helpers.migration.UrgentMslFlagMigration
|
import org.thoughtcrime.securesms.database.helpers.migration.UrgentMslFlagMigration
|
||||||
import org.thoughtcrime.securesms.database.model.databaseprotos.ReactionList
|
import org.thoughtcrime.securesms.database.model.databaseprotos.ReactionList
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
|
||||||
import org.thoughtcrime.securesms.groups.GroupId
|
import org.thoughtcrime.securesms.groups.GroupId
|
||||||
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob
|
import org.thoughtcrime.securesms.jobs.PreKeysSyncJob
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||||
import org.thoughtcrime.securesms.notifications.NotificationChannels
|
import org.thoughtcrime.securesms.notifications.NotificationChannels
|
||||||
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter
|
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter
|
||||||
|
@ -224,7 +223,7 @@ object SignalDatabaseMigrations {
|
||||||
db.execSQL("CREATE TABLE one_time_prekeys (_id INTEGER PRIMARY KEY, key_id INTEGER UNIQUE, public_key TEXT NOT NULL, private_key TEXT NOT NULL)")
|
db.execSQL("CREATE TABLE one_time_prekeys (_id INTEGER PRIMARY KEY, key_id INTEGER UNIQUE, public_key TEXT NOT NULL, private_key TEXT NOT NULL)")
|
||||||
|
|
||||||
if (!PreKeyMigrationHelper.migratePreKeys(context, db)) {
|
if (!PreKeyMigrationHelper.migratePreKeys(context, db)) {
|
||||||
ApplicationDependencies.getJobManager().add(RefreshPreKeysJob())
|
PreKeysSyncJob.enqueue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,11 +31,11 @@ import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||||
import org.thoughtcrime.securesms.jobmanager.JobMigrator;
|
import org.thoughtcrime.securesms.jobmanager.JobMigrator;
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.FactoryJobPredicate;
|
import org.thoughtcrime.securesms.jobmanager.impl.FactoryJobPredicate;
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.JsonDataSerializer;
|
import org.thoughtcrime.securesms.jobmanager.impl.JsonDataSerializer;
|
||||||
import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.FastJobStorage;
|
import org.thoughtcrime.securesms.jobs.FastJobStorage;
|
||||||
import org.thoughtcrime.securesms.jobs.GroupCallUpdateSendJob;
|
import org.thoughtcrime.securesms.jobs.GroupCallUpdateSendJob;
|
||||||
import org.thoughtcrime.securesms.jobs.JobManagerFactories;
|
import org.thoughtcrime.securesms.jobs.JobManagerFactories;
|
||||||
import org.thoughtcrime.securesms.jobs.MarkerJob;
|
import org.thoughtcrime.securesms.jobs.MarkerJob;
|
||||||
|
import org.thoughtcrime.securesms.jobs.PreKeysSyncJob;
|
||||||
import org.thoughtcrime.securesms.jobs.PushDecryptMessageJob;
|
import org.thoughtcrime.securesms.jobs.PushDecryptMessageJob;
|
||||||
import org.thoughtcrime.securesms.jobs.PushGroupSendJob;
|
import org.thoughtcrime.securesms.jobs.PushGroupSendJob;
|
||||||
import org.thoughtcrime.securesms.jobs.PushMediaSendJob;
|
import org.thoughtcrime.securesms.jobs.PushMediaSendJob;
|
||||||
|
@ -317,7 +317,7 @@ public class ApplicationDependencyProvider implements ApplicationDependencies.Pr
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsPreKeyJob) {
|
if (needsPreKeyJob) {
|
||||||
CreateSignedPreKeyJob.enqueueIfNeeded();
|
PreKeysSyncJob.enqueueIfNeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
SignalBaseIdentityKeyStore baseIdentityStore = new SignalBaseIdentityKeyStore(context);
|
SignalBaseIdentityKeyStore baseIdentityStore = new SignalBaseIdentityKeyStore(context);
|
||||||
|
|
|
@ -4,6 +4,7 @@ import android.content.Context;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
import androidx.annotation.WorkerThread;
|
import androidx.annotation.WorkerThread;
|
||||||
|
|
||||||
import org.signal.core.util.logging.Log;
|
import org.signal.core.util.logging.Log;
|
||||||
|
@ -14,6 +15,8 @@ import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static androidx.annotation.VisibleForTesting.PACKAGE_PRIVATE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A durable unit of work.
|
* A durable unit of work.
|
||||||
*
|
*
|
||||||
|
@ -193,15 +196,18 @@ public abstract class Job {
|
||||||
return new Result(ResultType.FAILURE, runtimeException, null, INVALID_BACKOFF);
|
return new Result(ResultType.FAILURE, runtimeException, null, INVALID_BACKOFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isSuccess() {
|
@VisibleForTesting(otherwise = PACKAGE_PRIVATE)
|
||||||
|
public boolean isSuccess() {
|
||||||
return resultType == ResultType.SUCCESS;
|
return resultType == ResultType.SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isRetry() {
|
@VisibleForTesting(otherwise = PACKAGE_PRIVATE)
|
||||||
|
public boolean isRetry() {
|
||||||
return resultType == ResultType.RETRY;
|
return resultType == ResultType.RETRY;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isFailure() {
|
@VisibleForTesting(otherwise = PACKAGE_PRIVATE)
|
||||||
|
public boolean isFailure() {
|
||||||
return resultType == ResultType.FAILURE;
|
return resultType == ResultType.FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -92,8 +92,8 @@ public abstract class BaseJob extends Job {
|
||||||
warn(tag, "", message, null);
|
warn(tag, "", message, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void warn(@NonNull String tag, @NonNull String event, @NonNull String message) {
|
protected void warn(@NonNull String tag, @NonNull Object extra, @NonNull String message) {
|
||||||
warn(tag, event, message, null);
|
warn(tag, extra.toString(), message, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void warn(@NonNull String tag, @Nullable Throwable t) {
|
protected void warn(@NonNull String tag, @Nullable Throwable t) {
|
||||||
|
|
|
@ -1,113 +0,0 @@
|
||||||
package org.thoughtcrime.securesms.jobs;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
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.Data;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
|
||||||
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
|
||||||
import org.whispersystems.signalservice.api.push.ServiceId;
|
|
||||||
import org.whispersystems.signalservice.api.push.ServiceIdType;
|
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates and uploads a new signed prekey for an identity if one hasn't been uploaded yet.
|
|
||||||
*/
|
|
||||||
public class CreateSignedPreKeyJob extends BaseJob {
|
|
||||||
|
|
||||||
public static final String KEY = "CreateSignedPreKeyJob";
|
|
||||||
|
|
||||||
private static final String TAG = Log.tag(CreateSignedPreKeyJob.class);
|
|
||||||
|
|
||||||
private CreateSignedPreKeyJob() {
|
|
||||||
this(new Job.Parameters.Builder()
|
|
||||||
.addConstraint(NetworkConstraint.KEY)
|
|
||||||
.setMaxInstancesForFactory(1)
|
|
||||||
.setQueue("CreateSignedPreKeyJob")
|
|
||||||
.setLifespan(TimeUnit.DAYS.toMillis(30))
|
|
||||||
.setMaxAttempts(Parameters.UNLIMITED)
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
private CreateSignedPreKeyJob(@NonNull Job.Parameters parameters) {
|
|
||||||
super(parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enqueues an instance of this job if we not yet created + uploaded signed prekeys for one of our identities.
|
|
||||||
*/
|
|
||||||
public static void enqueueIfNeeded() {
|
|
||||||
if (!SignalStore.account().aciPreKeys().isSignedPreKeyRegistered() || !SignalStore.account().pniPreKeys().isSignedPreKeyRegistered()) {
|
|
||||||
Log.i(TAG, "Some signed prekeys aren't registered yet. Enqueuing a job.");
|
|
||||||
ApplicationDependencies.getJobManager().add(new CreateSignedPreKeyJob());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull Data serialize() {
|
|
||||||
return Data.EMPTY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull String getFactoryKey() {
|
|
||||||
return KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRun() throws IOException {
|
|
||||||
if (!SignalStore.account().isRegistered() || SignalStore.account().getAci() == null || SignalStore.account().getPni() == null) {
|
|
||||||
Log.w(TAG, "Not yet registered...");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
createPreKeys(ServiceIdType.ACI, SignalStore.account().getAci(), ApplicationDependencies.getProtocolStore().aci(), SignalStore.account().aciPreKeys());
|
|
||||||
createPreKeys(ServiceIdType.PNI, SignalStore.account().getPni(), ApplicationDependencies.getProtocolStore().pni(), SignalStore.account().pniPreKeys());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createPreKeys(@NonNull ServiceIdType serviceIdType, @Nullable ServiceId serviceId, @NonNull SignalProtocolStore protocolStore, @NonNull PreKeyMetadataStore metadataStore)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
if (serviceId == null) {
|
|
||||||
warn(TAG, "AccountId not set!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (metadataStore.isSignedPreKeyRegistered()) {
|
|
||||||
warn(TAG, "Signed prekey for " + serviceIdType + " already registered...");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
|
|
||||||
SignedPreKeyRecord signedPreKeyRecord = PreKeyUtil.generateAndStoreSignedPreKey(protocolStore, metadataStore, true);
|
|
||||||
|
|
||||||
accountManager.setSignedPreKey(serviceIdType, signedPreKeyRecord);
|
|
||||||
metadataStore.setSignedPreKeyRegistered(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure() {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onShouldRetry(@NonNull Exception exception) {
|
|
||||||
if (exception instanceof PushNetworkException) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class Factory implements Job.Factory<CreateSignedPreKeyJob> {
|
|
||||||
@Override
|
|
||||||
public @NonNull CreateSignedPreKeyJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
|
||||||
return new CreateSignedPreKeyJob(parameters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -46,7 +46,6 @@ import org.thoughtcrime.securesms.migrations.EmojiDownloadMigrationJob;
|
||||||
import org.thoughtcrime.securesms.migrations.KbsEnclaveMigrationJob;
|
import org.thoughtcrime.securesms.migrations.KbsEnclaveMigrationJob;
|
||||||
import org.thoughtcrime.securesms.migrations.LegacyMigrationJob;
|
import org.thoughtcrime.securesms.migrations.LegacyMigrationJob;
|
||||||
import org.thoughtcrime.securesms.migrations.MigrationCompleteJob;
|
import org.thoughtcrime.securesms.migrations.MigrationCompleteJob;
|
||||||
import org.thoughtcrime.securesms.migrations.SyncDistributionListsMigrationJob;
|
|
||||||
import org.thoughtcrime.securesms.migrations.PassingMigrationJob;
|
import org.thoughtcrime.securesms.migrations.PassingMigrationJob;
|
||||||
import org.thoughtcrime.securesms.migrations.PinOptOutMigration;
|
import org.thoughtcrime.securesms.migrations.PinOptOutMigration;
|
||||||
import org.thoughtcrime.securesms.migrations.PinReminderMigrationJob;
|
import org.thoughtcrime.securesms.migrations.PinReminderMigrationJob;
|
||||||
|
@ -62,6 +61,7 @@ import org.thoughtcrime.securesms.migrations.StickerLaunchMigrationJob;
|
||||||
import org.thoughtcrime.securesms.migrations.StickerMyDailyLifeMigrationJob;
|
import org.thoughtcrime.securesms.migrations.StickerMyDailyLifeMigrationJob;
|
||||||
import org.thoughtcrime.securesms.migrations.StorageCapabilityMigrationJob;
|
import org.thoughtcrime.securesms.migrations.StorageCapabilityMigrationJob;
|
||||||
import org.thoughtcrime.securesms.migrations.StorageServiceMigrationJob;
|
import org.thoughtcrime.securesms.migrations.StorageServiceMigrationJob;
|
||||||
|
import org.thoughtcrime.securesms.migrations.SyncDistributionListsMigrationJob;
|
||||||
import org.thoughtcrime.securesms.migrations.TrimByLengthSettingsMigrationJob;
|
import org.thoughtcrime.securesms.migrations.TrimByLengthSettingsMigrationJob;
|
||||||
import org.thoughtcrime.securesms.migrations.UserNotificationMigrationJob;
|
import org.thoughtcrime.securesms.migrations.UserNotificationMigrationJob;
|
||||||
import org.thoughtcrime.securesms.migrations.UuidMigrationJob;
|
import org.thoughtcrime.securesms.migrations.UuidMigrationJob;
|
||||||
|
@ -89,7 +89,6 @@ public final class JobManagerFactories {
|
||||||
put(ClearFallbackKbsEnclaveJob.KEY, new ClearFallbackKbsEnclaveJob.Factory());
|
put(ClearFallbackKbsEnclaveJob.KEY, new ClearFallbackKbsEnclaveJob.Factory());
|
||||||
put(ConversationShortcutUpdateJob.KEY, new ConversationShortcutUpdateJob.Factory());
|
put(ConversationShortcutUpdateJob.KEY, new ConversationShortcutUpdateJob.Factory());
|
||||||
put(CreateReleaseChannelJob.KEY, new CreateReleaseChannelJob.Factory());
|
put(CreateReleaseChannelJob.KEY, new CreateReleaseChannelJob.Factory());
|
||||||
put(CreateSignedPreKeyJob.KEY, new CreateSignedPreKeyJob.Factory());
|
|
||||||
put(DirectoryRefreshJob.KEY, new DirectoryRefreshJob.Factory());
|
put(DirectoryRefreshJob.KEY, new DirectoryRefreshJob.Factory());
|
||||||
put(DonationReceiptRedemptionJob.KEY, new DonationReceiptRedemptionJob.Factory());
|
put(DonationReceiptRedemptionJob.KEY, new DonationReceiptRedemptionJob.Factory());
|
||||||
put(DownloadLatestEmojiDataJob.KEY, new DownloadLatestEmojiDataJob.Factory());
|
put(DownloadLatestEmojiDataJob.KEY, new DownloadLatestEmojiDataJob.Factory());
|
||||||
|
@ -136,6 +135,7 @@ public final class JobManagerFactories {
|
||||||
put(PaymentNotificationSendJob.KEY, new PaymentNotificationSendJob.Factory());
|
put(PaymentNotificationSendJob.KEY, new PaymentNotificationSendJob.Factory());
|
||||||
put(PaymentSendJob.KEY, new PaymentSendJob.Factory());
|
put(PaymentSendJob.KEY, new PaymentSendJob.Factory());
|
||||||
put(PaymentTransactionCheckJob.KEY, new PaymentTransactionCheckJob.Factory());
|
put(PaymentTransactionCheckJob.KEY, new PaymentTransactionCheckJob.Factory());
|
||||||
|
put(PreKeysSyncJob.KEY, new PreKeysSyncJob.Factory());
|
||||||
put(ProfileKeySendJob.KEY, new ProfileKeySendJob.Factory());
|
put(ProfileKeySendJob.KEY, new ProfileKeySendJob.Factory());
|
||||||
put(ProfileUploadJob.KEY, new ProfileUploadJob.Factory());
|
put(ProfileUploadJob.KEY, new ProfileUploadJob.Factory());
|
||||||
put(PushDecryptMessageJob.KEY, new PushDecryptMessageJob.Factory());
|
put(PushDecryptMessageJob.KEY, new PushDecryptMessageJob.Factory());
|
||||||
|
@ -152,7 +152,6 @@ public final class JobManagerFactories {
|
||||||
put(RecipientChangedNumberJob.KEY, new RecipientChangedNumberJob.Factory());
|
put(RecipientChangedNumberJob.KEY, new RecipientChangedNumberJob.Factory());
|
||||||
put(RefreshAttributesJob.KEY, new RefreshAttributesJob.Factory());
|
put(RefreshAttributesJob.KEY, new RefreshAttributesJob.Factory());
|
||||||
put(RefreshOwnProfileJob.KEY, new RefreshOwnProfileJob.Factory());
|
put(RefreshOwnProfileJob.KEY, new RefreshOwnProfileJob.Factory());
|
||||||
put(RefreshPreKeysJob.KEY, new RefreshPreKeysJob.Factory());
|
|
||||||
put(RemoteConfigRefreshJob.KEY, new RemoteConfigRefreshJob.Factory());
|
put(RemoteConfigRefreshJob.KEY, new RemoteConfigRefreshJob.Factory());
|
||||||
put(RemoteDeleteSendJob.KEY, new RemoteDeleteSendJob.Factory());
|
put(RemoteDeleteSendJob.KEY, new RemoteDeleteSendJob.Factory());
|
||||||
put(ReportSpamJob.KEY, new ReportSpamJob.Factory());
|
put(ReportSpamJob.KEY, new ReportSpamJob.Factory());
|
||||||
|
@ -165,7 +164,6 @@ public final class JobManagerFactories {
|
||||||
put(RetrieveRemoteAnnouncementsJob.KEY, new RetrieveRemoteAnnouncementsJob.Factory());
|
put(RetrieveRemoteAnnouncementsJob.KEY, new RetrieveRemoteAnnouncementsJob.Factory());
|
||||||
put(RotateCertificateJob.KEY, new RotateCertificateJob.Factory());
|
put(RotateCertificateJob.KEY, new RotateCertificateJob.Factory());
|
||||||
put(RotateProfileKeyJob.KEY, new RotateProfileKeyJob.Factory());
|
put(RotateProfileKeyJob.KEY, new RotateProfileKeyJob.Factory());
|
||||||
put(RotateSignedPreKeyJob.KEY, new RotateSignedPreKeyJob.Factory());
|
|
||||||
put(SenderKeyDistributionSendJob.KEY, new SenderKeyDistributionSendJob.Factory());
|
put(SenderKeyDistributionSendJob.KEY, new SenderKeyDistributionSendJob.Factory());
|
||||||
put(SendDeliveryReceiptJob.KEY, new SendDeliveryReceiptJob.Factory());
|
put(SendDeliveryReceiptJob.KEY, new SendDeliveryReceiptJob.Factory());
|
||||||
put(SendReadReceiptJob.KEY, new SendReadReceiptJob.Factory(application));
|
put(SendReadReceiptJob.KEY, new SendReadReceiptJob.Factory(application));
|
||||||
|
@ -241,6 +239,9 @@ public final class JobManagerFactories {
|
||||||
put("LeaveGroupJob", new FailingJob.Factory());
|
put("LeaveGroupJob", new FailingJob.Factory());
|
||||||
put("PushGroupUpdateJob", new FailingJob.Factory());
|
put("PushGroupUpdateJob", new FailingJob.Factory());
|
||||||
put("RequestGroupInfoJob", new FailingJob.Factory());
|
put("RequestGroupInfoJob", new FailingJob.Factory());
|
||||||
|
put("RotateSignedPreKeyJob", new PreKeysSyncJob.Factory());
|
||||||
|
put("CreateSignedPreKeyJob", new PreKeysSyncJob.Factory());
|
||||||
|
put("RefreshPreKeysJob", new PreKeysSyncJob.Factory());
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,178 @@
|
||||||
|
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.thoughtcrime.securesms.crypto.PreKeyUtil
|
||||||
|
import org.thoughtcrime.securesms.crypto.storage.PreKeyMetadataStore
|
||||||
|
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.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
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
class PreKeysSyncJob private constructor(private val forceRotate: Boolean = false, 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)
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
@JvmOverloads
|
||||||
|
fun enqueue(forceRotate: Boolean = false) {
|
||||||
|
ApplicationDependencies.getJobManager().add(PreKeysSyncJob(forceRotate))
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun enqueueIfNeeded() {
|
||||||
|
if (!SignalStore.account().aciPreKeys.isSignedPreKeyRegistered || !SignalStore.account().pniPreKeys.isSignedPreKeyRegistered) {
|
||||||
|
Log.i(TAG, "Some signed prekeys aren't registered yet. Enqueuing a job. ACI: ${SignalStore.account().aciPreKeys.isSignedPreKeyRegistered} PNI: ${SignalStore.account().pniPreKeys.isSignedPreKeyRegistered}")
|
||||||
|
ApplicationDependencies.getJobManager().add(PreKeysSyncJob())
|
||||||
|
} else if (SignalStore.account().aciPreKeys.activeSignedPreKeyId < 0 || SignalStore.account().pniPreKeys.activeSignedPreKeyId < 0) {
|
||||||
|
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
|
||||||
|
|
||||||
|
if (timeSinceLastRefresh > REFRESH_INTERVAL) {
|
||||||
|
Log.i(TAG, "Scheduling a prekey refresh. Time since last schedule: $timeSinceLastRefresh ms")
|
||||||
|
ApplicationDependencies.getJobManager().add(PreKeysSyncJob())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||||
|
constructor(forceRotate: Boolean = false) : this(
|
||||||
|
forceRotate,
|
||||||
|
Parameters.Builder()
|
||||||
|
.setQueue("PreKeysSyncJob")
|
||||||
|
.addConstraint(NetworkConstraint.KEY)
|
||||||
|
.setMaxInstancesForFactory(1)
|
||||||
|
.setMaxAttempts(Parameters.UNLIMITED)
|
||||||
|
.setLifespan(TimeUnit.DAYS.toMillis(30))
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun getFactoryKey(): String = KEY
|
||||||
|
|
||||||
|
override fun serialize(): Data {
|
||||||
|
return Data.Builder()
|
||||||
|
.putBoolean(KEY_FORCE_ROTATE, forceRotate)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRun() {
|
||||||
|
if (!SignalStore.account().isRegistered || SignalStore.account().aci == null || SignalStore.account().pni == null) {
|
||||||
|
warn(TAG, "Not yet registered")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun syncPreKeys(serviceIdType: ServiceIdType, serviceId: ServiceId?, protocolStore: SignalProtocolStore, metadataStore: PreKeyMetadataStore) {
|
||||||
|
if (serviceId == null) {
|
||||||
|
warn(TAG, serviceIdType, "AccountId not set!")
|
||||||
|
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")
|
||||||
|
|
||||||
|
if (availableKeys >= PREKEY_MINIMUM && metadataStore.isSignedPreKeyRegistered) {
|
||||||
|
log(serviceIdType, "Available keys sufficient.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val preKeyRecords = PreKeyUtil.generateAndStoreOneTimePreKeys(protocolStore, metadataStore)
|
||||||
|
val signedPreKeyRecord = PreKeyUtil.generateAndStoreSignedPreKey(protocolStore, metadataStore)
|
||||||
|
val identityKey = protocolStore.identityKeyPair
|
||||||
|
|
||||||
|
log(serviceIdType, "Registering new prekeys...")
|
||||||
|
|
||||||
|
accountManager.setPreKeys(serviceIdType, identityKey.publicKey, signedPreKeyRecord, preKeyRecords)
|
||||||
|
metadataStore.activeSignedPreKeyId = signedPreKeyRecord.id
|
||||||
|
metadataStore.isSignedPreKeyRegistered = true
|
||||||
|
|
||||||
|
log(serviceIdType, "Cleaning prekeys...")
|
||||||
|
PreKeyUtil.cleanSignedPreKeys(protocolStore, metadataStore)
|
||||||
|
|
||||||
|
SignalStore.misc().lastPrekeyRefreshTime = System.currentTimeMillis()
|
||||||
|
log(serviceIdType, "Successfully refreshed prekeys.")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onShouldRetry(e: Exception): Boolean {
|
||||||
|
return when (e) {
|
||||||
|
is NonSuccessfulResponseCodeException -> false
|
||||||
|
is PushNetworkException -> true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure() {
|
||||||
|
val aciStore = SignalStore.account().aciPreKeys
|
||||||
|
val pniStore = SignalStore.account().pniPreKeys
|
||||||
|
|
||||||
|
if ((aciStore.isSignedPreKeyRegistered || pniStore.isSignedPreKeyRegistered) && forceRotate) {
|
||||||
|
aciStore.signedPreKeyFailureCount++
|
||||||
|
pniStore.signedPreKeyFailureCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun log(serviceIdType: ServiceIdType, message: String) {
|
||||||
|
Log.i(TAG, "[$serviceIdType] $message")
|
||||||
|
}
|
||||||
|
|
||||||
|
class Factory : Job.Factory<PreKeysSyncJob> {
|
||||||
|
override fun create(parameters: Parameters, data: Data): PreKeysSyncJob {
|
||||||
|
return PreKeysSyncJob(data.getBooleanOrDefault(KEY_FORCE_ROTATE, false), parameters)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -103,7 +103,7 @@ public abstract class PushSendJob extends SendJob {
|
||||||
@Override
|
@Override
|
||||||
protected final void onSend() throws Exception {
|
protected final void onSend() throws Exception {
|
||||||
if (SignalStore.account().aciPreKeys().getSignedPreKeyFailureCount() > 5) {
|
if (SignalStore.account().aciPreKeys().getSignedPreKeyFailureCount() > 5) {
|
||||||
ApplicationDependencies.getJobManager().add(new RotateSignedPreKeyJob());
|
PreKeysSyncJob.enqueue(true);
|
||||||
throw new TextSecureExpiredException("Too many signed prekey rotation failures");
|
throw new TextSecureExpiredException("Too many signed prekey rotation failures");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,148 +0,0 @@
|
||||||
package org.thoughtcrime.securesms.jobs;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import org.signal.core.util.logging.Log;
|
|
||||||
import org.signal.libsignal.protocol.IdentityKeyPair;
|
|
||||||
import org.signal.libsignal.protocol.state.PreKeyRecord;
|
|
||||||
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.Data;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
|
||||||
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
|
||||||
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.io.IOException;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensures that our prekeys are up to date for both our ACI and PNI identities.
|
|
||||||
* Specifically, if we have less than {@link #PREKEY_MINIMUM} one-time prekeys, we will generate and upload
|
|
||||||
* a new batch of one-time prekeys, as well as a new signed prekey.
|
|
||||||
*/
|
|
||||||
public class RefreshPreKeysJob extends BaseJob {
|
|
||||||
|
|
||||||
public static final String KEY = "RefreshPreKeysJob";
|
|
||||||
|
|
||||||
private static final String TAG = Log.tag(RefreshPreKeysJob.class);
|
|
||||||
|
|
||||||
private static final int PREKEY_MINIMUM = 10;
|
|
||||||
|
|
||||||
private static final long REFRESH_INTERVAL = TimeUnit.DAYS.toMillis(3);
|
|
||||||
|
|
||||||
public RefreshPreKeysJob() {
|
|
||||||
this(new Job.Parameters.Builder()
|
|
||||||
.setQueue("RefreshPreKeysJob")
|
|
||||||
.addConstraint(NetworkConstraint.KEY)
|
|
||||||
.setMaxInstancesForFactory(1)
|
|
||||||
.setMaxAttempts(Parameters.UNLIMITED)
|
|
||||||
.setLifespan(TimeUnit.DAYS.toMillis(30))
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void scheduleIfNecessary() {
|
|
||||||
long timeSinceLastRefresh = System.currentTimeMillis() - SignalStore.misc().getLastPrekeyRefreshTime();
|
|
||||||
|
|
||||||
if (timeSinceLastRefresh > REFRESH_INTERVAL) {
|
|
||||||
Log.i(TAG, "Scheduling a prekey refresh. Time since last schedule: " + timeSinceLastRefresh + " ms");
|
|
||||||
ApplicationDependencies.getJobManager().add(new RefreshPreKeysJob());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private RefreshPreKeysJob(@NonNull Job.Parameters parameters) {
|
|
||||||
super(parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull Data serialize() {
|
|
||||||
return Data.EMPTY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull String getFactoryKey() {
|
|
||||||
return KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRun() throws IOException {
|
|
||||||
if (!SignalStore.account().isRegistered() || SignalStore.account().getAci() == null || SignalStore.account().getPni() == null) {
|
|
||||||
Log.w(TAG, "Not registered. Skipping.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SignalProtocolStore aciProtocolStore = ApplicationDependencies.getProtocolStore().aci();
|
|
||||||
PreKeyMetadataStore aciPreKeyStore = SignalStore.account().aciPreKeys();
|
|
||||||
|
|
||||||
SignalProtocolStore pniProtocolStore = ApplicationDependencies.getProtocolStore().pni();
|
|
||||||
PreKeyMetadataStore pniPreKeyStore = SignalStore.account().pniPreKeys();
|
|
||||||
|
|
||||||
if (refreshKeys(ServiceIdType.ACI, aciProtocolStore, aciPreKeyStore)) {
|
|
||||||
PreKeyUtil.cleanSignedPreKeys(aciProtocolStore, aciPreKeyStore);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (refreshKeys(ServiceIdType.PNI, pniProtocolStore, pniPreKeyStore)) {
|
|
||||||
PreKeyUtil.cleanSignedPreKeys(pniProtocolStore, pniPreKeyStore);
|
|
||||||
}
|
|
||||||
|
|
||||||
SignalStore.misc().setLastPrekeyRefreshTime(System.currentTimeMillis());
|
|
||||||
Log.i(TAG, "Successfully refreshed prekeys.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return True if we need to clean prekeys, otherwise false.
|
|
||||||
*/
|
|
||||||
private boolean refreshKeys(@NonNull ServiceIdType serviceIdType, @NonNull SignalProtocolStore protocolStore, @NonNull PreKeyMetadataStore metadataStore) throws IOException {
|
|
||||||
String logPrefix = "[" + serviceIdType + "] ";
|
|
||||||
|
|
||||||
SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
|
|
||||||
|
|
||||||
int availableKeys = accountManager.getPreKeysCount(serviceIdType);
|
|
||||||
log(TAG, logPrefix + "Available keys: " + availableKeys);
|
|
||||||
|
|
||||||
if (availableKeys >= PREKEY_MINIMUM && metadataStore.isSignedPreKeyRegistered()) {
|
|
||||||
log(TAG, logPrefix + "Available keys sufficient.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<PreKeyRecord> preKeyRecords = PreKeyUtil.generateAndStoreOneTimePreKeys(protocolStore, metadataStore);
|
|
||||||
SignedPreKeyRecord signedPreKeyRecord = PreKeyUtil.generateAndStoreSignedPreKey(protocolStore, metadataStore, false);
|
|
||||||
IdentityKeyPair identityKey = protocolStore.getIdentityKeyPair();
|
|
||||||
|
|
||||||
log(TAG, logPrefix + "Registering new prekeys...");
|
|
||||||
|
|
||||||
accountManager.setPreKeys(serviceIdType, identityKey.getPublicKey(), signedPreKeyRecord, preKeyRecords);
|
|
||||||
|
|
||||||
metadataStore.setActiveSignedPreKeyId(signedPreKeyRecord.getId());
|
|
||||||
metadataStore.setSignedPreKeyRegistered(true);
|
|
||||||
|
|
||||||
log(TAG, logPrefix + "Need to clean prekeys.");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onShouldRetry(@NonNull Exception exception) {
|
|
||||||
if (exception instanceof NonSuccessfulResponseCodeException) return false;
|
|
||||||
if (exception instanceof PushNetworkException) return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class Factory implements Job.Factory<RefreshPreKeysJob> {
|
|
||||||
@Override
|
|
||||||
public @NonNull RefreshPreKeysJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
|
||||||
return new RefreshPreKeysJob(parameters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,117 +0,0 @@
|
||||||
package org.thoughtcrime.securesms.jobs;
|
|
||||||
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
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.Data;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
|
||||||
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
|
||||||
import org.whispersystems.signalservice.api.push.ACI;
|
|
||||||
import org.whispersystems.signalservice.api.push.PNI;
|
|
||||||
import org.whispersystems.signalservice.api.push.ServiceIdType;
|
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Forces the creation + upload of new signed prekeys for both the ACI and PNI identities.
|
|
||||||
*/
|
|
||||||
public class RotateSignedPreKeyJob extends BaseJob {
|
|
||||||
|
|
||||||
public static final String KEY = "RotateSignedPreKeyJob";
|
|
||||||
|
|
||||||
private static final String TAG = Log.tag(RotateSignedPreKeyJob.class);
|
|
||||||
|
|
||||||
public RotateSignedPreKeyJob() {
|
|
||||||
this(new Job.Parameters.Builder()
|
|
||||||
.setQueue("RotateSignedPreKeyJob")
|
|
||||||
.addConstraint(NetworkConstraint.KEY)
|
|
||||||
.setMaxInstancesForFactory(1)
|
|
||||||
.setMaxAttempts(Parameters.UNLIMITED)
|
|
||||||
.setLifespan(TimeUnit.DAYS.toMillis(2))
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
private RotateSignedPreKeyJob(@NonNull Job.Parameters parameters) {
|
|
||||||
super(parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull Data serialize() {
|
|
||||||
return Data.EMPTY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull String getFactoryKey() {
|
|
||||||
return KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRun() throws Exception {
|
|
||||||
if (!SignalStore.account().isRegistered() || SignalStore.account().getAci() == null || SignalStore.account().getPni() == null) {
|
|
||||||
Log.w(TAG, "Not registered. Skipping.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.i(TAG, "Rotating signed prekey...");
|
|
||||||
|
|
||||||
ACI aci = SignalStore.account().getAci();
|
|
||||||
PNI pni = SignalStore.account().getPni();
|
|
||||||
|
|
||||||
if (aci == null) {
|
|
||||||
Log.w(TAG, "ACI is unset!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pni == null) {
|
|
||||||
Log.w(TAG, "PNI is unset!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
rotate(ServiceIdType.ACI, ApplicationDependencies.getProtocolStore().aci(), SignalStore.account().aciPreKeys());
|
|
||||||
rotate(ServiceIdType.PNI, ApplicationDependencies.getProtocolStore().pni(), SignalStore.account().pniPreKeys());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void rotate(@NonNull ServiceIdType serviceIdType, @NonNull SignalProtocolStore protocolStore, @NonNull PreKeyMetadataStore metadataStore)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
|
|
||||||
SignedPreKeyRecord signedPreKeyRecord = PreKeyUtil.generateAndStoreSignedPreKey(protocolStore, metadataStore, false);
|
|
||||||
|
|
||||||
accountManager.setSignedPreKey(serviceIdType, signedPreKeyRecord);
|
|
||||||
|
|
||||||
metadataStore.setActiveSignedPreKeyId(signedPreKeyRecord.getId());
|
|
||||||
metadataStore.setSignedPreKeyRegistered(true);
|
|
||||||
metadataStore.setSignedPreKeyFailureCount(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onShouldRetry(@NonNull Exception exception) {
|
|
||||||
return exception instanceof PushNetworkException;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure() {
|
|
||||||
PreKeyMetadataStore aciStore = SignalStore.account().aciPreKeys();
|
|
||||||
PreKeyMetadataStore pniStore = SignalStore.account().pniPreKeys();
|
|
||||||
|
|
||||||
aciStore.setSignedPreKeyFailureCount(aciStore.getSignedPreKeyFailureCount() + 1);
|
|
||||||
pniStore.setSignedPreKeyFailureCount(pniStore.getSignedPreKeyFailureCount() + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class Factory implements Job.Factory<RotateSignedPreKeyJob> {
|
|
||||||
@Override
|
|
||||||
public @NonNull RotateSignedPreKeyJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
|
||||||
return new RotateSignedPreKeyJob(parameters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -33,7 +33,7 @@ import org.thoughtcrime.securesms.groups.BadGroupIdException;
|
||||||
import org.thoughtcrime.securesms.groups.GroupId;
|
import org.thoughtcrime.securesms.groups.GroupId;
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||||
import org.thoughtcrime.securesms.jobs.AutomaticSessionResetJob;
|
import org.thoughtcrime.securesms.jobs.AutomaticSessionResetJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob;
|
import org.thoughtcrime.securesms.jobs.PreKeysSyncJob;
|
||||||
import org.thoughtcrime.securesms.jobs.SendRetryReceiptJob;
|
import org.thoughtcrime.securesms.jobs.SendRetryReceiptJob;
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||||
import org.thoughtcrime.securesms.logsubmit.SubmitDebugLogActivity;
|
import org.thoughtcrime.securesms.logsubmit.SubmitDebugLogActivity;
|
||||||
|
@ -43,7 +43,6 @@ import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||||
import org.thoughtcrime.securesms.notifications.NotificationIds;
|
import org.thoughtcrime.securesms.notifications.NotificationIds;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
|
||||||
import org.whispersystems.signalservice.api.InvalidMessageStructureException;
|
import org.whispersystems.signalservice.api.InvalidMessageStructureException;
|
||||||
import org.whispersystems.signalservice.api.SignalServiceAccountDataStore;
|
import org.whispersystems.signalservice.api.SignalServiceAccountDataStore;
|
||||||
import org.whispersystems.signalservice.api.crypto.ContentHint;
|
import org.whispersystems.signalservice.api.crypto.ContentHint;
|
||||||
|
@ -101,7 +100,7 @@ public final class MessageDecryptionUtil {
|
||||||
List<Job> jobs = new LinkedList<>();
|
List<Job> jobs = new LinkedList<>();
|
||||||
|
|
||||||
if (envelope.isPreKeySignalMessage()) {
|
if (envelope.isPreKeySignalMessage()) {
|
||||||
jobs.add(new RefreshPreKeysJob());
|
PreKeysSyncJob.enqueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -20,8 +20,8 @@ import org.thoughtcrime.securesms.jobmanager.Data;
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||||
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||||
import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob;
|
import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob;
|
||||||
import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
|
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
|
||||||
|
import org.thoughtcrime.securesms.jobs.PreKeysSyncJob;
|
||||||
import org.thoughtcrime.securesms.jobs.PushDecryptMessageJob;
|
import org.thoughtcrime.securesms.jobs.PushDecryptMessageJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
|
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||||
|
@ -129,7 +129,7 @@ public class LegacyMigrationJob extends MigrationJob {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastSeenVersion < SIGNED_PREKEY_VERSION) {
|
if (lastSeenVersion < SIGNED_PREKEY_VERSION) {
|
||||||
CreateSignedPreKeyJob.enqueueIfNeeded();
|
PreKeysSyncJob.enqueueIfNeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastSeenVersion < NO_DECRYPT_QUEUE_VERSION) {
|
if (lastSeenVersion < NO_DECRYPT_QUEUE_VERSION) {
|
||||||
|
|
|
@ -75,10 +75,11 @@ public class PniAccountInitializationMigrationJob extends MigrationJob {
|
||||||
|
|
||||||
if (!metadataStore.isSignedPreKeyRegistered()) {
|
if (!metadataStore.isSignedPreKeyRegistered()) {
|
||||||
Log.i(TAG, "Uploading signed prekey for PNI.");
|
Log.i(TAG, "Uploading signed prekey for PNI.");
|
||||||
SignedPreKeyRecord signedPreKey = PreKeyUtil.generateAndStoreSignedPreKey(protocolStore, metadataStore, true);
|
SignedPreKeyRecord signedPreKey = PreKeyUtil.generateAndStoreSignedPreKey(protocolStore, metadataStore);
|
||||||
List<PreKeyRecord> oneTimePreKeys = PreKeyUtil.generateAndStoreOneTimePreKeys(protocolStore, metadataStore);
|
List<PreKeyRecord> oneTimePreKeys = PreKeyUtil.generateAndStoreOneTimePreKeys(protocolStore, metadataStore);
|
||||||
|
|
||||||
accountManager.setPreKeys(ServiceIdType.PNI, protocolStore.getIdentityKeyPair().getPublicKey(), signedPreKey, oneTimePreKeys);
|
accountManager.setPreKeys(ServiceIdType.PNI, protocolStore.getIdentityKeyPair().getPublicKey(), signedPreKey, oneTimePreKeys);
|
||||||
|
metadataStore.setActiveSignedPreKeyId(signedPreKey.getId());
|
||||||
metadataStore.setSignedPreKeyRegistered(true);
|
metadataStore.setSignedPreKeyRegistered(true);
|
||||||
} else {
|
} else {
|
||||||
Log.w(TAG, "Already uploaded signed prekey for PNI. Skipping this step.");
|
Log.w(TAG, "Already uploaded signed prekey for PNI. Skipping this step.");
|
||||||
|
|
|
@ -190,10 +190,11 @@ public final class RegistrationRepository {
|
||||||
@NonNull PreKeyMetadataStore metadataStore)
|
@NonNull PreKeyMetadataStore metadataStore)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
SignedPreKeyRecord signedPreKey = PreKeyUtil.generateAndStoreSignedPreKey(protocolStore, metadataStore, true);
|
SignedPreKeyRecord signedPreKey = PreKeyUtil.generateAndStoreSignedPreKey(protocolStore, metadataStore);
|
||||||
List<PreKeyRecord> oneTimePreKeys = PreKeyUtil.generateAndStoreOneTimePreKeys(protocolStore, metadataStore);
|
List<PreKeyRecord> oneTimePreKeys = PreKeyUtil.generateAndStoreOneTimePreKeys(protocolStore, metadataStore);
|
||||||
|
|
||||||
accountManager.setPreKeys(serviceIdType, protocolStore.getIdentityKeyPair().getPublicKey(), signedPreKey, oneTimePreKeys);
|
accountManager.setPreKeys(serviceIdType, protocolStore.getIdentityKeyPair().getPublicKey(), signedPreKey, oneTimePreKeys);
|
||||||
|
metadataStore.setActiveSignedPreKeyId(signedPreKey.getId());
|
||||||
metadataStore.setSignedPreKeyRegistered(true);
|
metadataStore.setSignedPreKeyRegistered(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.jobs.RotateSignedPreKeyJob;
|
import org.thoughtcrime.securesms.jobs.PreKeysSyncJob;
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ public class RotateSignedPreKeyListener extends PersistentAlarmManagerListener {
|
||||||
@Override
|
@Override
|
||||||
protected long onAlarm(Context context, long scheduledTime) {
|
protected long onAlarm(Context context, long scheduledTime) {
|
||||||
if (scheduledTime != 0 && SignalStore.account().isRegistered()) {
|
if (scheduledTime != 0 && SignalStore.account().isRegistered()) {
|
||||||
ApplicationDependencies.getJobManager().add(new RotateSignedPreKeyJob());
|
PreKeysSyncJob.enqueue(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
long nextTime = System.currentTimeMillis() + INTERVAL;
|
long nextTime = System.currentTimeMillis() + INTERVAL;
|
||||||
|
|
|
@ -15,6 +15,10 @@ public class PreKeyStatus {
|
||||||
|
|
||||||
public PreKeyStatus() {}
|
public PreKeyStatus() {}
|
||||||
|
|
||||||
|
public PreKeyStatus(int count) {
|
||||||
|
this.count = count;
|
||||||
|
}
|
||||||
|
|
||||||
public int getCount() {
|
public int getCount() {
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
Ładowanie…
Reference in New Issue