kopia lustrzana https://github.com/ryukoposting/Signal-Android
179 wiersze
7.7 KiB
Kotlin
179 wiersze
7.7 KiB
Kotlin
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)
|
|
}
|
|
}
|
|
}
|