Ensure rate limit dialog appears during calls.

This commit is contained in:
Alex Hart 2024-10-23 14:15:42 -03:00 committed by GitHub
parent 6673293e29
commit 9fa04e03fd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 253 additions and 91 deletions

View file

@ -91,8 +91,11 @@ import org.thoughtcrime.securesms.components.webrtc.v2.CallPermissionsDialogCont
import org.thoughtcrime.securesms.conversation.ui.error.SafetyNumberChangeDialog;
import org.thoughtcrime.securesms.dependencies.AppDependencies;
import org.thoughtcrime.securesms.events.WebRtcViewModel;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.messagerequests.CalleeMustAcceptMessageRequestActivity;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.ratelimit.RecaptchaProofBottomSheetFragment;
import org.thoughtcrime.securesms.ratelimit.RecaptchaRequiredEvent;
import org.thoughtcrime.securesms.reactions.any.ReactWithAnyEmojiBottomSheetDialogFragment;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
@ -116,7 +119,6 @@ import org.whispersystems.signalservice.api.messages.calls.HangupMessage;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@ -127,7 +129,7 @@ import io.reactivex.rxjava3.disposables.Disposable;
import static org.thoughtcrime.securesms.components.sensors.Orientation.PORTRAIT_BOTTOM_EDGE;
public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChangeDialog.Callback, ReactWithAnyEmojiBottomSheetDialogFragment.Callback {
public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChangeDialog.Callback, ReactWithAnyEmojiBottomSheetDialogFragment.Callback, RecaptchaProofBottomSheetFragment.Callback {
private static final String TAG = Log.tag(WebRtcCallActivity.class);
@ -263,6 +265,8 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
public void onResume() {
Log.i(TAG, "onResume()");
super.onResume();
EventBus.getDefault().register(this);
initializeScreenshotSecurity();
if (!EventBus.getDefault().isRegistered(this)) {
@ -287,6 +291,10 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
enterPipOnResume = false;
enterPipModeIfPossible();
}
if (SignalStore.rateLimit().needsRecaptcha()) {
RecaptchaProofBottomSheetFragment.show(getSupportFragmentManager());
}
}
@Override
@ -303,6 +311,8 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
Log.i(TAG, "onPause");
super.onPause();
EventBus.getDefault().unregister(this);
if (!callPermissionsDialogController.isAskingForPermission() && !viewModel.isCallStarting() && !isChangingConfigurations()) {
CallParticipantsState state = viewModel.getCallParticipantsStateSnapshot();
if (state != null && state.getCallState().isPreJoinOrNetworkUnavailable()) {
@ -345,6 +355,11 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
EventBus.getDefault().unregister(this);
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onRecaptchaRequiredEvent(RecaptchaRequiredEvent recaptchaRequiredEvent) {
RecaptchaProofBottomSheetFragment.show(getSupportFragmentManager());
}
@SuppressLint("MissingSuperCall")
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
@ -1071,6 +1086,11 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
callOverflowPopupWindow.dismiss();
}
@Override
public void onProofCompleted() {
AppDependencies.getSignalCallManager().resendMediaKeys();
}
private final class ControlsListener implements WebRtcCallView.ControlsListener {
@Override

View file

@ -26,6 +26,7 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.OutgoingMessage;
import org.thoughtcrime.securesms.ratelimit.ProofRequiredExceptionHandler;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
@ -215,7 +216,12 @@ public class IndividualSendJob extends PushSendJob {
database.markAsSentFailed(messageId);
RetrieveProfileJob.enqueue(recipientId);
} catch (ProofRequiredException e) {
handleProofRequiredException(context, e, SignalDatabase.threads().getRecipientForThreadId(threadId), threadId, messageId, true);
ProofRequiredExceptionHandler.Result result = ProofRequiredExceptionHandler.handle(context, e, SignalDatabase.threads().getRecipientForThreadId(threadId), threadId, messageId);
if (result.isRetry()) {
throw new RetryLaterException();
} else {
throw e;
}
}
SignalLocalMetrics.IndividualMessageSend.onJobFinished(messageId);

View file

@ -14,6 +14,7 @@ import org.thoughtcrime.securesms.jobmanager.impl.DecryptionsDrainedConstraint;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.messages.GroupSendUtil;
import org.thoughtcrime.securesms.net.NotPushRegisteredException;
import org.thoughtcrime.securesms.ratelimit.ProofRequiredExceptionHandler;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
@ -22,6 +23,7 @@ import org.whispersystems.signalservice.api.crypto.ContentHint;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.SendMessageResult;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException;
import org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException;
import java.io.IOException;
@ -170,7 +172,8 @@ public class ProfileKeySendJob extends BaseJob {
.withTimestamp(System.currentTimeMillis())
.withProfileKey(Recipient.self().resolve().getProfileKey());
List<SendMessageResult> results = GroupSendUtil.sendUnresendableDataMessage(context, null, destinations, false, ContentHint.IMPLICIT, dataMessage.build(), false);
List<SendMessageResult> results = GroupSendUtil.sendUnresendableDataMessage(context, null, destinations, false, ContentHint.IMPLICIT, dataMessage.build(), false);
ProofRequiredException proofRequired = Stream.of(results).filter(r -> r.getProofRequiredFailure() != null).findLast().map(SendMessageResult::getProofRequiredFailure).orElse(null);
GroupSendJobHelper.SendResult groupResult = GroupSendJobHelper.getCompletedSends(destinations, results);
@ -178,6 +181,11 @@ public class ProfileKeySendJob extends BaseJob {
SignalDatabase.recipients().markUnregistered(unregistered);
}
if (proofRequired != null) {
Log.d(TAG, "Notifying the user they were rate limited.");
ProofRequiredExceptionHandler.handle(context, proofRequired, null, -1L, -1L);
}
return groupResult.completed;
}

View file

@ -37,6 +37,7 @@ import org.thoughtcrime.securesms.messages.StorySendUtil;
import org.thoughtcrime.securesms.mms.MessageGroupContext;
import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.OutgoingMessage;
import org.thoughtcrime.securesms.ratelimit.ProofRequiredExceptionHandler;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
@ -456,7 +457,12 @@ public final class PushGroupSendJob extends PushSendJob {
SignalDatabase.groupReceipts().setUnidentified(successUnidentifiedStatus, messageId);
if (proofRequired != null) {
handleProofRequiredException(context, proofRequired, groupRecipient, threadId, messageId, true);
ProofRequiredExceptionHandler.Result result = ProofRequiredExceptionHandler.handle(context, proofRequired, groupRecipient, threadId, messageId);
if (result.isRetry()) {
throw new RetryLaterException();
} else {
throw proofRequired;
}
}
if (existingNetworkFailures.isEmpty() && existingIdentityMismatches.isEmpty()) {

View file

@ -575,84 +575,6 @@ public abstract class PushSendJob extends SendJob {
}
}
protected static void handleProofRequiredException(@NonNull Context context, @NonNull ProofRequiredException proofRequired, @Nullable Recipient recipient, long threadId, long messageId, boolean isMms)
throws ProofRequiredException, RetryLaterException
{
Log.w(TAG, "[Proof Required] Options: " + proofRequired.getOptions());
try {
if (proofRequired.getOptions().contains(ProofRequiredException.Option.PUSH_CHALLENGE)) {
AppDependencies.getSignalServiceAccountManager().requestRateLimitPushChallenge();
Log.i(TAG, "[Proof Required] Successfully requested a challenge. Waiting up to " + PUSH_CHALLENGE_TIMEOUT + " ms.");
boolean success = new PushChallengeRequest(PUSH_CHALLENGE_TIMEOUT).blockUntilSuccess();
if (success) {
Log.i(TAG, "Successfully responded to a push challenge. Retrying message send.");
throw new RetryLaterException(1);
} else {
Log.w(TAG, "Failed to respond to the push challenge in time. Falling back.");
}
}
} catch (NonSuccessfulResponseCodeException e) {
Log.w(TAG, "[Proof Required] Could not request a push challenge (" + e.getCode() + "). Falling back.", e);
} catch (IOException e) {
Log.w(TAG, "[Proof Required] Network error when requesting push challenge. Retrying later.");
throw new RetryLaterException(e);
}
Log.w(TAG, "[Proof Required] Marking message as rate-limited. (id: " + messageId + ", mms: " + isMms + ", thread: " + threadId + ")");
if (isMms) {
SignalDatabase.messages().markAsRateLimited(messageId);
} else {
SignalDatabase.messages().markAsRateLimited(messageId);
}
if (proofRequired.getOptions().contains(ProofRequiredException.Option.CAPTCHA)) {
Log.i(TAG, "[Proof Required] CAPTCHA required.");
SignalStore.rateLimit().markNeedsRecaptcha(proofRequired.getToken());
if (recipient != null) {
ParentStoryId.GroupReply groupReply = SignalDatabase.messages().getParentStoryIdForGroupReply(messageId);
AppDependencies.getMessageNotifier().notifyProofRequired(context, recipient, ConversationId.fromThreadAndReply(threadId, groupReply));
} else {
Log.w(TAG, "[Proof Required] No recipient! Couldn't notify.");
}
}
throw proofRequired;
}
protected abstract void onPushSend() throws Exception;
public static class PushChallengeRequest {
private final long timeout;
private final CountDownLatch latch;
private final EventBus eventBus;
private PushChallengeRequest(long timeout) {
this.timeout = timeout;
this.latch = new CountDownLatch(1);
this.eventBus = EventBus.getDefault();
}
public boolean blockUntilSuccess() {
eventBus.register(this);
try {
return latch.await(timeout, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Log.w(TAG, "[Proof Required] Interrupted?", e);
return false;
} finally {
eventBus.unregister(this);
}
}
@Subscribe(threadMode = ThreadMode.POSTING)
public void onSuccessReceived(SubmitRateLimitPushChallengeJob.SuccessEvent event) {
Log.i(TAG, "[Proof Required] Received a successful result!");
latch.countDown();
}
}
}

View file

@ -0,0 +1,128 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.ratelimit
import android.content.Context
import androidx.annotation.WorkerThread
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.model.ParentStoryId
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.jobs.SubmitRateLimitPushChallengeJob.SuccessEvent
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.notifications.v2.ConversationId
import org.thoughtcrime.securesms.recipients.Recipient
import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException
import org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException
import java.io.IOException
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
/**
* Reusable ProofRequiredException handling code.
*/
object ProofRequiredExceptionHandler {
private val TAG = Log.tag(ProofRequiredExceptionHandler::class)
private val PUSH_CHALLENGE_TIMEOUT: Duration = 10.seconds
/**
* Handles the given exception, updating state as necessary.
*/
@JvmStatic
@WorkerThread
fun handle(context: Context, proofRequired: ProofRequiredException, recipient: Recipient?, threadId: Long, messageId: Long): Result {
Log.w(TAG, "[Proof Required] Options: ${proofRequired.options}")
try {
if (ProofRequiredException.Option.PUSH_CHALLENGE in proofRequired.options) {
AppDependencies.signalServiceAccountManager.requestRateLimitPushChallenge()
Log.i(TAG, "[Proof Required] Successfully requested a challenge. Waiting up to $PUSH_CHALLENGE_TIMEOUT ms.")
val success = PushChallengeRequest(PUSH_CHALLENGE_TIMEOUT).blockUntilSuccess()
if (success) {
Log.i(TAG, "Successfully responded to a push challenge. Retrying message send.")
return Result.RETRY_NOW
} else {
Log.w(TAG, "Failed to respond to the push challeng in time. Falling back.")
}
}
} catch (e: NonSuccessfulResponseCodeException) {
Log.w(TAG, "[Proof Required] Could not request a push challenge (${e.code}). Falling back.", e)
} catch (e: IOException) {
Log.w(TAG, "[Proof Required] Network error when requesting push challenge. Retrying later.")
return Result.RETRY_LATER
}
if (messageId > 0) {
Log.w(TAG, "[Proof Required] Marking message as rate-limited. (id: $messageId, thread: $threadId)")
SignalDatabase.messages.markAsRateLimited(messageId)
}
if (ProofRequiredException.Option.CAPTCHA in proofRequired.options) {
Log.i(TAG, "[Proof Required] CAPTCHA required.")
SignalStore.rateLimit.markNeedsRecaptcha(proofRequired.token)
if (recipient != null && messageId > -1L) {
val groupReply: ParentStoryId.GroupReply? = SignalDatabase.messages.getParentStoryIdForGroupReply(messageId)
AppDependencies.messageNotifier.notifyProofRequired(context, recipient, ConversationId.fromThreadAndReply(threadId, groupReply))
} else {
Log.w(TAG, "[Proof Required] No recipient! Couldn't notify.")
}
}
return Result.RETHROW
}
enum class Result {
/**
* The challenge was successful and the message send can be retried immediately.
*/
RETRY_NOW,
/**
* The challenge failed due to a network error and should be scheduled to retry with some offset.
*/
RETRY_LATER,
/**
* The caller should rethrow the original error.
*/
RETHROW;
fun isRetry() = this != RETHROW
}
private class PushChallengeRequest(val timeout: Duration) {
private val latch = CountDownLatch(1)
private val eventBus = EventBus.getDefault()
fun blockUntilSuccess(): Boolean {
eventBus.register(this)
return try {
latch.await(timeout.inWholeMilliseconds, TimeUnit.MILLISECONDS)
} catch (e: InterruptedException) {
Log.w(TAG, "[Proof Required] Interrupted?", e)
false
} finally {
eventBus.unregister(this)
}
}
@Subscribe(threadMode = ThreadMode.POSTING)
fun onSuccessReceived(event: SuccessEvent) {
Log.i(TAG, "[Proof Required] Received a successful result!")
latch.countDown()
}
}
}

View file

@ -10,8 +10,11 @@ import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
import androidx.activity.result.contract.ActivityResultContract;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.signal.core.util.concurrent.SimpleTask;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.BuildConfig;
import org.thoughtcrime.securesms.PassphraseRequiredActivity;
@ -20,7 +23,6 @@ import org.thoughtcrime.securesms.dependencies.AppDependencies;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.Util;
import org.signal.core.util.concurrent.SimpleTask;
import org.thoughtcrime.securesms.util.views.SimpleProgressDialog;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
@ -36,10 +38,6 @@ public class RecaptchaProofActivity extends PassphraseRequiredActivity {
private final DynamicTheme dynamicTheme = new DynamicTheme();
public static @NonNull Intent getIntent(@NonNull Context context) {
return new Intent(context, RecaptchaProofActivity.class);
}
@Override
protected void onPreCreate() {
dynamicTheme.onCreate(this);
@ -120,6 +118,7 @@ public class RecaptchaProofActivity extends PassphraseRequiredActivity {
if (result.clearState) {
Log.i(TAG, "Considering the response sufficient to clear the slate.");
SignalStore.rateLimit().onProofAccepted();
setResult(RESULT_OK);
}
if (!result.success) {
@ -140,4 +139,17 @@ public class RecaptchaProofActivity extends PassphraseRequiredActivity {
this.success = success;
}
}
public static class RecaptchaProofContract extends ActivityResultContract<Void, Boolean> {
@Override
public @NonNull Intent createIntent(@NonNull Context context, Void unused) {
return new Intent(context, RecaptchaProofActivity.class);
}
@Override
public Boolean parseResult(int resultCode, @Nullable Intent intent) {
return resultCode == RESULT_OK;
}
}
}

View file

@ -1,10 +1,12 @@
package org.thoughtcrime.securesms.ratelimit;
import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
@ -24,6 +26,8 @@ public final class RecaptchaProofBottomSheetFragment extends BottomSheetDialogFr
private static final String TAG = Log.tag(RecaptchaProofBottomSheetFragment.class);
private ActivityResultLauncher<Void> launcher;
public static void show(@NonNull FragmentManager manager) {
new RecaptchaProofBottomSheetFragment().show(manager, BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG);
}
@ -38,11 +42,25 @@ public final class RecaptchaProofBottomSheetFragment extends BottomSheetDialogFr
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.recaptcha_required_bottom_sheet, container, false);
view.findViewById(R.id.recaptcha_sheet_ok_button).setOnClickListener(v -> {
Activity activity = requireActivity();
final Callback callback;
if (activity instanceof Callback) {
callback = (Callback) activity;
} else {
callback = null;
}
launcher = registerForActivityResult(new RecaptchaProofActivity.RecaptchaProofContract(), (isOk) -> {
if (isOk && callback != null) {
callback.onProofCompleted();
}
dismissAllowingStateLoss();
startActivity(RecaptchaProofActivity.getIntent(requireContext()));
});
view.findViewById(R.id.recaptcha_sheet_ok_button).setOnClickListener(v -> launcher.launch(null));
return view;
}
@ -62,4 +80,12 @@ public final class RecaptchaProofBottomSheetFragment extends BottomSheetDialogFr
Log.i(TAG, "Ignoring repeat show.");
}
}
/**
* Optional callback interface to be invoked when the user successfully completes a push challenge.
* This is expected to be implemented on the activity which is displaying this fragment.
*/
public interface Callback {
void onProofCompleted();
}
}

View file

@ -331,4 +331,18 @@ public class GroupActionProcessor extends DeviceAwareActionProcessor {
return terminateGroupCall(currentState);
}
@Override
protected @NonNull WebRtcServiceState handleResendMediaKeys(@NonNull WebRtcServiceState currentState) {
GroupCall groupCall = currentState.getCallInfoState().getGroupCall();
if (groupCall != null) {
try {
currentState.getCallInfoState().getGroupCall().resendMediaKeys();
} catch (CallException e) {
return groupCallFailure(currentState, "Unable to resend media keys", e);
}
}
return currentState;
}
}

View file

@ -14,7 +14,6 @@ import androidx.annotation.Nullable;
import com.annimon.stream.Stream;
import org.greenrobot.eventbus.EventBus;
import org.signal.core.util.ListUtil;
import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log;
import org.signal.libsignal.protocol.util.Pair;
@ -56,6 +55,7 @@ import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.messages.GroupSendUtil;
import org.thoughtcrime.securesms.notifications.v2.ConversationId;
import org.thoughtcrime.securesms.ratelimit.ProofRequiredExceptionHandler;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
@ -85,6 +85,7 @@ import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMess
import org.whispersystems.signalservice.api.messages.calls.TurnServerInfo;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.push.ServiceId.ACI;
import org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException;
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
import org.whispersystems.signalservice.internal.push.SyncMessage;
@ -801,6 +802,11 @@ public final class SignalCallManager implements CallManager.Observer, GroupCall.
Log.i(TAG, "onSendCallMessage onFailure: ", e);
RetrieveProfileJob.enqueue(recipient.getId());
process((s, p) -> p.handleGroupMessageSentError(s, Collections.singletonList(recipient.getId()), UNTRUSTED_IDENTITY));
} catch (ProofRequiredException e) {
Log.i(TAG, "onSendCallMessage onFailure: ", e);
ProofRequiredExceptionHandler.handle(context, e, recipient, -1L, -1L);
process((s, p) -> p.handleResendMediaKeys(s));
process((s, p) -> p.handleGroupMessageSentError(s, Collections.singletonList(recipient.getId()), NETWORK_FAILURE));
} catch (IOException e) {
Log.i(TAG, "onSendCallMessage onFailure: ", e);
process((s, p) -> p.handleGroupMessageSentError(s, Collections.singletonList(recipient.getId()), NETWORK_FAILURE));
@ -1147,6 +1153,10 @@ public final class SignalCallManager implements CallManager.Observer, GroupCall.
isCallFull));
}
public void resendMediaKeys() {
process((s, p) -> p.handleResendMediaKeys(s));
}
public void sendCallMessage(@NonNull final RemotePeer remotePeer,
@NonNull final SignalServiceCallMessage callMessage)
{
@ -1170,6 +1180,11 @@ public final class SignalCallManager implements CallManager.Observer, GroupCall.
UNTRUSTED_IDENTITY,
Optional.ofNullable(e.getIdentityKey())));
} catch (IOException e) {
if (e instanceof ProofRequiredException) {
ProofRequiredExceptionHandler.handle(context, (ProofRequiredException) e, null, -1L, -1L);
process((s, p) -> p.handleResendMediaKeys(s));
}
processSendMessageFailureWithChangeDetection(remotePeer,
(s, p) -> p.handleMessageSentError(s,
remotePeer.getCallId(),

View file

@ -793,6 +793,11 @@ public abstract class WebRtcActionProcessor {
return currentState;
}
protected @NonNull WebRtcServiceState handleResendMediaKeys(@NonNull WebRtcServiceState currentState) {
Log.i(tag, "handleResendMediaKeys not processed");
return currentState;
}
protected @NonNull WebRtcServiceState handleReceivedOpaqueMessage(@NonNull WebRtcServiceState currentState, @NonNull WebRtcData.OpaqueMessageMetadata opaqueMessageMetadata) {
Log.i(tag, "handleReceivedOpaqueMessage():");