Fix illegal session state crashes in receipt send flows.

This commit is contained in:
Cody Henthorne
2026-06-04 11:21:46 -04:00
committed by GitHub
parent 7dcaa933f2
commit af4d0a0ef0
4 changed files with 62 additions and 17 deletions
@@ -0,0 +1,45 @@
package org.thoughtcrime.securesms.jobs
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.recipients.RecipientId
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException
import org.whispersystems.signalservice.api.messages.SendMessageResult
import java.io.IOException
/**
* Shared send logic for the receipt jobs ([SendReadReceiptJob], [SendDeliveryReceiptJob], [SendViewedReceiptJob]) that
* will repair on first failure and then try again.
*/
object ReceiptSender {
private val TAG = Log.tag(ReceiptSender::class.java)
/**
* @return the result of the send, or `null` if the receipt was dropped because the session could not be repaired.
*/
@JvmStatic
@Throws(IOException::class, UntrustedIdentityException::class)
fun sendWithSessionRepair(recipientId: RecipientId, operation: ReceiptSendOperation): SendMessageResult? {
return try {
operation.send()
} catch (e: IllegalStateException) {
Log.w(TAG, "Failed to send receipt, likely due to a missing or corrupt session. Archiving sessions and retrying.", e)
AppDependencies.protocolStore.aci().sessions().archiveSessions(recipientId)
AppDependencies.protocolStore.pni().sessions().archiveSessions(recipientId)
try {
operation.send()
} catch (retryError: IllegalStateException) {
Log.w(TAG, "Failed to send receipt even after archiving sessions. Dropping.", retryError)
null
}
}
}
fun interface ReceiptSendOperation {
@Throws(IOException::class, UntrustedIdentityException::class)
fun send(): SendMessageResult
}
}
@@ -125,12 +125,12 @@ public class SendDeliveryReceiptJob extends BaseJob {
Collections.singletonList(messageSentTimestamp),
timestamp);
SendMessageResult result = messageSender.sendReceipt(remoteAddress,
SealedSenderAccessUtil.getSealedSenderAccessFor(recipient, this::getGroupSendFullToken),
receiptMessage,
recipient.getNeedsPniSignature());
SendMessageResult result = ReceiptSender.sendWithSessionRepair(recipientId, () -> messageSender.sendReceipt(remoteAddress,
SealedSenderAccessUtil.getSealedSenderAccessFor(recipient, this::getGroupSendFullToken),
receiptMessage,
recipient.getNeedsPniSignature()));
if (messageId != null) {
if (result != null && messageId != null) {
SignalDatabase.messageLog().insertIfPossible(recipientId, timestamp, result, ContentHint.IMPLICIT, messageId, false);
}
}
@@ -191,13 +191,13 @@ public class SendReadReceiptJob extends BaseJob {
SignalServiceAddress remoteAddress = RecipientUtil.toSignalServiceAddress(context, recipient);
SignalServiceReceiptMessage receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.READ, messageSentTimestamps, timestamp);
SendMessageResult result = messageSender.sendReceipt(remoteAddress,
SealedSenderAccessUtil.getSealedSenderAccessFor(recipient,
() -> SignalDatabase.groups().getGroupSendFullToken(threadId, recipientId)),
receiptMessage,
recipient.getNeedsPniSignature());
SendMessageResult result = ReceiptSender.sendWithSessionRepair(recipientId, () -> messageSender.sendReceipt(remoteAddress,
SealedSenderAccessUtil.getSealedSenderAccessFor(recipient,
() -> SignalDatabase.groups().getGroupSendFullToken(threadId, recipientId)),
receiptMessage,
recipient.getNeedsPniSignature()));
if (Util.hasItems(messageIds)) {
if (result != null && Util.hasItems(messageIds)) {
SignalDatabase.messageLog().insertIfPossible(recipientId, timestamp, result, ContentHint.IMPLICIT, messageIds, false);
}
}
@@ -209,13 +209,13 @@ public class SendViewedReceiptJob extends BaseJob {
messageSentTimestamps,
timestamp);
SendMessageResult result = messageSender.sendReceipt(remoteAddress,
SealedSenderAccessUtil.getSealedSenderAccessFor(recipient,
() -> SignalDatabase.groups().getGroupSendFullToken(threadId, recipientId)),
receiptMessage,
recipient.getNeedsPniSignature());
SendMessageResult result = ReceiptSender.sendWithSessionRepair(recipientId, () -> messageSender.sendReceipt(remoteAddress,
SealedSenderAccessUtil.getSealedSenderAccessFor(recipient,
() -> SignalDatabase.groups().getGroupSendFullToken(threadId, recipientId)),
receiptMessage,
recipient.getNeedsPniSignature()));
if (Util.hasItems(foundMessageIds)) {
if (result != null && Util.hasItems(foundMessageIds)) {
SignalDatabase.messageLog().insertIfPossible(recipientId, timestamp, result, ContentHint.IMPLICIT, foundMessageIds, false);
}
}