Use libsignal-net for multi-recipient send.

This commit is contained in:
Cody Henthorne
2026-03-13 10:51:30 -04:00
committed by Michelle Tang
parent 6c1897d8d5
commit 4d2f23ec37
17 changed files with 240 additions and 249 deletions

View File

@@ -147,6 +147,11 @@ public class GroupCallUpdateSendJob extends BaseJob {
e instanceof RetryLaterException;
}
@Override
public long getNextRunAttemptBackoff(int pastAttemptCount, @NonNull Exception exception) {
return SendJobUtil.getBackoffMillisFromException(this, TAG, pastAttemptCount, exception, () -> super.getNextRunAttemptBackoff(pastAttemptCount, exception));
}
@Override
public void onFailure() {
if (recipients.size() < initialRecipientCount) {

View File

@@ -165,6 +165,11 @@ public class ProfileKeySendJob extends BaseJob {
e instanceof RetryLaterException;
}
@Override
public long getNextRunAttemptBackoff(int pastAttemptCount, @NonNull Exception exception) {
return SendJobUtil.getBackoffMillisFromException(this, TAG, pastAttemptCount, exception, () -> super.getNextRunAttemptBackoff(pastAttemptCount, exception));
}
@Override
public @Nullable byte[] serialize() {
return new JsonJobData.Builder()

View File

@@ -9,14 +9,17 @@ import androidx.annotation.WorkerThread;
import com.annimon.stream.Collectors;
import com.annimon.stream.Stream;
import org.signal.core.models.ServiceId;
import org.signal.core.models.ServiceId.ACI;
import org.signal.core.util.Base64;
import org.signal.core.util.logging.Log;
import org.signal.storageservice.storage.protos.groups.local.DecryptedGroup;
import org.thoughtcrime.securesms.database.RecipientTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.SealedSenderConstraint;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.jobmanager.impl.SealedSenderConstraint;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.messages.GroupSendUtil;
import org.thoughtcrime.securesms.mms.MessageGroupContext;
@@ -25,7 +28,6 @@ import org.thoughtcrime.securesms.net.NotPushRegisteredException;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.signal.core.util.Base64;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.signalservice.api.crypto.ContentHint;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
@@ -33,8 +35,6 @@ import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil;
import org.whispersystems.signalservice.api.messages.SendMessageResult;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2;
import org.signal.core.models.ServiceId;
import org.signal.core.models.ServiceId.ACI;
import org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException;
import org.whispersystems.signalservice.internal.push.GroupContextV2;
@@ -168,6 +168,11 @@ public final class PushGroupSilentUpdateSendJob extends BaseJob {
e instanceof RetryLaterException;
}
@Override
public long getNextRunAttemptBackoff(int pastAttemptCount, @NonNull Exception exception) {
return SendJobUtil.getBackoffMillisFromException(this, TAG, pastAttemptCount, exception, () -> super.getNextRunAttemptBackoff(pastAttemptCount, exception));
}
@Override
public void onFailure() {
Log.w(TAG, "Failed to send remote delete to all recipients! (" + (initialRecipientCount - recipients.size() + "/" + initialRecipientCount + ")") );

View File

@@ -72,6 +72,7 @@ import org.signal.core.models.ServiceId.ACI;
import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException;
import org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException;
import org.whispersystems.signalservice.api.push.exceptions.RateLimitException;
import org.whispersystems.signalservice.api.push.exceptions.RetryNetworkException;
import org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException;
import org.whispersystems.signalservice.internal.push.BodyRange;
@@ -160,29 +161,7 @@ public abstract class PushSendJob extends SendJob {
@Override
public long getNextRunAttemptBackoff(int pastAttemptCount, @NonNull Exception exception) {
if (exception instanceof ProofRequiredException) {
long backoff = ((ProofRequiredException) exception).getRetryAfterSeconds();
warn(TAG, "[Proof Required] Retry-After is " + backoff + " seconds.");
if (backoff >= 0) {
return TimeUnit.SECONDS.toMillis(backoff);
}
} else if (exception instanceof RateLimitException) {
long backoff = ((RateLimitException) exception).getRetryAfterMilliseconds().orElse(-1L);
if (backoff >= 0) {
return backoff;
}
} else if (exception instanceof NonSuccessfulResponseCodeException) {
if (((NonSuccessfulResponseCodeException) exception).is5xx()) {
return BackoffUtil.exponentialBackoff(pastAttemptCount, RemoteConfig.getServerErrorMaxBackoff());
}
} else if (exception instanceof RetryLaterException) {
long backoff = ((RetryLaterException) exception).getBackoff();
if (backoff >= 0) {
return backoff;
}
}
return super.getNextRunAttemptBackoff(pastAttemptCount, exception);
return SendJobUtil.getBackoffMillisFromException(this, TAG, pastAttemptCount, exception, () -> super.getNextRunAttemptBackoff(pastAttemptCount, exception));
}
protected Optional<byte[]> getProfileKey(@NonNull Recipient recipient) {

View File

@@ -15,8 +15,8 @@ import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.ReactionRecord;
import org.thoughtcrime.securesms.dependencies.AppDependencies;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.jobmanager.impl.SealedSenderConstraint;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
@@ -193,6 +193,11 @@ public class ReactionSendJob extends BaseJob {
e instanceof RetryLaterException;
}
@Override
public long getNextRunAttemptBackoff(int pastAttemptCount, @NonNull Exception exception) {
return SendJobUtil.getBackoffMillisFromException(this, TAG, pastAttemptCount, exception, () -> super.getNextRunAttemptBackoff(pastAttemptCount, exception));
}
@Override
public void onFailure() {
if (recipients.size() < initialRecipientCount) {

View File

@@ -7,6 +7,7 @@ import androidx.annotation.WorkerThread;
import com.annimon.stream.Stream;
import org.signal.core.util.SetUtil;
import org.signal.core.util.Util;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.database.MessageTable;
import org.thoughtcrime.securesms.database.NoSuchMessageException;
@@ -17,9 +18,9 @@ import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import org.thoughtcrime.securesms.dependencies.AppDependencies;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.jobmanager.impl.SealedSenderConstraint;
import org.thoughtcrime.securesms.messages.GroupSendUtil;
import org.thoughtcrime.securesms.net.NotPushRegisteredException;
@@ -28,7 +29,6 @@ import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.signal.core.util.Util;
import org.whispersystems.signalservice.api.crypto.ContentHint;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.SendMessageResult;
@@ -198,6 +198,11 @@ public class RemoteDeleteSendJob extends BaseJob {
e instanceof RetryLaterException;
}
@Override
public long getNextRunAttemptBackoff(int pastAttemptCount, @NonNull Exception exception) {
return SendJobUtil.getBackoffMillisFromException(this, TAG, pastAttemptCount, exception, () -> super.getNextRunAttemptBackoff(pastAttemptCount, exception));
}
@Override
public void onFailure() {
Log.w(TAG, "Failed to send remote delete to all recipients! (" + (initialRecipientCount - recipients.size() + "/" + initialRecipientCount + ")") );

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2026 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
@file:JvmName("SendJobUtil")
package org.thoughtcrime.securesms.jobs
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.jobmanager.Job
import org.thoughtcrime.securesms.jobmanager.JobLogger
import org.thoughtcrime.securesms.jobmanager.impl.BackoffUtil
import org.thoughtcrime.securesms.transport.RetryLaterException
import org.thoughtcrime.securesms.util.RemoteConfig.serverErrorMaxBackoff
import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException
import org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException
import org.whispersystems.signalservice.api.push.exceptions.RateLimitException
import org.whispersystems.signalservice.api.push.exceptions.RetryNetworkException
import java.util.concurrent.TimeUnit
import org.signal.libsignal.net.RetryLaterException as LibSignalRetryLaterException
fun Job.getBackoffMillisFromException(tag: String, pastAttemptCount: Int, exception: Exception, default: () -> Long): Long {
when (exception) {
is ProofRequiredException -> {
val backoff = exception.retryAfterSeconds
Log.w(tag, JobLogger.format(this, "[Proof Required] Retry-After is $backoff seconds."))
if (backoff >= 0) {
return TimeUnit.SECONDS.toMillis(backoff)
}
}
is RateLimitException -> {
val backoff = exception.retryAfterMilliseconds.orElse(-1L)
if (backoff >= 0) {
return backoff
}
}
is NonSuccessfulResponseCodeException -> {
if (exception.is5xx()) {
return BackoffUtil.exponentialBackoff(pastAttemptCount, serverErrorMaxBackoff)
}
}
is LibSignalRetryLaterException -> {
return exception.duration.toMillis()
}
is RetryNetworkException -> {
return exception.retryAfterMs
}
is RetryLaterException -> {
val backoff = exception.backoff
if (backoff >= 0) {
return backoff
}
}
}
return default()
}