diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/call/CallsListener.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/call/CallsListener.kt index 49207f9ed0..7ec46e7c10 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/call/CallsListener.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/call/CallsListener.kt @@ -49,4 +49,6 @@ interface CallsListener { fun onCallAnswerReceived(callAnswerContent: CallAnswerContent) fun onCallHangupReceived(callHangupContent: CallHangupContent) + + fun onCallManagedByOtherSession(callId: String) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/call/CallEventsObserverTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/call/CallEventsObserverTask.kt index 950381ef6b..4810853881 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/call/CallEventsObserverTask.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/call/CallEventsObserverTask.kt @@ -53,6 +53,7 @@ internal class DefaultCallEventsObserverTask @Inject constructor( private fun update(realm: Realm, events: List, userId: String) { val now = System.currentTimeMillis() + // TODO might check if an invite is not closed (hangup/answsered) in the same event batch? events.forEach { event -> event.roomId ?: return@forEach Unit.also { Timber.w("Event with no room id ${event.eventId}") diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/call/DefaultCallSignalingService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/call/DefaultCallSignalingService.kt index ed9361b4c5..b8ecd5abe4 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/call/DefaultCallSignalingService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/call/DefaultCallSignalingService.kt @@ -19,6 +19,7 @@ package im.vector.matrix.android.internal.session.call import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.extensions.tryThis import im.vector.matrix.android.api.session.call.CallSignalingService +import im.vector.matrix.android.api.session.call.CallState import im.vector.matrix.android.api.session.call.CallsListener import im.vector.matrix.android.api.session.call.MxCall import im.vector.matrix.android.api.session.call.TurnServerResponse @@ -108,19 +109,33 @@ internal class DefaultCallSignalingService @Inject constructor( } internal fun onCallEvent(event: Event) { - // TODO if handled by other of my sessions - // this test is too simple, should notify upstream - if (event.senderId == userId) { - // ignore local echos! - return - } when (event.getClearType()) { EventType.CALL_ANSWER -> { event.getClearContent().toModel()?.let { + if (event.senderId == userId) { + // ok it's an answer from me.. is it remote echo or other session + val knownCall = getCallWithId(it.callId) + if (knownCall == null) { + Timber.d("## VOIP onCallEvent ${event.getClearType()} id ${it.callId} send by me") + } else if (!knownCall.isOutgoing) { + // incoming call + // if it was anwsered by this session, the call state would be in Answering(or connected) state + if (knownCall.state == CallState.LocalRinging) { + // discard current call, it's answered by another of my session + onCallManageByOtherSession(it.callId) + } + } + return + } + onCallAnswer(it) } } EventType.CALL_INVITE -> { + if (event.senderId == userId) { + // Always ignore local echos of invite + return + } event.getClearContent().toModel()?.let { content -> val incomingCall = MxCallImpl( callId = content.callId ?: return@let, @@ -138,11 +153,31 @@ internal class DefaultCallSignalingService @Inject constructor( } EventType.CALL_HANGUP -> { event.getClearContent().toModel()?.let { content -> + + if (event.senderId == userId) { + // ok it's an answer from me.. is it remote echo or other session + val knownCall = getCallWithId(content.callId) + if (knownCall == null) { + Timber.d("## VOIP onCallEvent ${event.getClearType()} id ${content.callId} send by me") + } else if (!knownCall.isOutgoing) { + // incoming call + if (knownCall.state == CallState.LocalRinging) { + // discard current call, it's answered by another of my session + onCallManageByOtherSession(content.callId) + } + } + return + } + onCallHangup(content) activeCalls.removeAll { it.callId == content.callId } } } EventType.CALL_CANDIDATES -> { + if (event.senderId == userId) { + // Always ignore local echos of invite + return + } event.getClearContent().toModel()?.let { content -> activeCalls.firstOrNull { it.callId == content.callId }?.let { onCallIceCandidate(it, content) @@ -168,6 +203,14 @@ internal class DefaultCallSignalingService @Inject constructor( } } + private fun onCallManageByOtherSession(callId: String) { + callListeners.toList().forEach { + tryThis { + it.onCallManagedByOtherSession(callId) + } + } + } + private fun onCallInvite(incomingCall: MxCall, invite: CallInviteContent) { // Ignore the invitation from current user if (incomingCall.otherUserId == userId) return diff --git a/vector/src/main/java/im/vector/riotx/features/call/WebRtcPeerConnectionManager.kt b/vector/src/main/java/im/vector/riotx/features/call/WebRtcPeerConnectionManager.kt index be8380a3d8..d91bf270fe 100644 --- a/vector/src/main/java/im/vector/riotx/features/call/WebRtcPeerConnectionManager.kt +++ b/vector/src/main/java/im/vector/riotx/features/call/WebRtcPeerConnectionManager.kt @@ -692,6 +692,11 @@ class WebRtcPeerConnectionManager @Inject constructor( close() } + override fun onCallManagedByOtherSession(callId: String) { + currentCall = null + CallService.onNoActiveCall(context) + } + private inner class StreamObserver(val callContext: CallContext) : PeerConnection.Observer { override fun onConnectionChange(newState: PeerConnection.PeerConnectionState?) {