Fix crash when handling call messaging failures.

This commit is contained in:
Cody Henthorne 2020-11-05 15:56:56 -05:00 committed by GitHub
parent 2b4a4d6109
commit 349a2f72cb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 61 additions and 22 deletions

View file

@ -451,7 +451,8 @@ public class WebRtcCallActivity extends AppCompatActivity implements SafetyNumbe
public void onSendAnywayAfterSafetyNumberChange() { public void onSendAnywayAfterSafetyNumberChange() {
Intent intent = new Intent(WebRtcCallActivity.this, WebRtcCallService.class); Intent intent = new Intent(WebRtcCallActivity.this, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_OUTGOING_CALL) intent.setAction(WebRtcCallService.ACTION_OUTGOING_CALL)
.putExtra(WebRtcCallService.EXTRA_REMOTE_PEER, new RemotePeer(viewModel.getRecipient().getId())); .putExtra(WebRtcCallService.EXTRA_REMOTE_PEER, new RemotePeer(viewModel.getRecipient().getId()))
.putExtra(WebRtcCallService.EXTRA_OFFER_TYPE, OfferMessage.Type.AUDIO_CALL.getCode());
startService(intent); startService(intent);
} }

View file

@ -35,7 +35,14 @@ public class WebRtcViewModel {
// Multiring Hangup States // Multiring Hangup States
CALL_ACCEPTED_ELSEWHERE, CALL_ACCEPTED_ELSEWHERE,
CALL_DECLINED_ELSEWHERE, CALL_DECLINED_ELSEWHERE,
CALL_ONGOING_ELSEWHERE CALL_ONGOING_ELSEWHERE;
public boolean isErrorState() {
return this == NETWORK_FAILURE ||
this == RECIPIENT_UNAVAILABLE ||
this == NO_SUCH_USER ||
this == UNTRUSTED_IDENTITY;
}
} }
private final @NonNull State state; private final @NonNull State state;

View file

@ -24,6 +24,7 @@ import org.signal.ringrtc.IceCandidate;
import org.signal.ringrtc.Remote; import org.signal.ringrtc.Remote;
import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.WebRtcCallActivity; import org.thoughtcrime.securesms.WebRtcCallActivity;
import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
@ -52,9 +53,11 @@ import org.thoughtcrime.securesms.webrtc.locks.LockManager;
import org.whispersystems.libsignal.util.Pair; import org.whispersystems.libsignal.util.Pair;
import org.whispersystems.signalservice.api.SignalServiceAccountManager; import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.calls.HangupMessage; import org.whispersystems.signalservice.api.messages.calls.HangupMessage;
import org.whispersystems.signalservice.api.messages.calls.OfferMessage; import org.whispersystems.signalservice.api.messages.calls.OfferMessage;
import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage; import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage;
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -97,7 +100,8 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
public static final String EXTRA_ENABLE = "enable_value"; public static final String EXTRA_ENABLE = "enable_value";
public static final String EXTRA_BROADCAST = "broadcast"; public static final String EXTRA_BROADCAST = "broadcast";
public static final String EXTRA_ANSWER_WITH_VIDEO = "enable_video"; public static final String EXTRA_ANSWER_WITH_VIDEO = "enable_video";
public static final String EXTRA_ERROR = "error"; public static final String EXTRA_ERROR_CALL_STATE = "error_call_state";
public static final String EXTRA_ERROR_IDENTITY_KEY = "remote_identity_key";
public static final String EXTRA_CAMERA_STATE = "camera_state"; public static final String EXTRA_CAMERA_STATE = "camera_state";
public static final String EXTRA_IS_ALWAYS_TURN = "is_always_turn"; public static final String EXTRA_IS_ALWAYS_TURN = "is_always_turn";
public static final String EXTRA_TURN_SERVER_INFO = "turn_server_info"; public static final String EXTRA_TURN_SERVER_INFO = "turn_server_info";
@ -580,10 +584,22 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
@Override @Override
public void onFailureContinue(@Nullable Throwable error) { public void onFailureContinue(@Nullable Throwable error) {
Log.i(TAG, "onFailureContinue: ", error);
Intent intent = new Intent(WebRtcCallService.this, WebRtcCallService.class); Intent intent = new Intent(WebRtcCallService.this, WebRtcCallService.class);
intent.setAction(ACTION_MESSAGE_SENT_ERROR) intent.setAction(ACTION_MESSAGE_SENT_ERROR)
.putExtra(EXTRA_CALL_ID, getCallId().longValue()) .putExtra(EXTRA_CALL_ID, getCallId().longValue());
.putExtra(EXTRA_ERROR, error);
WebRtcViewModel.State state = WebRtcViewModel.State.NETWORK_FAILURE;
if (error instanceof UntrustedIdentityException) {
intent.putExtra(EXTRA_ERROR_IDENTITY_KEY, new IdentityKeyParcelable(((UntrustedIdentityException) error).getIdentityKey()));
state = WebRtcViewModel.State.UNTRUSTED_IDENTITY;
} else if (error instanceof UnregisteredUserException) {
state = WebRtcViewModel.State.NO_SUCH_USER;
}
intent.putExtra(EXTRA_ERROR_CALL_STATE, state);
startService(intent); startService(intent);
} }
@ -603,7 +619,6 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
if (serviceState.getCallInfoState().getPeer(remotePeer.hashCode()) == null) { if (serviceState.getCallInfoState().getPeer(remotePeer.hashCode()) == null) {
Log.w(TAG, "remotePeer not found in map with key: " + remotePeer.hashCode() + "! Dropping."); Log.w(TAG, "remotePeer not found in map with key: " + remotePeer.hashCode() + "! Dropping.");
try { try {
//noinspection ConstantConditions
callManager.drop(callId); callManager.drop(callId);
} catch (CallException e) { } catch (CallException e) {
serviceState = serviceState.getActionProcessor().callFailure(serviceState, "callManager.drop() failed: ", e); serviceState = serviceState.getActionProcessor().callFailure(serviceState, "callManager.drop() failed: ", e);

View file

@ -232,7 +232,7 @@ public class ActiveCallActionProcessorDelegate extends WebRtcActionProcessor {
protected @NonNull WebRtcServiceState handleEnded(@NonNull WebRtcServiceState currentState, @NonNull String action, @NonNull RemotePeer remotePeer) { protected @NonNull WebRtcServiceState handleEnded(@NonNull WebRtcServiceState currentState, @NonNull String action, @NonNull RemotePeer remotePeer) {
Log.i(tag, "handleEnded(): call_id: " + remotePeer.getCallId() + " action: " + action); Log.i(tag, "handleEnded(): call_id: " + remotePeer.getCallId() + " action: " + action);
if (remotePeer.callIdEquals(currentState.getCallInfoState().getActivePeer())) { if (remotePeer.callIdEquals(currentState.getCallInfoState().getActivePeer()) && !currentState.getCallInfoState().getCallState().isErrorState()) {
currentState = currentState.builder() currentState = currentState.builder()
.changeCallInfoState() .changeCallInfoState()
.callState(WebRtcViewModel.State.NETWORK_FAILURE) .callState(WebRtcViewModel.State.NETWORK_FAILURE)

View file

@ -26,15 +26,14 @@ import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceStateBuilder
import org.thoughtcrime.securesms.util.TelephonyUtil; import org.thoughtcrime.securesms.util.TelephonyUtil;
import org.thoughtcrime.securesms.webrtc.locks.LockManager; import org.thoughtcrime.securesms.webrtc.locks.LockManager;
import org.webrtc.PeerConnection; import org.webrtc.PeerConnection;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.libsignal.InvalidKeyException;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.messages.calls.BusyMessage; import org.whispersystems.signalservice.api.messages.calls.BusyMessage;
import org.whispersystems.signalservice.api.messages.calls.HangupMessage; import org.whispersystems.signalservice.api.messages.calls.HangupMessage;
import org.whispersystems.signalservice.api.messages.calls.OfferMessage; import org.whispersystems.signalservice.api.messages.calls.OfferMessage;
import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage; import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage;
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@ -92,7 +91,6 @@ import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_WIRED_
import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_ANSWER_WITH_VIDEO; import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_ANSWER_WITH_VIDEO;
import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_BLUETOOTH; import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_BLUETOOTH;
import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_CAMERA_STATE; import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_CAMERA_STATE;
import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_ERROR;
import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_IS_ALWAYS_TURN; import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_IS_ALWAYS_TURN;
import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_MUTE; import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_MUTE;
import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_RESULT_RECEIVER; import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_RESULT_RECEIVER;
@ -104,6 +102,8 @@ import static org.thoughtcrime.securesms.service.webrtc.WebRtcIntentParser.getAv
import static org.thoughtcrime.securesms.service.webrtc.WebRtcIntentParser.getBroadcastFlag; import static org.thoughtcrime.securesms.service.webrtc.WebRtcIntentParser.getBroadcastFlag;
import static org.thoughtcrime.securesms.service.webrtc.WebRtcIntentParser.getCallId; import static org.thoughtcrime.securesms.service.webrtc.WebRtcIntentParser.getCallId;
import static org.thoughtcrime.securesms.service.webrtc.WebRtcIntentParser.getEnable; import static org.thoughtcrime.securesms.service.webrtc.WebRtcIntentParser.getEnable;
import static org.thoughtcrime.securesms.service.webrtc.WebRtcIntentParser.getErrorCallState;
import static org.thoughtcrime.securesms.service.webrtc.WebRtcIntentParser.getErrorIdentityKey;
import static org.thoughtcrime.securesms.service.webrtc.WebRtcIntentParser.getIceCandidates; import static org.thoughtcrime.securesms.service.webrtc.WebRtcIntentParser.getIceCandidates;
import static org.thoughtcrime.securesms.service.webrtc.WebRtcIntentParser.getIceServers; import static org.thoughtcrime.securesms.service.webrtc.WebRtcIntentParser.getIceServers;
import static org.thoughtcrime.securesms.service.webrtc.WebRtcIntentParser.getOfferMessageType; import static org.thoughtcrime.securesms.service.webrtc.WebRtcIntentParser.getOfferMessageType;
@ -173,7 +173,7 @@ public abstract class WebRtcActionProcessor {
case ACTION_LOCAL_HANGUP: return handleLocalHangup(currentState); case ACTION_LOCAL_HANGUP: return handleLocalHangup(currentState);
case ACTION_SEND_HANGUP: return handleSendHangup(currentState, CallMetadata.fromIntent(intent), HangupMetadata.fromIntent(intent), getBroadcastFlag(intent)); case ACTION_SEND_HANGUP: return handleSendHangup(currentState, CallMetadata.fromIntent(intent), HangupMetadata.fromIntent(intent), getBroadcastFlag(intent));
case ACTION_MESSAGE_SENT_SUCCESS: return handleMessageSentSuccess(currentState, getCallId(intent)); case ACTION_MESSAGE_SENT_SUCCESS: return handleMessageSentSuccess(currentState, getCallId(intent));
case ACTION_MESSAGE_SENT_ERROR: return handleMessageSentError(currentState, getCallId(intent), (Throwable) intent.getSerializableExtra(EXTRA_ERROR)); case ACTION_MESSAGE_SENT_ERROR: return handleMessageSentError(currentState, getCallId(intent), getErrorCallState(intent), getErrorIdentityKey(intent));
// Call Setup Actions // Call Setup Actions
case ACTION_RECEIVE_ICE_CANDIDATES: return handleReceivedIceCandidates(currentState, CallMetadata.fromIntent(intent), getIceCandidates(intent)); case ACTION_RECEIVE_ICE_CANDIDATES: return handleReceivedIceCandidates(currentState, CallMetadata.fromIntent(intent), getIceCandidates(intent));
@ -453,8 +453,11 @@ public abstract class WebRtcActionProcessor {
return currentState; return currentState;
} }
protected @NonNull WebRtcServiceState handleMessageSentError(@NonNull WebRtcServiceState currentState, @NonNull CallId callId, @Nullable Throwable error) { protected @NonNull WebRtcServiceState handleMessageSentError(@NonNull WebRtcServiceState currentState,
Log.w(tag, error); @NonNull CallId callId,
@NonNull WebRtcViewModel.State errorCallState,
@NonNull Optional<IdentityKey> identityKey) {
Log.w(tag, "handleMessageSentError():");
try { try {
webRtcInteractor.getCallManager().messageSendFailure(callId); webRtcInteractor.getCallManager().messageSendFailure(callId);
@ -469,21 +472,17 @@ public abstract class WebRtcActionProcessor {
WebRtcServiceStateBuilder builder = currentState.builder(); WebRtcServiceStateBuilder builder = currentState.builder();
if (error instanceof UntrustedIdentityException) { if (errorCallState == WebRtcViewModel.State.UNTRUSTED_IDENTITY) {
CallParticipant participant = Objects.requireNonNull(currentState.getCallInfoState().getRemoteParticipant(activePeer.getRecipient())); CallParticipant participant = Objects.requireNonNull(currentState.getCallInfoState().getRemoteParticipant(activePeer.getRecipient()));
CallParticipant untrusted = participant.withIdentityKey(((UntrustedIdentityException) error).getIdentityKey()); CallParticipant untrusted = participant.withIdentityKey(identityKey.get());
builder.changeCallInfoState() builder.changeCallInfoState()
.callState(WebRtcViewModel.State.UNTRUSTED_IDENTITY) .callState(WebRtcViewModel.State.UNTRUSTED_IDENTITY)
.putParticipant(activePeer.getRecipient(), untrusted) .putParticipant(activePeer.getRecipient(), untrusted)
.commit(); .commit();
} else if (error instanceof UnregisteredUserException) { } else {
builder.changeCallInfoState() builder.changeCallInfoState()
.callState(WebRtcViewModel.State.NO_SUCH_USER) .callState(errorCallState)
.commit();
} else if (error instanceof IOException) {
builder.changeCallInfoState()
.callState(WebRtcViewModel.State.NETWORK_FAILURE)
.commit(); .commit();
} }

View file

@ -6,6 +6,8 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import org.signal.ringrtc.CallId; import org.signal.ringrtc.CallId;
import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
import org.thoughtcrime.securesms.events.WebRtcViewModel;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.ringrtc.IceCandidateParcel; import org.thoughtcrime.securesms.ringrtc.IceCandidateParcel;
import org.thoughtcrime.securesms.ringrtc.RemotePeer; import org.thoughtcrime.securesms.ringrtc.RemotePeer;
@ -13,6 +15,8 @@ import org.thoughtcrime.securesms.ringrtc.TurnServerInfoParcel;
import org.thoughtcrime.securesms.service.WebRtcCallService; import org.thoughtcrime.securesms.service.WebRtcCallService;
import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceState; import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceState;
import org.webrtc.PeerConnection; import org.webrtc.PeerConnection;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.messages.calls.OfferMessage; import org.whispersystems.signalservice.api.messages.calls.OfferMessage;
import java.util.ArrayList; import java.util.ArrayList;
@ -26,6 +30,8 @@ import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_AVAILAB
import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_BROADCAST; import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_BROADCAST;
import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_CALL_ID; import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_CALL_ID;
import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_ENABLE; import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_ENABLE;
import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_ERROR_CALL_STATE;
import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_ERROR_IDENTITY_KEY;
import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_ICE_CANDIDATES; import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_ICE_CANDIDATES;
import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_MULTI_RING; import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_MULTI_RING;
import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_OFFER_OPAQUE; import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_OFFER_OPAQUE;
@ -143,4 +149,15 @@ public final class WebRtcIntentParser {
return intent.getBooleanExtra(EXTRA_ENABLE, false); return intent.getBooleanExtra(EXTRA_ENABLE, false);
} }
public static @NonNull WebRtcViewModel.State getErrorCallState(@NonNull Intent intent) {
return (WebRtcViewModel.State) Objects.requireNonNull(intent.getSerializableExtra(EXTRA_ERROR_CALL_STATE));
}
public static @NonNull Optional<IdentityKey> getErrorIdentityKey(@NonNull Intent intent) {
IdentityKeyParcelable identityKeyParcelable = (IdentityKeyParcelable) intent.getParcelableExtra(EXTRA_ERROR_IDENTITY_KEY);
if (identityKeyParcelable != null) {
return Optional.fromNullable(identityKeyParcelable.get());
}
return Optional.absent();
}
} }