Use a cancelation result instead of an exception for message sends.

This commit is contained in:
Greyson Parrelli
2025-11-04 11:27:40 -05:00
committed by Michelle Tang
parent 95837b09db
commit 42c3f7ead4
6 changed files with 49 additions and 44 deletions

View File

@@ -17,7 +17,6 @@ import org.thoughtcrime.securesms.net.NotPushRegisteredException;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.api.CancelationException;
import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage.Action;
@@ -127,15 +126,11 @@ public class TypingSendJob extends BaseJob {
SignalServiceTypingMessage typingMessage = new SignalServiceTypingMessage(typing ? Action.STARTED : Action.STOPPED, System.currentTimeMillis(), groupId);
try {
GroupSendUtil.sendTypingMessage(context,
recipient.getGroupId().map(GroupId::requireV2).orElse(null),
recipients,
typingMessage,
this::isCanceled);
} catch (CancelationException e) {
Log.w(TAG, "Canceled during send!");
}
GroupSendUtil.sendTypingMessage(context,
recipient.getGroupId().map(GroupId::requireV2).orElse(null),
recipients,
typingMessage,
this::isCanceled);
}
@Override

View File

@@ -35,7 +35,6 @@ import org.thoughtcrime.securesms.util.RecipientAccessList;
import org.thoughtcrime.securesms.util.RemoteConfig;
import org.thoughtcrime.securesms.util.SignalLocalMetrics;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.signalservice.api.CancelationException;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.SignalServiceMessageSender.LegacyGroupEvents;
import org.whispersystems.signalservice.api.SignalServiceMessageSender.SenderKeyGroupEvents;
@@ -446,7 +445,20 @@ public final class GroupSendUtil {
}
if (cancelationSignal != null && cancelationSignal.isCanceled()) {
throw new CancelationException();
Log.i(TAG, "Send canceled. Adding canceled results for " + legacyTargets.size() + " remaining legacy targets.");
for (Recipient recipient : legacyTargets) {
allResults.add(SendMessageResult.canceledFailure(recipients.getAddress(recipient.getId())));
}
if (unregisteredTargets.size() > 0) {
List<SendMessageResult> unregisteredResults = unregisteredTargets.stream()
.filter(Recipient::getHasServiceId)
.map(t -> SendMessageResult.unregisteredFailure(new SignalServiceAddress(t.requireServiceId(), t.getE164().orElse(null))))
.collect(Collectors.toList());
allResults.addAll(unregisteredResults);
}
return allResults;
}
boolean onlyTargetIsSelfWithLinkedDevice = legacyTargets.isEmpty() && senderKeyTargets.isEmpty() && SignalStore.account().isMultiDevice();

View File

@@ -1,12 +0,0 @@
package org.whispersystems.signalservice.api;
import java.io.IOException;
public class CancelationException extends IOException {
public CancelationException() {
}
public CancelationException(Throwable cause) {
super(cause);
}
}

View File

@@ -1954,7 +1954,7 @@ public class SignalServiceMessageSender {
for (int i = 0; i < RETRY_COUNT; i++) {
if (cancelationSignal != null && cancelationSignal.isCanceled()) {
throw new CancelationException();
return SendMessageResult.canceledFailure(recipient);
}
try {
@@ -1976,7 +1976,7 @@ public class SignalServiceMessageSender {
}
if (cancelationSignal != null && cancelationSignal.isCanceled()) {
throw new CancelationException();
return SendMessageResult.canceledFailure(recipient);
}
try {
@@ -2015,7 +2015,7 @@ public class SignalServiceMessageSender {
}
if (cancelationSignal != null && cancelationSignal.isCanceled()) {
throw new CancelationException();
return SendMessageResult.canceledFailure(recipient);
}
SendMessageResponse response = socket.sendMessage(messages, sealedSenderAccess, story);
@@ -2100,7 +2100,11 @@ public class SignalServiceMessageSender {
if (cause instanceof IOException) {
throw (IOException) cause;
} else if (cause instanceof InterruptedException) {
throw new CancelationException(e);
List<SendMessageResult> canceledResults = new ArrayList<>(recipients.size());
for (SignalServiceAddress recipient : recipients) {
canceledResults.add(SendMessageResult.canceledFailure(recipient));
}
return canceledResults;
} else {
throw e;
}
@@ -2175,7 +2179,7 @@ public class SignalServiceMessageSender {
Single<SendMessageResult> sendWithFallback = messagesSingle
.flatMap(messages -> {
if (cancelationSignal != null && cancelationSignal.isCanceled()) {
return Single.error(new CancelationException());
return Single.just(new kotlin.Pair<>(messages, new NetworkResult.NetworkError<SendMessageResponse>(new IOException("Canceled"))));
}
return Single.fromCallable(() -> messageApi.sendMessage(messages, sealedSenderAccess, story))
@@ -2202,7 +2206,7 @@ public class SignalServiceMessageSender {
return Single.just(result);
} catch (IOException throwable) {
if (cancelationSignal != null && cancelationSignal.isCanceled()) {
return Single.error(new CancelationException());
return Single.just(SendMessageResult.canceledFailure(recipient));
}
if (throwable instanceof AuthorizationFailedException ||
@@ -2248,7 +2252,7 @@ public class SignalServiceMessageSender {
return sendWithFallback.onErrorResumeNext(t -> {
if (cancelationSignal != null && cancelationSignal.isCanceled()) {
return Single.error(new CancelationException());
return Single.just(SendMessageResult.canceledFailure(recipient));
}
if (retryCount >= RETRY_COUNT) {

View File

@@ -20,33 +20,38 @@ public class SendMessageResult {
private final ProofRequiredException proofRequiredFailure;
private final RateLimitException rateLimitFailure;
private final boolean invalidPreKeyFailure;
private final boolean canceledFailure;
public static SendMessageResult success(SignalServiceAddress address, List<Integer> devices, boolean unidentified, boolean needsSync, long duration, Optional<Content> content) {
return new SendMessageResult(address, new Success(unidentified, needsSync, duration, content, devices), false, false, null, null, null, false);
return new SendMessageResult(address, new Success(unidentified, needsSync, duration, content, devices), false, false, null, null, null, false, false);
}
public static SendMessageResult networkFailure(SignalServiceAddress address) {
return new SendMessageResult(address, null, true, false, null, null, null, false);
return new SendMessageResult(address, null, true, false, null, null, null, false, false);
}
public static SendMessageResult unregisteredFailure(SignalServiceAddress address) {
return new SendMessageResult(address, null, false, true, null, null, null, false);
return new SendMessageResult(address, null, false, true, null, null, null, false, false);
}
public static SendMessageResult identityFailure(SignalServiceAddress address, IdentityKey identityKey) {
return new SendMessageResult(address, null, false, false, new IdentityFailure(identityKey), null, null, false);
return new SendMessageResult(address, null, false, false, new IdentityFailure(identityKey), null, null, false, false);
}
public static SendMessageResult proofRequiredFailure(SignalServiceAddress address, ProofRequiredException proofRequiredException) {
return new SendMessageResult(address, null, false, false, null, proofRequiredException, null, false);
return new SendMessageResult(address, null, false, false, null, proofRequiredException, null, false, false);
}
public static SendMessageResult rateLimitFailure(SignalServiceAddress address, RateLimitException rateLimitException) {
return new SendMessageResult(address, null, false, false, null, null, rateLimitException, false);
return new SendMessageResult(address, null, false, false, null, null, rateLimitException, false, false);
}
public static SendMessageResult invalidPreKeyFailure(SignalServiceAddress address) {
return new SendMessageResult(address, null, false, false, null, null, null, true);
return new SendMessageResult(address, null, false, false, null, null, null, true, false);
}
public static SendMessageResult canceledFailure(SignalServiceAddress address) {
return new SendMessageResult(address, null, false, false, null, null, null, false, true);
}
public SignalServiceAddress getAddress() {
@@ -85,6 +90,10 @@ public class SendMessageResult {
return invalidPreKeyFailure;
}
public boolean isCanceledFailure() {
return canceledFailure;
}
private SendMessageResult(SignalServiceAddress address,
Success success,
boolean networkFailure,
@@ -92,7 +101,8 @@ public class SendMessageResult {
IdentityFailure identityFailure,
ProofRequiredException proofRequiredFailure,
RateLimitException rateLimitFailure,
boolean invalidPreKeyFailure)
boolean invalidPreKeyFailure,
boolean canceledFailure)
{
this.address = address;
this.success = success;
@@ -102,6 +112,7 @@ public class SendMessageResult {
this.proofRequiredFailure = proofRequiredFailure;
this.rateLimitFailure = rateLimitFailure;
this.invalidPreKeyFailure = invalidPreKeyFailure;
this.canceledFailure = canceledFailure;
}
public static class Success {

View File

@@ -19,7 +19,6 @@ import org.signal.libsignal.sgxsession.SgxCommunicationFailureException
import org.signal.libsignal.svr.DataMissingException
import org.signal.libsignal.svr.InvalidSvrBDataException
import org.signal.libsignal.svr.RestoreFailedException
import org.whispersystems.signalservice.api.CancelationException
import org.whispersystems.signalservice.api.NetworkResult
import org.whispersystems.signalservice.api.backup.MessageBackupKey
import org.whispersystems.signalservice.internal.push.AuthCredentials
@@ -71,8 +70,6 @@ class SvrBApi(private val network: Network) {
is SgxCommunicationFailureException -> StoreResult.SvrError(exception)
else -> StoreResult.UnknownError(exception)
}
} catch (e: CancelationException) {
StoreResult.UnknownError(e)
} catch (e: ExecutionException) {
StoreResult.UnknownError(e)
} catch (e: InterruptedException) {
@@ -107,8 +104,6 @@ class SvrBApi(private val network: Network) {
is SgxCommunicationFailureException -> RestoreResult.SvrError(exception)
else -> RestoreResult.UnknownError(exception)
}
} catch (e: CancelationException) {
RestoreResult.UnknownError(e)
} catch (e: ExecutionException) {
RestoreResult.UnknownError(e)
} catch (e: InterruptedException) {