Update MessageApiV2 to use libsignal-net.

This commit is contained in:
Greyson Parrelli
2026-05-29 14:02:42 -04:00
committed by Michelle Tang
parent 51c4afe5f5
commit f76292769a
5 changed files with 608 additions and 570 deletions
@@ -17,6 +17,7 @@ import okio.utf8Size
import org.signal.core.models.ServiceId
import org.signal.core.util.logging.Log
import org.signal.core.util.orNull
import org.signal.libsignal.net.ChallengeOption
import org.signal.libsignal.protocol.SignalProtocolAddress
import org.signal.network.service.MessageService
import org.thoughtcrime.securesms.BuildConfig
@@ -201,7 +202,7 @@ class IndividualSendJobV2 private constructor(parameters: Parameters, private va
val syntheticResult = SendMessageResult.success(
SignalServiceAddress(recipient.requireServiceId(), recipient.e164.orNull()),
success.devices,
success.sentUnidentified,
success.sentSealedSender,
false,
0L,
Optional.of(content)
@@ -220,7 +221,7 @@ class IndividualSendJobV2 private constructor(parameters: Parameters, private va
SignalDatabase.pendingPniSignatureMessages.insertIfNecessary(recipient.id, message.sentTimeMillis, syntheticResult)
}
SignalDatabase.messages.markAsSent(messageId, success.sentUnidentified)
SignalDatabase.messages.markAsSent(messageId, success.sentSealedSender)
PushSendJob.markAttachmentsUploaded(messageId, message)
SignalDatabase.threads.updateSilently(threadId, false)
@@ -232,11 +233,11 @@ class IndividualSendJobV2 private constructor(parameters: Parameters, private va
}
val accessMode = recipient.sealedSenderAccessMode
if (success.sentUnidentified && accessMode == SealedSenderAccessMode.UNKNOWN && recipient.profileKey == null) {
if (success.sentSealedSender && accessMode == SealedSenderAccessMode.UNKNOWN && recipient.profileKey == null) {
SignalDatabase.recipients.setSealedSenderAccessMode(recipient.id, SealedSenderAccessMode.UNRESTRICTED)
} else if (success.sentUnidentified && accessMode == SealedSenderAccessMode.UNKNOWN) {
} else if (success.sentSealedSender && accessMode == SealedSenderAccessMode.UNKNOWN) {
SignalDatabase.recipients.setSealedSenderAccessMode(recipient.id, SealedSenderAccessMode.ENABLED)
} else if (!success.sentUnidentified && accessMode != SealedSenderAccessMode.DISABLED) {
} else if (!success.sentSealedSender && accessMode != SealedSenderAccessMode.DISABLED) {
SignalDatabase.recipients.setSealedSenderAccessMode(recipient.id, SealedSenderAccessMode.DISABLED)
}
@@ -259,36 +260,41 @@ class IndividualSendJobV2 private constructor(parameters: Parameters, private va
ifLeft = { error ->
when (error) {
is MessageService.SendError.IdentityMismatch -> {
Log.w(TAG, "${logPrefix(message.sentTimeMillis)} Identity mismatch for ${error.recipient.identifier}", error.cause)
val externalRecipient = Recipient.external(error.recipient.identifier)
Log.w(TAG, "${logPrefix(message.sentTimeMillis)} Identity mismatch for ${error.serviceId}", error.exception)
val externalRecipient = Recipient.external(error.serviceId.toString())
if (externalRecipient == null) {
Log.w(TAG, "${logPrefix(message.sentTimeMillis)} Failed to create a Recipient for the identifier!")
} else {
SignalDatabase.messages.addMismatchedIdentity(messageId, externalRecipient.id, error.cause.untrustedIdentity)
SignalDatabase.messages.addMismatchedIdentity(messageId, externalRecipient.id, error.exception.untrustedIdentity)
SignalDatabase.messages.markAsSentFailed(messageId)
RetrieveProfileJob.enqueue(externalRecipient.id, true)
}
Result.success()
}
MessageService.SendError.NotRegistered -> {
Log.w(TAG, "${logPrefix(message.sentTimeMillis)} Recipient not registered")
is MessageService.SendError.NotRegistered -> {
Log.w(TAG, "${logPrefix(message.sentTimeMillis)} Recipient not registered", error)
SignalDatabase.messages.markAsSentFailed(messageId)
PushSendJob.notifyMediaMessageDeliveryFailed(context, messageId)
AppDependencies.jobManager.add(DirectoryRefreshJob(false))
Result.success()
}
MessageService.SendError.Unauthorized -> {
Log.w(TAG, "${logPrefix(message.sentTimeMillis)} Unauthorized send")
is MessageService.SendError.Unauthorized -> {
Log.w(TAG, "${logPrefix(message.sentTimeMillis)} Unauthorized send", error)
Result.failure()
}
is MessageService.SendError.ChallengeRequired -> {
Log.w(TAG, "${logPrefix(message.sentTimeMillis)} Challenge required (options=${error.options})")
Log.w(TAG, "${logPrefix(message.sentTimeMillis)} Challenge required (options=${error.options})", error)
val proofResponse = ProofRequiredResponse().apply {
token = error.token
options = error.options
options = error.options.map {
when (it) {
ChallengeOption.PUSH_CHALLENGE -> "pushChallenge"
ChallengeOption.CAPTCHA -> "captcha"
}
}
}
val proofException = ProofRequiredException(proofResponse, error.retryAfter?.inWholeSeconds ?: 0L)
val threadRecipient = SignalDatabase.threads.getRecipientForThreadId(threadId)
@@ -299,23 +305,23 @@ class IndividualSendJobV2 private constructor(parameters: Parameters, private va
}
}
MessageService.SendError.ServerRejected -> {
Log.w(TAG, "${logPrefix(message.sentTimeMillis)} Server rejected the send")
is MessageService.SendError.ServerRejected -> {
Log.w(TAG, "${logPrefix(message.sentTimeMillis)} Server rejected the send", error)
Result.failure()
}
is MessageService.SendError.ContentTooLarge -> {
Log.w(TAG, "${logPrefix(message.sentTimeMillis)} Content too large (${error.size} > ${error.maxAllowed} bytes); failing.")
Log.w(TAG, "${logPrefix(message.sentTimeMillis)} Content too large (${error.size} > ${error.maxAllowed} bytes). Failing.", error)
Result.failure()
}
MessageService.SendError.SessionAttemptsExhausted -> {
Log.w(TAG, "${logPrefix(message.sentTimeMillis)} Exhausted device-resolution attempts; retrying")
is MessageService.SendError.SessionAttemptsExhausted -> {
Log.w(TAG, "${logPrefix(message.sentTimeMillis)} Exhausted device-resolution attempts. Retrying", error)
Result.retry(nextRunAttemptBackoff(runAttempt + 1))
}
is MessageService.SendError.PreKeyUnavailable -> {
Log.w(TAG, "${logPrefix(message.sentTimeMillis)} Prekey unavailable: ${error.reason}")
Log.w(TAG, "${logPrefix(message.sentTimeMillis)} Prekey unavailable: ${error.reason}", error)
Result.retry(nextRunAttemptBackoff(runAttempt + 1))
}
@@ -323,16 +329,16 @@ class IndividualSendJobV2 private constructor(parameters: Parameters, private va
val defaultBackoff = nextRunAttemptBackoff(runAttempt + 1)
val serverBackoff = error.retryAfter?.inWholeMilliseconds ?: 0L
val backoff = maxOf(defaultBackoff, serverBackoff)
Log.w(TAG, "${logPrefix(message.sentTimeMillis)} Rate limited, retryAfter=${error.retryAfter}, using backoff=${backoff}ms")
Log.w(TAG, "${logPrefix(message.sentTimeMillis)} Rate limited, retryAfter=${error.retryAfter}, using backoff=${backoff}ms", error)
Result.retry(backoff)
}
is MessageService.SendError.NetworkError -> {
Log.w(TAG, "${logPrefix(message.sentTimeMillis)} Network error", error.cause)
Log.w(TAG, "${logPrefix(message.sentTimeMillis)} Network error", error.exception)
Result.retry(nextRunAttemptBackoff(runAttempt + 1))
}
is MessageService.SendError.ApplicationError -> when (val cause = error.cause) {
is MessageService.SendError.ApplicationError -> when (val cause = error.exception) {
is RuntimeException -> {
Log.e(TAG, "${logPrefix(message.sentTimeMillis)} Encountered a fatal application error. Crash imminent.", cause)
Result.fatalFailure(cause)
@@ -397,7 +403,7 @@ class IndividualSendJobV2 private constructor(parameters: Parameters, private va
}
return AppDependencies.messageService.sendMessage(
recipient = SignalServiceAddress(recipient.requireServiceId(), recipient.e164.orNull()),
serviceId = recipient.requireServiceId(),
envelopeContent = envelopeContent,
timestamp = dataMessage.timestamp!!,
sealedSenderAccess = SealedSenderAccessUtil.getSealedSenderAccessFor(recipient),
@@ -434,7 +440,7 @@ class IndividualSendJobV2 private constructor(parameters: Parameters, private va
unidentifiedStatus = listOf(
SyncMessage.Sent.UnidentifiedDeliveryStatus(
destinationServiceIdBinary = recipientServiceId.toByteString(),
unidentified = primaryResult.sentUnidentified,
unidentified = primaryResult.sentSealedSender,
destinationPniIdentityKey = pniIdentityKey
)
)
@@ -444,7 +450,7 @@ class IndividualSendJobV2 private constructor(parameters: Parameters, private va
val syncEnvelope = EnvelopeContent.encrypted(syncContent, ContentHint.IMPLICIT, Optional.empty())
return AppDependencies.messageService.sendMessage(
recipient = SignalServiceAddress(SignalStore.account.requireAci()),
serviceId = SignalStore.account.requireAci(),
envelopeContent = syncEnvelope,
timestamp = timestamp,
sealedSenderAccess = null, // We don't use sealed sender for sync messages