Add call disposition syncing.

This commit is contained in:
Cody Henthorne
2022-12-21 23:56:28 -05:00
committed by Greyson Parrelli
parent d471647e12
commit 06b414f4ef
23 changed files with 788 additions and 76 deletions

View File

@@ -0,0 +1,36 @@
package org.thoughtcrime.securesms.service.webrtc
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.ringrtc.RemotePeer
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.CallEvent
/**
* Helper for creating call event sync messages.
*/
object CallEventSyncMessageUtil {
@JvmStatic
fun createAcceptedSyncMessage(remotePeer: RemotePeer, timestamp: Long, isOutgoing: Boolean, isVideoCall: Boolean): CallEvent {
return CallEvent
.newBuilder()
.setPeerUuid(Recipient.resolved(remotePeer.id).requireServiceId().toByteString())
.setId(remotePeer.callId.longValue())
.setTimestamp(timestamp)
.setType(if (isVideoCall) CallEvent.Type.VIDEO_CALL else CallEvent.Type.AUDIO_CALL)
.setDirection(if (isOutgoing) CallEvent.Direction.OUTGOING else CallEvent.Direction.INCOMING)
.setEvent(CallEvent.Event.ACCEPTED)
.build()
}
@JvmStatic
fun createNotAcceptedSyncMessage(remotePeer: RemotePeer, timestamp: Long, isOutgoing: Boolean, isVideoCall: Boolean): CallEvent {
return CallEvent
.newBuilder()
.setPeerUuid(Recipient.resolved(remotePeer.id).requireServiceId().toByteString())
.setId(remotePeer.callId.longValue())
.setTimestamp(timestamp)
.setType(if (isVideoCall) CallEvent.Type.VIDEO_CALL else CallEvent.Type.AUDIO_CALL)
.setDirection(if (isOutgoing) CallEvent.Direction.OUTGOING else CallEvent.Direction.INCOMING)
.setEvent(CallEvent.Event.NOT_ACCEPTED)
.build()
}
}

View File

@@ -37,6 +37,12 @@ public class CallSetupActionProcessorDelegate extends WebRtcActionProcessor {
RemotePeer activePeer = currentState.getCallInfoState().requireActivePeer();
webRtcInteractor.sendAcceptedCallEventSyncMessage(
activePeer,
currentState.getCallInfoState().getCallState() == WebRtcViewModel.State.CALL_RINGING,
currentState.getCallSetupState(activePeer).isAcceptWithVideo() || currentState.getLocalDeviceState().getCameraState().isEnabled()
);
ApplicationDependencies.getAppForegroundObserver().removeListener(webRtcInteractor.getForegroundListener());
webRtcInteractor.startAudioCommunication();
webRtcInteractor.activateCall(activePeer.getId());

View File

@@ -10,6 +10,7 @@ import org.signal.core.util.logging.Log;
import org.signal.ringrtc.CallException;
import org.signal.ringrtc.CallId;
import org.signal.ringrtc.CallManager;
import org.thoughtcrime.securesms.database.CallTable;
import org.thoughtcrime.securesms.database.RecipientTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
@@ -130,8 +131,6 @@ public class IncomingCallActionProcessor extends DeviceAwareActionProcessor {
Log.i(TAG, "handleAcceptCall(): call_id: " + activePeer.getCallId());
SignalDatabase.messages().insertReceivedCall(activePeer.getId(), currentState.getCallSetupState(activePeer).isRemoteVideoOffer());
currentState = currentState.builder()
.changeCallSetupState(activePeer.getCallId())
.acceptWithVideo(answerWithVideo)
@@ -156,10 +155,13 @@ public class IncomingCallActionProcessor extends DeviceAwareActionProcessor {
Log.i(TAG, "handleDenyCall():");
webRtcInteractor.sendNotAcceptedCallEventSyncMessage(activePeer,
false,
currentState.getCallSetupState(activePeer).isAcceptWithVideo() || currentState.getLocalDeviceState().getCameraState().isEnabled());
try {
webRtcInteractor.rejectIncomingCall(activePeer.getId());
webRtcInteractor.getCallManager().hangup();
SignalDatabase.messages().insertMissedCall(activePeer.getId(), System.currentTimeMillis(), currentState.getCallSetupState(activePeer).isRemoteVideoOffer());
return terminate(currentState, activePeer);
} catch (CallException e) {
return callFailure(currentState, "hangup() failed: ", e);
@@ -173,6 +175,14 @@ public class IncomingCallActionProcessor extends DeviceAwareActionProcessor {
Recipient recipient = remotePeer.getRecipient();
activePeer.localRinging();
SignalDatabase.calls().insertCall(remotePeer.getCallId().longValue(),
System.currentTimeMillis(),
remotePeer.getId(),
currentState.getCallSetupState(activePeer).isRemoteVideoOffer() ? CallTable.Type.VIDEO_CALL : CallTable.Type.AUDIO_CALL,
CallTable.Direction.INCOMING,
CallTable.Event.ONGOING);
webRtcInteractor.updatePhoneState(LockManager.PhoneState.INTERACTIVE);
boolean shouldDisturbUserWithCall = DoNotDisturbUtil.shouldDisturbUserWithCall(context.getApplicationContext(), recipient);

View File

@@ -11,6 +11,7 @@ import org.signal.ringrtc.CallException;
import org.signal.ringrtc.CallId;
import org.signal.ringrtc.CallManager;
import org.thoughtcrime.securesms.components.webrtc.EglBaseWrapper;
import org.thoughtcrime.securesms.database.CallTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.events.CallParticipant;
import org.thoughtcrime.securesms.events.WebRtcViewModel;
@@ -83,7 +84,13 @@ public class OutgoingCallActionProcessor extends DeviceAwareActionProcessor {
}
RecipientUtil.setAndSendUniversalExpireTimerIfNecessary(context, Recipient.resolved(remotePeer.getId()), SignalDatabase.threads().getThreadIdIfExistsFor(remotePeer.getId()));
SignalDatabase.messages().insertOutgoingCall(remotePeer.getId(), isVideoCall);
SignalDatabase.calls().insertCall(remotePeer.getCallId().longValue(),
System.currentTimeMillis(),
remotePeer.getId(),
isVideoCall ? CallTable.Type.VIDEO_CALL : CallTable.Type.AUDIO_CALL,
CallTable.Direction.OUTGOING,
CallTable.Event.ONGOING);
EglBaseWrapper.replaceHolder(EglBaseWrapper.OUTGOING_PLACEHOLDER, remotePeer.getCallId().longValue());
@@ -236,6 +243,13 @@ public class OutgoingCallActionProcessor extends DeviceAwareActionProcessor {
@Override
protected @NonNull WebRtcServiceState handleLocalHangup(@NonNull WebRtcServiceState currentState) {
RemotePeer activePeer = currentState.getCallInfoState().getActivePeer();
if (activePeer != null) {
webRtcInteractor.sendNotAcceptedCallEventSyncMessage(activePeer,
true,
currentState.getCallSetupState(activePeer).isAcceptWithVideo() || currentState.getLocalDeviceState().getCameraState().isEnabled());
}
return activeCallDelegate.handleLocalHangup(currentState);
}
@@ -246,6 +260,19 @@ public class OutgoingCallActionProcessor extends DeviceAwareActionProcessor {
@Override
protected @NonNull WebRtcServiceState handleEndedRemote(@NonNull WebRtcServiceState currentState, @NonNull CallManager.CallEvent endedRemoteEvent, @NonNull RemotePeer remotePeer) {
RemotePeer activePeer = currentState.getCallInfoState().getActivePeer();
if (activePeer != null &&
(endedRemoteEvent == CallManager.CallEvent.ENDED_REMOTE_HANGUP ||
endedRemoteEvent == CallManager.CallEvent.ENDED_REMOTE_HANGUP_NEED_PERMISSION ||
endedRemoteEvent == CallManager.CallEvent.ENDED_REMOTE_BUSY ||
endedRemoteEvent == CallManager.CallEvent.ENDED_TIMEOUT ||
endedRemoteEvent == CallManager.CallEvent.ENDED_REMOTE_GLARE))
{
webRtcInteractor.sendNotAcceptedCallEventSyncMessage(activePeer,
true,
currentState.getCallSetupState(activePeer).isAcceptWithVideo() || currentState.getLocalDeviceState().getCameraState().isEnabled());
}
return activeCallDelegate.handleEndedRemote(currentState, endedRemoteEvent, remotePeer);
}

View File

@@ -31,6 +31,7 @@ import org.signal.storageservice.protos.groups.GroupExternalCredential;
import org.thoughtcrime.securesms.WebRtcCallActivity;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.CallTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.events.GroupCallPeekEvent;
@@ -66,8 +67,10 @@ import org.whispersystems.signalservice.api.messages.calls.OfferMessage;
import org.whispersystems.signalservice.api.messages.calls.OpaqueMessage;
import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage;
import org.whispersystems.signalservice.api.messages.calls.TurnServerInfo;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage;
import java.io.IOException;
import java.util.Collection;
@@ -521,7 +524,7 @@ private void processStateless(@NonNull Function1<WebRtcEphemeralState, WebRtcEph
Log.i(TAG, "Ignoring event: " + event);
break;
default:
throw new AssertionError("Unexpected event: " + event.toString());
throw new AssertionError("Unexpected event: " + event);
}
return s;
@@ -825,18 +828,28 @@ private void processStateless(@NonNull Function1<WebRtcEphemeralState, WebRtcEph
});
}
public void insertMissedCall(@NonNull RemotePeer remotePeer, boolean signal, long timestamp, boolean isVideoOffer) {
Pair<Long, Long> messageAndThreadId = SignalDatabase.messages().insertMissedCall(remotePeer.getId(), timestamp, isVideoOffer);
public void insertMissedCall(@NonNull RemotePeer remotePeer, long timestamp, boolean isVideoOffer) {
CallTable.Call call = SignalDatabase.calls()
.updateCall(remotePeer.getCallId().longValue(), CallTable.Event.MISSED);
ApplicationDependencies.getMessageNotifier()
.updateNotification(context, ConversationId.forConversation(messageAndThreadId.second()), signal);
if (call == null) {
CallTable.Type type = isVideoOffer ? CallTable.Type.VIDEO_CALL : CallTable.Type.AUDIO_CALL;
SignalDatabase.calls()
.insertCall(remotePeer.getCallId().longValue(), timestamp, remotePeer.getId(), type, CallTable.Direction.INCOMING, CallTable.Event.MISSED);
}
}
public void insertReceivedCall(@NonNull RemotePeer remotePeer, boolean signal, boolean isVideoOffer) {
Pair<Long, Long> messageAndThreadId = SignalDatabase.messages().insertReceivedCall(remotePeer.getId(), isVideoOffer);
public void insertReceivedCall(@NonNull RemotePeer remotePeer, boolean isVideoOffer) {
CallTable.Call call = SignalDatabase.calls()
.updateCall(remotePeer.getCallId().longValue(), CallTable.Event.ACCEPTED);
ApplicationDependencies.getMessageNotifier()
.updateNotification(context, ConversationId.forConversation(messageAndThreadId.second()), signal);
if (call == null) {
CallTable.Type type = isVideoOffer ? CallTable.Type.VIDEO_CALL : CallTable.Type.AUDIO_CALL;
SignalDatabase.calls()
.insertCall(remotePeer.getCallId().longValue(), System.currentTimeMillis(), remotePeer.getId(), type, CallTable.Direction.INCOMING, CallTable.Event.ACCEPTED);
}
}
public void retrieveTurnServers(@NonNull RemotePeer remotePeer) {
@@ -919,6 +932,40 @@ private void processStateless(@NonNull Function1<WebRtcEphemeralState, WebRtcEph
});
}
public void sendAcceptedCallEventSyncMessage(@NonNull RemotePeer remotePeer, boolean isOutgoing, boolean isVideoCall) {
SignalDatabase
.calls()
.updateCall(remotePeer.getCallId().longValue(), CallTable.Event.ACCEPTED);
if (TextSecurePreferences.isMultiDevice(context)) {
networkExecutor.execute(() -> {
try {
SyncMessage.CallEvent callEvent = CallEventSyncMessageUtil.createAcceptedSyncMessage(remotePeer, System.currentTimeMillis(), isOutgoing, isVideoCall);
ApplicationDependencies.getSignalServiceMessageSender().sendSyncMessage(SignalServiceSyncMessage.forCallEvent(callEvent), Optional.empty());
} catch (IOException | UntrustedIdentityException e) {
Log.w(TAG, "Unable to send call event sync message for " + remotePeer.getCallId().longValue(), e);
}
});
}
}
public void sendNotAcceptedCallEventSyncMessage(@NonNull RemotePeer remotePeer, boolean isOutgoing, boolean isVideoCall) {
SignalDatabase
.calls()
.updateCall(remotePeer.getCallId().longValue(), CallTable.Event.NOT_ACCEPTED);
if (TextSecurePreferences.isMultiDevice(context)) {
networkExecutor.execute(() -> {
try {
SyncMessage.CallEvent callEvent = CallEventSyncMessageUtil.createNotAcceptedSyncMessage(remotePeer, System.currentTimeMillis(), isOutgoing, isVideoCall);
ApplicationDependencies.getSignalServiceMessageSender().sendSyncMessage(SignalServiceSyncMessage.forCallEvent(callEvent), Optional.empty());
} catch (IOException | UntrustedIdentityException e) {
Log.w(TAG, "Unable to send call event sync message for " + remotePeer.getCallId().longValue(), e);
}
});
}
}
private void processSendMessageFailureWithChangeDetection(@NonNull RemotePeer remotePeer,
@NonNull ProcessAction failureProcessAction)
{

View File

@@ -109,11 +109,11 @@ public class WebRtcInteractor {
}
void insertMissedCall(@NonNull RemotePeer remotePeer, long timestamp, boolean isVideoOffer) {
signalCallManager.insertMissedCall(remotePeer, true, timestamp, isVideoOffer);
signalCallManager.insertMissedCall(remotePeer, timestamp, isVideoOffer);
}
void insertReceivedCall(@NonNull RemotePeer remotePeer, boolean isVideoOffer) {
signalCallManager.insertReceivedCall(remotePeer, true, isVideoOffer);
signalCallManager.insertReceivedCall(remotePeer, isVideoOffer);
}
boolean startWebRtcCallActivityIfPossible() {
@@ -187,4 +187,12 @@ public class WebRtcInteractor {
public void requestGroupMembershipProof(GroupId.V2 groupId, int groupCallHashCode) {
signalCallManager.requestGroupMembershipToken(groupId, groupCallHashCode);
}
public void sendAcceptedCallEventSyncMessage(@NonNull RemotePeer remotePeer, boolean isOutgoing, boolean isVideoCall) {
signalCallManager.sendAcceptedCallEventSyncMessage(remotePeer, isOutgoing, isVideoCall);
}
public void sendNotAcceptedCallEventSyncMessage(@NonNull RemotePeer remotePeer, boolean isOutgoing, boolean isVideoCall) {
signalCallManager.sendNotAcceptedCallEventSyncMessage(remotePeer, isOutgoing, isVideoCall);
}
}