JingleRtpConnection code clean up
This commit is contained in:
parent
1471969237
commit
80c49955f0
|
@ -18,7 +18,6 @@ import com.google.common.collect.ImmutableMultimap;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.collect.Multimap;
|
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import com.google.common.primitives.Ints;
|
import com.google.common.primitives.Ints;
|
||||||
import com.google.common.util.concurrent.FutureCallback;
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
|
@ -26,23 +25,6 @@ import com.google.common.util.concurrent.Futures;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
import com.google.common.util.concurrent.MoreExecutors;
|
import com.google.common.util.concurrent.MoreExecutors;
|
||||||
|
|
||||||
import org.webrtc.EglBase;
|
|
||||||
import org.webrtc.IceCandidate;
|
|
||||||
import org.webrtc.PeerConnection;
|
|
||||||
import org.webrtc.VideoTrack;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Queue;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.ScheduledFuture;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import eu.siacs.conversations.BuildConfig;
|
import eu.siacs.conversations.BuildConfig;
|
||||||
import eu.siacs.conversations.Config;
|
import eu.siacs.conversations.Config;
|
||||||
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
|
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
|
||||||
|
@ -61,7 +43,6 @@ import eu.siacs.conversations.utils.IP;
|
||||||
import eu.siacs.conversations.xml.Element;
|
import eu.siacs.conversations.xml.Element;
|
||||||
import eu.siacs.conversations.xml.Namespace;
|
import eu.siacs.conversations.xml.Namespace;
|
||||||
import eu.siacs.conversations.xmpp.Jid;
|
import eu.siacs.conversations.xmpp.Jid;
|
||||||
import eu.siacs.conversations.xmpp.OnIqPacketReceived;
|
|
||||||
import eu.siacs.conversations.xmpp.jingle.stanzas.Content;
|
import eu.siacs.conversations.xmpp.jingle.stanzas.Content;
|
||||||
import eu.siacs.conversations.xmpp.jingle.stanzas.Group;
|
import eu.siacs.conversations.xmpp.jingle.stanzas.Group;
|
||||||
import eu.siacs.conversations.xmpp.jingle.stanzas.IceUdpTransportInfo;
|
import eu.siacs.conversations.xmpp.jingle.stanzas.IceUdpTransportInfo;
|
||||||
|
@ -73,6 +54,23 @@ import eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription;
|
||||||
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
|
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
|
||||||
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
|
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
|
||||||
|
|
||||||
|
import org.webrtc.EglBase;
|
||||||
|
import org.webrtc.IceCandidate;
|
||||||
|
import org.webrtc.PeerConnection;
|
||||||
|
import org.webrtc.VideoTrack;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class JingleRtpConnection extends AbstractJingleConnection
|
public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
implements WebRTCWrapper.EventCallback {
|
implements WebRTCWrapper.EventCallback {
|
||||||
|
|
||||||
|
@ -195,64 +193,37 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
}
|
}
|
||||||
|
|
||||||
private static State reasonToState(Reason reason) {
|
private static State reasonToState(Reason reason) {
|
||||||
switch (reason) {
|
return switch (reason) {
|
||||||
case SUCCESS:
|
case SUCCESS -> State.TERMINATED_SUCCESS;
|
||||||
return State.TERMINATED_SUCCESS;
|
case DECLINE, BUSY -> State.TERMINATED_DECLINED_OR_BUSY;
|
||||||
case DECLINE:
|
case CANCEL, TIMEOUT -> State.TERMINATED_CANCEL_OR_TIMEOUT;
|
||||||
case BUSY:
|
case SECURITY_ERROR -> State.TERMINATED_SECURITY_ERROR;
|
||||||
return State.TERMINATED_DECLINED_OR_BUSY;
|
case FAILED_APPLICATION, UNSUPPORTED_TRANSPORTS, UNSUPPORTED_APPLICATIONS -> State
|
||||||
case CANCEL:
|
.TERMINATED_APPLICATION_FAILURE;
|
||||||
case TIMEOUT:
|
default -> State.TERMINATED_CONNECTIVITY_ERROR;
|
||||||
return State.TERMINATED_CANCEL_OR_TIMEOUT;
|
};
|
||||||
case SECURITY_ERROR:
|
|
||||||
return State.TERMINATED_SECURITY_ERROR;
|
|
||||||
case FAILED_APPLICATION:
|
|
||||||
case UNSUPPORTED_TRANSPORTS:
|
|
||||||
case UNSUPPORTED_APPLICATIONS:
|
|
||||||
return State.TERMINATED_APPLICATION_FAILURE;
|
|
||||||
default:
|
|
||||||
return State.TERMINATED_CONNECTIVITY_ERROR;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
synchronized void deliverPacket(final JinglePacket jinglePacket) {
|
synchronized void deliverPacket(final JinglePacket jinglePacket) {
|
||||||
switch (jinglePacket.getAction()) {
|
switch (jinglePacket.getAction()) {
|
||||||
case SESSION_INITIATE:
|
case SESSION_INITIATE -> receiveSessionInitiate(jinglePacket);
|
||||||
receiveSessionInitiate(jinglePacket);
|
case TRANSPORT_INFO -> receiveTransportInfo(jinglePacket);
|
||||||
break;
|
case SESSION_ACCEPT -> receiveSessionAccept(jinglePacket);
|
||||||
case TRANSPORT_INFO:
|
case SESSION_TERMINATE -> receiveSessionTerminate(jinglePacket);
|
||||||
receiveTransportInfo(jinglePacket);
|
case CONTENT_ADD -> receiveContentAdd(jinglePacket);
|
||||||
break;
|
case CONTENT_ACCEPT -> receiveContentAccept(jinglePacket);
|
||||||
case SESSION_ACCEPT:
|
case CONTENT_REJECT -> receiveContentReject(jinglePacket);
|
||||||
receiveSessionAccept(jinglePacket);
|
case CONTENT_REMOVE -> receiveContentRemove(jinglePacket);
|
||||||
break;
|
case CONTENT_MODIFY -> receiveContentModify(jinglePacket);
|
||||||
case SESSION_TERMINATE:
|
default -> {
|
||||||
receiveSessionTerminate(jinglePacket);
|
|
||||||
break;
|
|
||||||
case CONTENT_ADD:
|
|
||||||
receiveContentAdd(jinglePacket);
|
|
||||||
break;
|
|
||||||
case CONTENT_ACCEPT:
|
|
||||||
receiveContentAccept(jinglePacket);
|
|
||||||
break;
|
|
||||||
case CONTENT_REJECT:
|
|
||||||
receiveContentReject(jinglePacket);
|
|
||||||
break;
|
|
||||||
case CONTENT_REMOVE:
|
|
||||||
receiveContentRemove(jinglePacket);
|
|
||||||
break;
|
|
||||||
case CONTENT_MODIFY:
|
|
||||||
receiveContentModify(jinglePacket);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
respondOk(jinglePacket);
|
respondOk(jinglePacket);
|
||||||
Log.d(
|
Log.d(
|
||||||
Config.LOGTAG,
|
Config.LOGTAG,
|
||||||
String.format(
|
String.format(
|
||||||
"%s: received unhandled jingle action %s",
|
"%s: received unhandled jingle action %s",
|
||||||
id.account.getJid().asBareJid(), jinglePacket.getAction()));
|
id.account.getJid().asBareJid(), jinglePacket.getAction()));
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -354,15 +325,22 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
final Set<Map.Entry<String, RtpContentMap.DescriptionTransport>> candidates =
|
final Set<Map.Entry<String, RtpContentMap.DescriptionTransport>> candidates =
|
||||||
contentMap.contents.entrySet();
|
contentMap.contents.entrySet();
|
||||||
final RtpContentMap remote = getRemoteContentMap();
|
final RtpContentMap remote = getRemoteContentMap();
|
||||||
final Set<String> remoteContentIds = remote == null ? Collections.emptySet() : remote.contents.keySet();
|
final Set<String> remoteContentIds =
|
||||||
|
remote == null ? Collections.emptySet() : remote.contents.keySet();
|
||||||
if (Collections.disjoint(remoteContentIds, contentMap.contents.keySet())) {
|
if (Collections.disjoint(remoteContentIds, contentMap.contents.keySet())) {
|
||||||
Log.d(Config.LOGTAG,"received transport-info for unknown contents "+contentMap.contents.keySet()+" (known: "+remoteContentIds+")");
|
Log.d(
|
||||||
|
Config.LOGTAG,
|
||||||
|
"received transport-info for unknown contents "
|
||||||
|
+ contentMap.contents.keySet()
|
||||||
|
+ " (known: "
|
||||||
|
+ remoteContentIds
|
||||||
|
+ ")");
|
||||||
respondOk(jinglePacket);
|
respondOk(jinglePacket);
|
||||||
pendingIceCandidates.addAll(candidates);
|
pendingIceCandidates.addAll(candidates);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.state != State.SESSION_ACCEPTED) {
|
if (this.state != State.SESSION_ACCEPTED) {
|
||||||
Log.d(Config.LOGTAG,"received transport-info prematurely. adding to backlog");
|
Log.d(Config.LOGTAG, "received transport-info prematurely. adding to backlog");
|
||||||
respondOk(jinglePacket);
|
respondOk(jinglePacket);
|
||||||
pendingIceCandidates.addAll(candidates);
|
pendingIceCandidates.addAll(candidates);
|
||||||
return;
|
return;
|
||||||
|
@ -401,26 +379,31 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
final boolean hasFullTransportInfo = modification.hasFullTransportInfo();
|
final boolean hasFullTransportInfo = modification.hasFullTransportInfo();
|
||||||
final ListenableFuture<RtpContentMap> future =
|
final ListenableFuture<RtpContentMap> future =
|
||||||
receiveRtpContentMap(
|
receiveRtpContentMap(
|
||||||
modification, this.omemoVerification.hasFingerprint() && hasFullTransportInfo);
|
modification,
|
||||||
Futures.addCallback(future, new FutureCallback<RtpContentMap>() {
|
this.omemoVerification.hasFingerprint() && hasFullTransportInfo);
|
||||||
@Override
|
Futures.addCallback(
|
||||||
public void onSuccess(final RtpContentMap rtpContentMap) {
|
future,
|
||||||
receiveContentAdd(jinglePacket, rtpContentMap);
|
new FutureCallback<>() {
|
||||||
}
|
@Override
|
||||||
|
public void onSuccess(final RtpContentMap rtpContentMap) {
|
||||||
|
receiveContentAdd(jinglePacket, rtpContentMap);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(@NonNull Throwable throwable) {
|
public void onFailure(@NonNull Throwable throwable) {
|
||||||
respondOk(jinglePacket);
|
respondOk(jinglePacket);
|
||||||
final Throwable rootCause = Throwables.getRootCause(throwable);
|
final Throwable rootCause = Throwables.getRootCause(throwable);
|
||||||
Log.d(
|
Log.d(
|
||||||
Config.LOGTAG,
|
Config.LOGTAG,
|
||||||
id.account.getJid().asBareJid()
|
id.account.getJid().asBareJid()
|
||||||
+ ": improperly formatted contents in content-add",
|
+ ": improperly formatted contents in content-add",
|
||||||
throwable);
|
throwable);
|
||||||
webRTCWrapper.close();
|
webRTCWrapper.close();
|
||||||
sendSessionTerminate(Reason.ofThrowable(rootCause), rootCause.getMessage());
|
sendSessionTerminate(
|
||||||
}
|
Reason.ofThrowable(rootCause), rootCause.getMessage());
|
||||||
}, MoreExecutors.directExecutor());
|
}
|
||||||
|
},
|
||||||
|
MoreExecutors.directExecutor());
|
||||||
} else {
|
} else {
|
||||||
terminateWithOutOfOrder(jinglePacket);
|
terminateWithOutOfOrder(jinglePacket);
|
||||||
}
|
}
|
||||||
|
@ -508,19 +491,24 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
final boolean hasFullTransportInfo = receivedContentAccept.hasFullTransportInfo();
|
final boolean hasFullTransportInfo = receivedContentAccept.hasFullTransportInfo();
|
||||||
final ListenableFuture<RtpContentMap> future =
|
final ListenableFuture<RtpContentMap> future =
|
||||||
receiveRtpContentMap(
|
receiveRtpContentMap(
|
||||||
receivedContentAccept, this.omemoVerification.hasFingerprint() && hasFullTransportInfo);
|
receivedContentAccept,
|
||||||
Futures.addCallback(future, new FutureCallback<RtpContentMap>() {
|
this.omemoVerification.hasFingerprint() && hasFullTransportInfo);
|
||||||
@Override
|
Futures.addCallback(
|
||||||
public void onSuccess(final RtpContentMap result) {
|
future,
|
||||||
receiveContentAccept(result);
|
new FutureCallback<>() {
|
||||||
}
|
@Override
|
||||||
|
public void onSuccess(final RtpContentMap result) {
|
||||||
|
receiveContentAccept(result);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(@NonNull final Throwable throwable) {
|
public void onFailure(@NonNull final Throwable throwable) {
|
||||||
webRTCWrapper.close();
|
webRTCWrapper.close();
|
||||||
sendSessionTerminate(Reason.ofThrowable(throwable), throwable.getMessage());
|
sendSessionTerminate(
|
||||||
}
|
Reason.ofThrowable(throwable), throwable.getMessage());
|
||||||
}, MoreExecutors.directExecutor());
|
}
|
||||||
|
},
|
||||||
|
MoreExecutors.directExecutor());
|
||||||
} else {
|
} else {
|
||||||
Log.d(Config.LOGTAG, "received content-accept did not match our outgoing content-add");
|
Log.d(Config.LOGTAG, "received content-accept did not match our outgoing content-add");
|
||||||
terminateWithOutOfOrder(jinglePacket);
|
terminateWithOutOfOrder(jinglePacket);
|
||||||
|
@ -573,25 +561,34 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
final boolean isInitiator = isInitiator();
|
final boolean isInitiator = isInitiator();
|
||||||
final RtpContentMap currentOutgoing = this.outgoingContentAdd;
|
final RtpContentMap currentOutgoing = this.outgoingContentAdd;
|
||||||
final RtpContentMap remoteContentMap = this.getRemoteContentMap();
|
final RtpContentMap remoteContentMap = this.getRemoteContentMap();
|
||||||
final Set<String> currentOutgoingMediaIds = currentOutgoing == null ? Collections.emptySet() : currentOutgoing.contents.keySet();
|
final Set<String> currentOutgoingMediaIds =
|
||||||
|
currentOutgoing == null
|
||||||
|
? Collections.emptySet()
|
||||||
|
: currentOutgoing.contents.keySet();
|
||||||
Log.d(Config.LOGTAG, "receiveContentModification(" + modification + ")");
|
Log.d(Config.LOGTAG, "receiveContentModification(" + modification + ")");
|
||||||
if (currentOutgoing != null && currentOutgoingMediaIds.containsAll(modification.keySet())) {
|
if (currentOutgoing != null && currentOutgoingMediaIds.containsAll(modification.keySet())) {
|
||||||
respondOk(jinglePacket);
|
respondOk(jinglePacket);
|
||||||
final RtpContentMap modifiedContentMap;
|
final RtpContentMap modifiedContentMap;
|
||||||
try {
|
try {
|
||||||
modifiedContentMap = currentOutgoing.modifiedSendersChecked(isInitiator, modification);
|
modifiedContentMap =
|
||||||
|
currentOutgoing.modifiedSendersChecked(isInitiator, modification);
|
||||||
} catch (final IllegalArgumentException e) {
|
} catch (final IllegalArgumentException e) {
|
||||||
webRTCWrapper.close();
|
webRTCWrapper.close();
|
||||||
sendSessionTerminate(Reason.FAILED_APPLICATION, e.getMessage());
|
sendSessionTerminate(Reason.FAILED_APPLICATION, e.getMessage());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.outgoingContentAdd = modifiedContentMap;
|
this.outgoingContentAdd = modifiedContentMap;
|
||||||
Log.d(Config.LOGTAG, id.account.getJid().asBareJid()+": processed content-modification for pending content-add");
|
Log.d(
|
||||||
} else if (remoteContentMap != null && remoteContentMap.contents.keySet().containsAll(modification.keySet())) {
|
Config.LOGTAG,
|
||||||
|
id.account.getJid().asBareJid()
|
||||||
|
+ ": processed content-modification for pending content-add");
|
||||||
|
} else if (remoteContentMap != null
|
||||||
|
&& remoteContentMap.contents.keySet().containsAll(modification.keySet())) {
|
||||||
respondOk(jinglePacket);
|
respondOk(jinglePacket);
|
||||||
final RtpContentMap modifiedRemoteContentMap;
|
final RtpContentMap modifiedRemoteContentMap;
|
||||||
try {
|
try {
|
||||||
modifiedRemoteContentMap = remoteContentMap.modifiedSendersChecked(isInitiator, modification);
|
modifiedRemoteContentMap =
|
||||||
|
remoteContentMap.modifiedSendersChecked(isInitiator, modification);
|
||||||
} catch (final IllegalArgumentException e) {
|
} catch (final IllegalArgumentException e) {
|
||||||
webRTCWrapper.close();
|
webRTCWrapper.close();
|
||||||
sendSessionTerminate(Reason.FAILED_APPLICATION, e.getMessage());
|
sendSessionTerminate(Reason.FAILED_APPLICATION, e.getMessage());
|
||||||
|
@ -601,20 +598,27 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
try {
|
try {
|
||||||
offer = SessionDescription.of(modifiedRemoteContentMap, !isInitiator());
|
offer = SessionDescription.of(modifiedRemoteContentMap, !isInitiator());
|
||||||
} catch (final IllegalArgumentException | NullPointerException e) {
|
} catch (final IllegalArgumentException | NullPointerException e) {
|
||||||
Log.d(Config.LOGTAG, id.getAccount().getJid().asBareJid() + ": unable convert offer from content-modify to SDP", e);
|
Log.d(
|
||||||
|
Config.LOGTAG,
|
||||||
|
id.getAccount().getJid().asBareJid()
|
||||||
|
+ ": unable convert offer from content-modify to SDP",
|
||||||
|
e);
|
||||||
webRTCWrapper.close();
|
webRTCWrapper.close();
|
||||||
sendSessionTerminate(Reason.FAILED_APPLICATION, e.getMessage());
|
sendSessionTerminate(Reason.FAILED_APPLICATION, e.getMessage());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Log.d(Config.LOGTAG, id.account.getJid().asBareJid()+": auto accepting content-modification");
|
Log.d(
|
||||||
|
Config.LOGTAG,
|
||||||
|
id.account.getJid().asBareJid() + ": auto accepting content-modification");
|
||||||
this.autoAcceptContentModify(modifiedRemoteContentMap, offer);
|
this.autoAcceptContentModify(modifiedRemoteContentMap, offer);
|
||||||
} else {
|
} else {
|
||||||
Log.d(Config.LOGTAG,"received unsupported content modification "+modification);
|
Log.d(Config.LOGTAG, "received unsupported content modification " + modification);
|
||||||
respondWithItemNotFound(jinglePacket);
|
respondWithItemNotFound(jinglePacket);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void autoAcceptContentModify(final RtpContentMap modifiedRemoteContentMap, final SessionDescription offer) {
|
private void autoAcceptContentModify(
|
||||||
|
final RtpContentMap modifiedRemoteContentMap, final SessionDescription offer) {
|
||||||
this.setRemoteContentMap(modifiedRemoteContentMap);
|
this.setRemoteContentMap(modifiedRemoteContentMap);
|
||||||
final org.webrtc.SessionDescription sdp =
|
final org.webrtc.SessionDescription sdp =
|
||||||
new org.webrtc.SessionDescription(
|
new org.webrtc.SessionDescription(
|
||||||
|
@ -625,8 +629,9 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
final SessionDescription answer = setLocalSessionDescription();
|
final SessionDescription answer = setLocalSessionDescription();
|
||||||
final RtpContentMap rtpContentMap = RtpContentMap.of(answer, isInitiator());
|
final RtpContentMap rtpContentMap = RtpContentMap.of(answer, isInitiator());
|
||||||
modifyLocalContentMap(rtpContentMap);
|
modifyLocalContentMap(rtpContentMap);
|
||||||
// we do not need to send an answer but do we have to resend the candidates currently in SDP?
|
// we do not need to send an answer but do we have to resend the candidates currently in
|
||||||
//resendCandidatesFromSdp(answer);
|
// SDP?
|
||||||
|
// resendCandidatesFromSdp(answer);
|
||||||
webRTCWrapper.setIsReadyToReceiveIceCandidates(true);
|
webRTCWrapper.setIsReadyToReceiveIceCandidates(true);
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
Log.d(Config.LOGTAG, "unable to accept content add", Throwables.getRootCause(e));
|
Log.d(Config.LOGTAG, "unable to accept content add", Throwables.getRootCause(e));
|
||||||
|
@ -635,17 +640,20 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ImmutableMultimap<String, IceUdpTransportInfo.Candidate> parseCandidates(final SessionDescription answer) {
|
private static ImmutableMultimap<String, IceUdpTransportInfo.Candidate> parseCandidates(
|
||||||
final ImmutableMultimap.Builder<String, IceUdpTransportInfo.Candidate> candidateBuilder = new ImmutableMultimap.Builder<>();
|
final SessionDescription answer) {
|
||||||
for(final SessionDescription.Media media : answer.media) {
|
final ImmutableMultimap.Builder<String, IceUdpTransportInfo.Candidate> candidateBuilder =
|
||||||
|
new ImmutableMultimap.Builder<>();
|
||||||
|
for (final SessionDescription.Media media : answer.media) {
|
||||||
final String mid = Iterables.getFirst(media.attributes.get("mid"), null);
|
final String mid = Iterables.getFirst(media.attributes.get("mid"), null);
|
||||||
if (Strings.isNullOrEmpty(mid)) {
|
if (Strings.isNullOrEmpty(mid)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for(final String sdpCandidate : media.attributes.get("candidate")) {
|
for (final String sdpCandidate : media.attributes.get("candidate")) {
|
||||||
final IceUdpTransportInfo.Candidate candidate = IceUdpTransportInfo.Candidate.fromSdpAttributeValue(sdpCandidate, null);
|
final IceUdpTransportInfo.Candidate candidate =
|
||||||
|
IceUdpTransportInfo.Candidate.fromSdpAttributeValue(sdpCandidate, null);
|
||||||
if (candidate != null) {
|
if (candidate != null) {
|
||||||
candidateBuilder.put(mid,candidate);
|
candidateBuilder.put(mid, candidate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -677,7 +685,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
if (ourSummary.equals(ContentAddition.summary(receivedContentReject))) {
|
if (ourSummary.equals(ContentAddition.summary(receivedContentReject))) {
|
||||||
this.outgoingContentAdd = null;
|
this.outgoingContentAdd = null;
|
||||||
respondOk(jinglePacket);
|
respondOk(jinglePacket);
|
||||||
Log.d(Config.LOGTAG,jinglePacket.toString());
|
Log.d(Config.LOGTAG, jinglePacket.toString());
|
||||||
receiveContentReject(ourSummary);
|
receiveContentReject(ourSummary);
|
||||||
} else {
|
} else {
|
||||||
Log.d(Config.LOGTAG, "received content-reject did not match our outgoing content-add");
|
Log.d(Config.LOGTAG, "received content-reject did not match our outgoing content-add");
|
||||||
|
@ -813,7 +821,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
"Unexpected rollback condition. Senders were not uniformly none");
|
"Unexpected rollback condition. Senders were not uniformly none");
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void acceptContentAdd(@NonNull final Set<ContentAddition.Summary> contentAddition) {
|
public synchronized void acceptContentAdd(
|
||||||
|
@NonNull final Set<ContentAddition.Summary> contentAddition) {
|
||||||
final RtpContentMap incomingContentAdd = this.incomingContentAdd;
|
final RtpContentMap incomingContentAdd = this.incomingContentAdd;
|
||||||
if (incomingContentAdd == null) {
|
if (incomingContentAdd == null) {
|
||||||
throw new IllegalStateException("No incoming content add");
|
throw new IllegalStateException("No incoming content add");
|
||||||
|
@ -822,37 +831,61 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
if (contentAddition.equals(ContentAddition.summary(incomingContentAdd))) {
|
if (contentAddition.equals(ContentAddition.summary(incomingContentAdd))) {
|
||||||
this.incomingContentAdd = null;
|
this.incomingContentAdd = null;
|
||||||
final Set<Content.Senders> senders = incomingContentAdd.getSenders();
|
final Set<Content.Senders> senders = incomingContentAdd.getSenders();
|
||||||
Log.d(Config.LOGTAG,"senders of incoming content-add: "+senders);
|
Log.d(Config.LOGTAG, "senders of incoming content-add: " + senders);
|
||||||
if (senders.equals(Content.Senders.receiveOnly(isInitiator()))) {
|
if (senders.equals(Content.Senders.receiveOnly(isInitiator()))) {
|
||||||
Log.d(Config.LOGTAG,"content addition is receive only. we want to upgrade to 'both'");
|
Log.d(
|
||||||
final RtpContentMap modifiedSenders = incomingContentAdd.modifiedSenders(Content.Senders.BOTH);
|
Config.LOGTAG,
|
||||||
final JinglePacket proposedContentModification = modifiedSenders.toStub().toJinglePacket(JinglePacket.Action.CONTENT_MODIFY, id.sessionId);
|
"content addition is receive only. we want to upgrade to 'both'");
|
||||||
|
final RtpContentMap modifiedSenders =
|
||||||
|
incomingContentAdd.modifiedSenders(Content.Senders.BOTH);
|
||||||
|
final JinglePacket proposedContentModification =
|
||||||
|
modifiedSenders
|
||||||
|
.toStub()
|
||||||
|
.toJinglePacket(JinglePacket.Action.CONTENT_MODIFY, id.sessionId);
|
||||||
proposedContentModification.setTo(id.with);
|
proposedContentModification.setTo(id.with);
|
||||||
xmppConnectionService.sendIqPacket(id.account, proposedContentModification, (account, response) -> {
|
xmppConnectionService.sendIqPacket(
|
||||||
if (response.getType() == IqPacket.TYPE.RESULT) {
|
id.account,
|
||||||
Log.d(Config.LOGTAG,id.account.getJid().asBareJid()+": remote has accepted our upgrade to senders=both");
|
proposedContentModification,
|
||||||
acceptContentAdd(ContentAddition.summary(modifiedSenders), modifiedSenders);
|
(account, response) -> {
|
||||||
} else {
|
if (response.getType() == IqPacket.TYPE.RESULT) {
|
||||||
Log.d(Config.LOGTAG,id.account.getJid().asBareJid()+": remote has rejected our upgrade to senders=both");
|
Log.d(
|
||||||
acceptContentAdd(contentAddition, incomingContentAdd);
|
Config.LOGTAG,
|
||||||
}
|
id.account.getJid().asBareJid()
|
||||||
});
|
+ ": remote has accepted our upgrade to senders=both");
|
||||||
|
acceptContentAdd(
|
||||||
|
ContentAddition.summary(modifiedSenders), modifiedSenders);
|
||||||
|
} else {
|
||||||
|
Log.d(
|
||||||
|
Config.LOGTAG,
|
||||||
|
id.account.getJid().asBareJid()
|
||||||
|
+ ": remote has rejected our upgrade to senders=both");
|
||||||
|
acceptContentAdd(contentAddition, incomingContentAdd);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("Accepted content add does not match pending content-add");
|
throw new IllegalStateException(
|
||||||
|
"Accepted content add does not match pending content-add");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void acceptContentAdd(@NonNull final Set<ContentAddition.Summary> contentAddition, final RtpContentMap incomingContentAdd) {
|
private void acceptContentAdd(
|
||||||
|
@NonNull final Set<ContentAddition.Summary> contentAddition,
|
||||||
|
final RtpContentMap incomingContentAdd) {
|
||||||
final IceUdpTransportInfo.Setup setup = getPeerDtlsSetup();
|
final IceUdpTransportInfo.Setup setup = getPeerDtlsSetup();
|
||||||
final RtpContentMap modifiedContentMap = getRemoteContentMap().addContent(incomingContentAdd, setup);
|
final RtpContentMap modifiedContentMap =
|
||||||
|
getRemoteContentMap().addContent(incomingContentAdd, setup);
|
||||||
this.setRemoteContentMap(modifiedContentMap);
|
this.setRemoteContentMap(modifiedContentMap);
|
||||||
|
|
||||||
final SessionDescription offer;
|
final SessionDescription offer;
|
||||||
try {
|
try {
|
||||||
offer = SessionDescription.of(modifiedContentMap, !isInitiator());
|
offer = SessionDescription.of(modifiedContentMap, !isInitiator());
|
||||||
} catch (final IllegalArgumentException | NullPointerException e) {
|
} catch (final IllegalArgumentException | NullPointerException e) {
|
||||||
Log.d(Config.LOGTAG, id.getAccount().getJid().asBareJid() + ": unable convert offer from content-add to SDP", e);
|
Log.d(
|
||||||
|
Config.LOGTAG,
|
||||||
|
id.getAccount().getJid().asBareJid()
|
||||||
|
+ ": unable convert offer from content-add to SDP",
|
||||||
|
e);
|
||||||
webRTCWrapper.close();
|
webRTCWrapper.close();
|
||||||
sendSessionTerminate(Reason.FAILED_APPLICATION, e.getMessage());
|
sendSessionTerminate(Reason.FAILED_APPLICATION, e.getMessage());
|
||||||
return;
|
return;
|
||||||
|
@ -893,10 +926,11 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
addIceCandidatesFromBlackLog();
|
addIceCandidatesFromBlackLog();
|
||||||
|
|
||||||
modifyLocalContentMap(rtpContentMap);
|
modifyLocalContentMap(rtpContentMap);
|
||||||
final ListenableFuture<RtpContentMap> future = prepareOutgoingContentMap(contentAcceptMap);
|
final ListenableFuture<RtpContentMap> future =
|
||||||
|
prepareOutgoingContentMap(contentAcceptMap);
|
||||||
Futures.addCallback(
|
Futures.addCallback(
|
||||||
future,
|
future,
|
||||||
new FutureCallback<RtpContentMap>() {
|
new FutureCallback<>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(final RtpContentMap rtpContentMap) {
|
public void onSuccess(final RtpContentMap rtpContentMap) {
|
||||||
sendContentAccept(rtpContentMap);
|
sendContentAccept(rtpContentMap);
|
||||||
|
@ -917,7 +951,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendContentAccept(final RtpContentMap contentAcceptMap) {
|
private void sendContentAccept(final RtpContentMap contentAcceptMap) {
|
||||||
final JinglePacket jinglePacket = contentAcceptMap.toJinglePacket(JinglePacket.Action.CONTENT_ACCEPT, id.sessionId);
|
final JinglePacket jinglePacket =
|
||||||
|
contentAcceptMap.toJinglePacket(JinglePacket.Action.CONTENT_ACCEPT, id.sessionId);
|
||||||
send(jinglePacket);
|
send(jinglePacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -963,7 +998,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
// ICE-restart
|
// ICE-restart
|
||||||
// and if that's the case we are seeing an answer.
|
// and if that's the case we are seeing an answer.
|
||||||
// This might be more spec compliant but also more error prone potentially
|
// This might be more spec compliant but also more error prone potentially
|
||||||
final boolean isSignalStateStable = this.webRTCWrapper.getSignalingState() == PeerConnection.SignalingState.STABLE;
|
final boolean isSignalStateStable =
|
||||||
|
this.webRTCWrapper.getSignalingState() == PeerConnection.SignalingState.STABLE;
|
||||||
// TODO a stable signal state can be another indicator that we have an offer to restart ICE
|
// TODO a stable signal state can be another indicator that we have an offer to restart ICE
|
||||||
final boolean isOffer = rtpContentMap.emptyCandidates();
|
final boolean isOffer = rtpContentMap.emptyCandidates();
|
||||||
final RtpContentMap restartContentMap;
|
final RtpContentMap restartContentMap;
|
||||||
|
@ -1027,7 +1063,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
final RtpContentMap restartContentMap,
|
final RtpContentMap restartContentMap,
|
||||||
final boolean isOffer)
|
final boolean isOffer)
|
||||||
throws ExecutionException, InterruptedException {
|
throws ExecutionException, InterruptedException {
|
||||||
final SessionDescription sessionDescription = SessionDescription.of(restartContentMap, !isInitiator());
|
final SessionDescription sessionDescription =
|
||||||
|
SessionDescription.of(restartContentMap, !isInitiator());
|
||||||
final org.webrtc.SessionDescription.Type type =
|
final org.webrtc.SessionDescription.Type type =
|
||||||
isOffer
|
isOffer
|
||||||
? org.webrtc.SessionDescription.Type.OFFER
|
? org.webrtc.SessionDescription.Type.OFFER
|
||||||
|
@ -1126,8 +1163,10 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
return Futures.immediateFailedFuture(e);
|
return Futures.immediateFailedFuture(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private ListenableFuture<RtpContentMap> receiveRtpContentMap(final RtpContentMap receivedContentMap, final boolean expectVerification) {
|
|
||||||
|
private ListenableFuture<RtpContentMap> receiveRtpContentMap(
|
||||||
|
final RtpContentMap receivedContentMap, final boolean expectVerification) {
|
||||||
Log.d(
|
Log.d(
|
||||||
Config.LOGTAG,
|
Config.LOGTAG,
|
||||||
"receiveRtpContentMap("
|
"receiveRtpContentMap("
|
||||||
|
@ -1183,7 +1222,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
final ListenableFuture<RtpContentMap> future = receiveRtpContentMap(jinglePacket, false);
|
final ListenableFuture<RtpContentMap> future = receiveRtpContentMap(jinglePacket, false);
|
||||||
Futures.addCallback(
|
Futures.addCallback(
|
||||||
future,
|
future,
|
||||||
new FutureCallback<RtpContentMap>() {
|
new FutureCallback<>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(@Nullable RtpContentMap rtpContentMap) {
|
public void onSuccess(@Nullable RtpContentMap rtpContentMap) {
|
||||||
receiveSessionInitiate(jinglePacket, rtpContentMap);
|
receiveSessionInitiate(jinglePacket, rtpContentMap);
|
||||||
|
@ -1272,7 +1311,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
receiveRtpContentMap(jinglePacket, this.omemoVerification.hasFingerprint());
|
receiveRtpContentMap(jinglePacket, this.omemoVerification.hasFingerprint());
|
||||||
Futures.addCallback(
|
Futures.addCallback(
|
||||||
future,
|
future,
|
||||||
new FutureCallback<RtpContentMap>() {
|
new FutureCallback<>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(@Nullable RtpContentMap rtpContentMap) {
|
public void onSuccess(@Nullable RtpContentMap rtpContentMap) {
|
||||||
receiveSessionAccept(jinglePacket, rtpContentMap);
|
receiveSessionAccept(jinglePacket, rtpContentMap);
|
||||||
|
@ -1438,7 +1477,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
sendSessionTerminate(Reason.ofThrowable(rootCause), rootCause.getMessage());
|
sendSessionTerminate(Reason.ofThrowable(rootCause), rootCause.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void failureToPerformAction(final JinglePacket.Action action, final Throwable throwable) {
|
private void failureToPerformAction(
|
||||||
|
final JinglePacket.Action action, final Throwable throwable) {
|
||||||
if (isTerminated()) {
|
if (isTerminated()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1459,7 +1499,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
}
|
}
|
||||||
|
|
||||||
private void prepareSessionAccept(
|
private void prepareSessionAccept(
|
||||||
final org.webrtc.SessionDescription webRTCSessionDescription, final boolean includeCandidates) {
|
final org.webrtc.SessionDescription webRTCSessionDescription,
|
||||||
|
final boolean includeCandidates) {
|
||||||
final SessionDescription sessionDescription =
|
final SessionDescription sessionDescription =
|
||||||
SessionDescription.parse(webRTCSessionDescription.description);
|
SessionDescription.parse(webRTCSessionDescription.description);
|
||||||
final RtpContentMap respondingRtpContentMap = RtpContentMap.of(sessionDescription, false);
|
final RtpContentMap respondingRtpContentMap = RtpContentMap.of(sessionDescription, false);
|
||||||
|
@ -1475,7 +1516,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
prepareOutgoingContentMap(respondingRtpContentMap);
|
prepareOutgoingContentMap(respondingRtpContentMap);
|
||||||
Futures.addCallback(
|
Futures.addCallback(
|
||||||
outgoingContentMapFuture,
|
outgoingContentMapFuture,
|
||||||
new FutureCallback<RtpContentMap>() {
|
new FutureCallback<>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(final RtpContentMap outgoingContentMap) {
|
public void onSuccess(final RtpContentMap outgoingContentMap) {
|
||||||
if (includeCandidates) {
|
if (includeCandidates) {
|
||||||
|
@ -1548,30 +1589,23 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
+ ": delivered message to JingleRtpConnection "
|
+ ": delivered message to JingleRtpConnection "
|
||||||
+ message);
|
+ message);
|
||||||
switch (message.getName()) {
|
switch (message.getName()) {
|
||||||
case "propose":
|
case "propose" -> receivePropose(
|
||||||
receivePropose(from, Propose.upgrade(message), serverMessageId, timestamp);
|
from, Propose.upgrade(message), serverMessageId, timestamp);
|
||||||
break;
|
case "proceed" -> receiveProceed(
|
||||||
case "proceed":
|
from, Proceed.upgrade(message), serverMessageId, timestamp);
|
||||||
receiveProceed(from, Proceed.upgrade(message), serverMessageId, timestamp);
|
case "retract" -> receiveRetract(from, serverMessageId, timestamp);
|
||||||
break;
|
case "reject" -> receiveReject(from, serverMessageId, timestamp);
|
||||||
case "retract":
|
case "accept" -> receiveAccept(from, serverMessageId, timestamp);
|
||||||
receiveRetract(from, serverMessageId, timestamp);
|
|
||||||
break;
|
|
||||||
case "reject":
|
|
||||||
receiveReject(from, serverMessageId, timestamp);
|
|
||||||
break;
|
|
||||||
case "accept":
|
|
||||||
receiveAccept(from, serverMessageId, timestamp);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void deliverFailedProceed(final String message) {
|
void deliverFailedProceed(final String message) {
|
||||||
Log.d(
|
Log.d(
|
||||||
Config.LOGTAG,
|
Config.LOGTAG,
|
||||||
id.account.getJid().asBareJid() + ": receive message error for proceed message ("+Strings.nullToEmpty(message)+")");
|
id.account.getJid().asBareJid()
|
||||||
|
+ ": receive message error for proceed message ("
|
||||||
|
+ Strings.nullToEmpty(message)
|
||||||
|
+ ")");
|
||||||
if (transition(State.TERMINATED_CONNECTIVITY_ERROR)) {
|
if (transition(State.TERMINATED_CONNECTIVITY_ERROR)) {
|
||||||
webRTCWrapper.close();
|
webRTCWrapper.close();
|
||||||
Log.d(
|
Log.d(
|
||||||
|
@ -1609,9 +1643,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
this.message.setTime(timestamp);
|
this.message.setTime(timestamp);
|
||||||
this.message.setCarbon(true); // indicate that call was accepted on other device
|
this.message.setCarbon(true); // indicate that call was accepted on other device
|
||||||
this.writeLogMessageSuccess(0);
|
this.writeLogMessageSuccess(0);
|
||||||
this.xmppConnectionService
|
this.xmppConnectionService.getNotificationService().cancelIncomingCallNotification();
|
||||||
.getNotificationService()
|
|
||||||
.cancelIncomingCallNotification();
|
|
||||||
this.finish();
|
this.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1749,14 +1781,14 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
private synchronized void ringingTimeout() {
|
private synchronized void ringingTimeout() {
|
||||||
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": timeout reached for ringing");
|
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": timeout reached for ringing");
|
||||||
switch (this.state) {
|
switch (this.state) {
|
||||||
case PROPOSED:
|
case PROPOSED -> {
|
||||||
message.markUnread();
|
message.markUnread();
|
||||||
rejectCallFromProposed();
|
rejectCallFromProposed();
|
||||||
break;
|
}
|
||||||
case SESSION_INITIALIZED:
|
case SESSION_INITIALIZED -> {
|
||||||
message.markUnread();
|
message.markUnread();
|
||||||
rejectCallFromSessionInitiate();
|
rejectCallFromSessionInitiate();
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
xmppConnectionService.getNotificationService().pushMissedCallNow(message);
|
xmppConnectionService.getNotificationService().pushMissedCallNow(message);
|
||||||
}
|
}
|
||||||
|
@ -1932,7 +1964,9 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
}
|
}
|
||||||
|
|
||||||
private void prepareSessionInitiate(
|
private void prepareSessionInitiate(
|
||||||
final org.webrtc.SessionDescription webRTCSessionDescription, final boolean includeCandidates, final State targetState) {
|
final org.webrtc.SessionDescription webRTCSessionDescription,
|
||||||
|
final boolean includeCandidates,
|
||||||
|
final State targetState) {
|
||||||
final SessionDescription sessionDescription =
|
final SessionDescription sessionDescription =
|
||||||
SessionDescription.parse(webRTCSessionDescription.description);
|
SessionDescription.parse(webRTCSessionDescription.description);
|
||||||
final RtpContentMap rtpContentMap = RtpContentMap.of(sessionDescription, true);
|
final RtpContentMap rtpContentMap = RtpContentMap.of(sessionDescription, true);
|
||||||
|
@ -1947,7 +1981,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
encryptSessionInitiate(rtpContentMap);
|
encryptSessionInitiate(rtpContentMap);
|
||||||
Futures.addCallback(
|
Futures.addCallback(
|
||||||
outgoingContentMapFuture,
|
outgoingContentMapFuture,
|
||||||
new FutureCallback<RtpContentMap>() {
|
new FutureCallback<>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(final RtpContentMap outgoingContentMap) {
|
public void onSuccess(final RtpContentMap outgoingContentMap) {
|
||||||
if (includeCandidates) {
|
if (includeCandidates) {
|
||||||
|
@ -1956,7 +1990,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
"including "
|
"including "
|
||||||
+ candidates.size()
|
+ candidates.size()
|
||||||
+ " candidates in session initiate");
|
+ " candidates in session initiate");
|
||||||
sendSessionInitiate(outgoingContentMap.withCandidates(candidates), targetState);
|
sendSessionInitiate(
|
||||||
|
outgoingContentMap.withCandidates(candidates), targetState);
|
||||||
webRTCWrapper.resetPendingCandidates();
|
webRTCWrapper.resetPendingCandidates();
|
||||||
} else {
|
} else {
|
||||||
sendSessionInitiate(outgoingContentMap, targetState);
|
sendSessionInitiate(outgoingContentMap, targetState);
|
||||||
|
@ -2170,59 +2205,62 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
|
|
||||||
public RtpEndUserState getEndUserState() {
|
public RtpEndUserState getEndUserState() {
|
||||||
switch (this.state) {
|
switch (this.state) {
|
||||||
case NULL:
|
case NULL, PROPOSED, SESSION_INITIALIZED -> {
|
||||||
case PROPOSED:
|
|
||||||
case SESSION_INITIALIZED:
|
|
||||||
if (isInitiator()) {
|
if (isInitiator()) {
|
||||||
return RtpEndUserState.RINGING;
|
return RtpEndUserState.RINGING;
|
||||||
} else {
|
} else {
|
||||||
return RtpEndUserState.INCOMING_CALL;
|
return RtpEndUserState.INCOMING_CALL;
|
||||||
}
|
}
|
||||||
case PROCEED:
|
}
|
||||||
|
case PROCEED -> {
|
||||||
if (isInitiator()) {
|
if (isInitiator()) {
|
||||||
return RtpEndUserState.RINGING;
|
return RtpEndUserState.RINGING;
|
||||||
} else {
|
} else {
|
||||||
return RtpEndUserState.ACCEPTING_CALL;
|
return RtpEndUserState.ACCEPTING_CALL;
|
||||||
}
|
}
|
||||||
case SESSION_INITIALIZED_PRE_APPROVED:
|
}
|
||||||
|
case SESSION_INITIALIZED_PRE_APPROVED -> {
|
||||||
if (isInitiator()) {
|
if (isInitiator()) {
|
||||||
return RtpEndUserState.RINGING;
|
return RtpEndUserState.RINGING;
|
||||||
} else {
|
} else {
|
||||||
return RtpEndUserState.CONNECTING;
|
return RtpEndUserState.CONNECTING;
|
||||||
}
|
}
|
||||||
case SESSION_ACCEPTED:
|
}
|
||||||
|
case SESSION_ACCEPTED -> {
|
||||||
final ContentAddition ca = getPendingContentAddition();
|
final ContentAddition ca = getPendingContentAddition();
|
||||||
if (ca != null && ca.direction == ContentAddition.Direction.INCOMING) {
|
if (ca != null && ca.direction == ContentAddition.Direction.INCOMING) {
|
||||||
return RtpEndUserState.INCOMING_CONTENT_ADD;
|
return RtpEndUserState.INCOMING_CONTENT_ADD;
|
||||||
}
|
}
|
||||||
return getPeerConnectionStateAsEndUserState();
|
return getPeerConnectionStateAsEndUserState();
|
||||||
case REJECTED:
|
}
|
||||||
case REJECTED_RACED:
|
case REJECTED, REJECTED_RACED, TERMINATED_DECLINED_OR_BUSY -> {
|
||||||
case TERMINATED_DECLINED_OR_BUSY:
|
|
||||||
if (isInitiator()) {
|
if (isInitiator()) {
|
||||||
return RtpEndUserState.DECLINED_OR_BUSY;
|
return RtpEndUserState.DECLINED_OR_BUSY;
|
||||||
} else {
|
} else {
|
||||||
return RtpEndUserState.ENDED;
|
return RtpEndUserState.ENDED;
|
||||||
}
|
}
|
||||||
case TERMINATED_SUCCESS:
|
}
|
||||||
case ACCEPTED:
|
case TERMINATED_SUCCESS, ACCEPTED, RETRACTED, TERMINATED_CANCEL_OR_TIMEOUT -> {
|
||||||
case RETRACTED:
|
|
||||||
case TERMINATED_CANCEL_OR_TIMEOUT:
|
|
||||||
return RtpEndUserState.ENDED;
|
return RtpEndUserState.ENDED;
|
||||||
case RETRACTED_RACED:
|
}
|
||||||
|
case RETRACTED_RACED -> {
|
||||||
if (isInitiator()) {
|
if (isInitiator()) {
|
||||||
return RtpEndUserState.ENDED;
|
return RtpEndUserState.ENDED;
|
||||||
} else {
|
} else {
|
||||||
return RtpEndUserState.RETRACTED;
|
return RtpEndUserState.RETRACTED;
|
||||||
}
|
}
|
||||||
case TERMINATED_CONNECTIVITY_ERROR:
|
}
|
||||||
|
case TERMINATED_CONNECTIVITY_ERROR -> {
|
||||||
return zeroDuration()
|
return zeroDuration()
|
||||||
? RtpEndUserState.CONNECTIVITY_ERROR
|
? RtpEndUserState.CONNECTIVITY_ERROR
|
||||||
: RtpEndUserState.CONNECTIVITY_LOST_ERROR;
|
: RtpEndUserState.CONNECTIVITY_LOST_ERROR;
|
||||||
case TERMINATED_APPLICATION_FAILURE:
|
}
|
||||||
|
case TERMINATED_APPLICATION_FAILURE -> {
|
||||||
return RtpEndUserState.APPLICATION_ERROR;
|
return RtpEndUserState.APPLICATION_ERROR;
|
||||||
case TERMINATED_SECURITY_ERROR:
|
}
|
||||||
|
case TERMINATED_SECURITY_ERROR -> {
|
||||||
return RtpEndUserState.SECURITY_ERROR;
|
return RtpEndUserState.SECURITY_ERROR;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
String.format("%s has no equivalent EndUserState", this.state));
|
String.format("%s has no equivalent EndUserState", this.state));
|
||||||
|
@ -2237,19 +2275,14 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
// be in SESSION_ACCEPTED even though the peerConnection has been torn down
|
// be in SESSION_ACCEPTED even though the peerConnection has been torn down
|
||||||
return RtpEndUserState.ENDING_CALL;
|
return RtpEndUserState.ENDING_CALL;
|
||||||
}
|
}
|
||||||
switch (state) {
|
return switch (state) {
|
||||||
case CONNECTED:
|
case CONNECTED -> RtpEndUserState.CONNECTED;
|
||||||
return RtpEndUserState.CONNECTED;
|
case NEW, CONNECTING -> RtpEndUserState.CONNECTING;
|
||||||
case NEW:
|
case CLOSED -> RtpEndUserState.ENDING_CALL;
|
||||||
case CONNECTING:
|
default -> zeroDuration()
|
||||||
return RtpEndUserState.CONNECTING;
|
? RtpEndUserState.CONNECTIVITY_ERROR
|
||||||
case CLOSED:
|
: RtpEndUserState.RECONNECTING;
|
||||||
return RtpEndUserState.ENDING_CALL;
|
};
|
||||||
default:
|
|
||||||
return zeroDuration()
|
|
||||||
? RtpEndUserState.CONNECTIVITY_ERROR
|
|
||||||
: RtpEndUserState.RECONNECTING;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ContentAddition getPendingContentAddition() {
|
public ContentAddition getPendingContentAddition() {
|
||||||
|
@ -2284,9 +2317,10 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
} else if (initiatorContentMap != null) {
|
} else if (initiatorContentMap != null) {
|
||||||
return initiatorContentMap.getMedia();
|
return initiatorContentMap.getMedia();
|
||||||
} else if (isTerminated()) {
|
} else if (isTerminated()) {
|
||||||
return Collections.emptySet(); //we might fail before we ever got a chance to set media
|
return Collections.emptySet(); // we might fail before we ever got a chance to set media
|
||||||
} else {
|
} else {
|
||||||
return Preconditions.checkNotNull(this.proposedMedia, "RTP connection has not been initialized properly");
|
return Preconditions.checkNotNull(
|
||||||
|
this.proposedMedia, "RTP connection has not been initialized properly");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2306,35 +2340,29 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
throw new IllegalStateException(String.format("%s has already been proposed", media));
|
throw new IllegalStateException(String.format("%s has already been proposed", media));
|
||||||
}
|
}
|
||||||
// TODO add state protection - can only add while ACCEPTED or so
|
// TODO add state protection - can only add while ACCEPTED or so
|
||||||
Log.d(Config.LOGTAG,"adding media: "+media);
|
Log.d(Config.LOGTAG, "adding media: " + media);
|
||||||
return webRTCWrapper.addTrack(media);
|
return webRTCWrapper.addTrack(media);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void acceptCall() {
|
public synchronized void acceptCall() {
|
||||||
switch (this.state) {
|
switch (this.state) {
|
||||||
case PROPOSED:
|
case PROPOSED -> {
|
||||||
cancelRingingTimeout();
|
cancelRingingTimeout();
|
||||||
acceptCallFromProposed();
|
acceptCallFromProposed();
|
||||||
break;
|
}
|
||||||
case SESSION_INITIALIZED:
|
case SESSION_INITIALIZED -> {
|
||||||
cancelRingingTimeout();
|
cancelRingingTimeout();
|
||||||
acceptCallFromSessionInitialized();
|
acceptCallFromSessionInitialized();
|
||||||
break;
|
}
|
||||||
case ACCEPTED:
|
case ACCEPTED -> Log.w(
|
||||||
Log.w(
|
Config.LOGTAG,
|
||||||
Config.LOGTAG,
|
id.account.getJid().asBareJid()
|
||||||
id.account.getJid().asBareJid()
|
+ ": the call has already been accepted with another client. UI was just lagging behind");
|
||||||
+ ": the call has already been accepted with another client. UI was just lagging behind");
|
case PROCEED, SESSION_ACCEPTED -> Log.w(
|
||||||
break;
|
Config.LOGTAG,
|
||||||
case PROCEED:
|
id.account.getJid().asBareJid()
|
||||||
case SESSION_ACCEPTED:
|
+ ": the call has already been accepted. user probably double tapped the UI");
|
||||||
Log.w(
|
default -> throw new IllegalStateException("Can not accept call from " + this.state);
|
||||||
Config.LOGTAG,
|
|
||||||
id.account.getJid().asBareJid()
|
|
||||||
+ ": the call has already been accepted. user probably double tapped the UI");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalStateException("Can not accept call from " + this.state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2356,14 +2384,9 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switch (this.state) {
|
switch (this.state) {
|
||||||
case PROPOSED:
|
case PROPOSED -> rejectCallFromProposed();
|
||||||
rejectCallFromProposed();
|
case SESSION_INITIALIZED -> rejectCallFromSessionInitiate();
|
||||||
break;
|
default -> throw new IllegalStateException("Can not reject call from " + this.state);
|
||||||
case SESSION_INITIALIZED:
|
|
||||||
rejectCallFromSessionInitiate();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalStateException("Can not reject call from " + this.state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2428,9 +2451,14 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupWebRTC(final Set<Media> media, final List<PeerConnection.IceServer> iceServers, final boolean trickle) throws WebRTCWrapper.InitializationException {
|
private void setupWebRTC(
|
||||||
|
final Set<Media> media,
|
||||||
|
final List<PeerConnection.IceServer> iceServers,
|
||||||
|
final boolean trickle)
|
||||||
|
throws WebRTCWrapper.InitializationException {
|
||||||
this.jingleConnectionManager.ensureConnectionIsRegistered(this);
|
this.jingleConnectionManager.ensureConnectionIsRegistered(this);
|
||||||
this.webRTCWrapper.setup(this.xmppConnectionService, AppRTCAudioManager.SpeakerPhonePreference.of(media));
|
this.webRTCWrapper.setup(
|
||||||
|
this.xmppConnectionService, AppRTCAudioManager.SpeakerPhonePreference.of(media));
|
||||||
this.webRTCWrapper.initializePeerConnection(media, iceServers, trickle);
|
this.webRTCWrapper.initializePeerConnection(media, iceServers, trickle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2606,7 +2634,10 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
final Throwable cause = Throwables.getRootCause(e);
|
final Throwable cause = Throwables.getRootCause(e);
|
||||||
webRTCWrapper.close();
|
webRTCWrapper.close();
|
||||||
if (isTerminated()) {
|
if (isTerminated()) {
|
||||||
Log.d(Config.LOGTAG, "failed to renegotiate. session was already terminated", cause);
|
Log.d(
|
||||||
|
Config.LOGTAG,
|
||||||
|
"failed to renegotiate. session was already terminated",
|
||||||
|
cause);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Log.d(Config.LOGTAG, "failed to renegotiate. sending session-terminate", cause);
|
Log.d(Config.LOGTAG, "failed to renegotiate. sending session-terminate", cause);
|
||||||
|
@ -2691,7 +2722,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
prepareOutgoingContentMap(contentAdd);
|
prepareOutgoingContentMap(contentAdd);
|
||||||
Futures.addCallback(
|
Futures.addCallback(
|
||||||
outgoingContentMapFuture,
|
outgoingContentMapFuture,
|
||||||
new FutureCallback<RtpContentMap>() {
|
new FutureCallback<>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(final RtpContentMap outgoingContentMap) {
|
public void onSuccess(final RtpContentMap outgoingContentMap) {
|
||||||
sendContentAdd(outgoingContentMap);
|
sendContentAdd(outgoingContentMap);
|
||||||
|
@ -2889,9 +2920,6 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (Arrays.asList("stun", "stuns", "turn", "turns")
|
if (Arrays.asList("stun", "stuns", "turn", "turns")
|
||||||
.contains(type)
|
.contains(type)
|
||||||
&& Arrays.asList("udp", "tcp").contains(transport)) {
|
&& Arrays.asList("udp", "tcp").contains(transport)) {
|
||||||
|
@ -2906,15 +2934,19 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
|
|
||||||
// STUN URLs do not support a query section since M110
|
// STUN URLs do not support a query section since M110
|
||||||
final String uri;
|
final String uri;
|
||||||
if (Arrays.asList("stun","stuns").contains(type)) {
|
if (Arrays.asList("stun", "stuns").contains(type)) {
|
||||||
uri = String.format("%s:%s:%s", type, IP.wrapIPv6(host),port);
|
uri =
|
||||||
|
String.format(
|
||||||
|
"%s:%s:%s",
|
||||||
|
type, IP.wrapIPv6(host), port);
|
||||||
} else {
|
} else {
|
||||||
uri = String.format(
|
uri =
|
||||||
"%s:%s:%s?transport=%s",
|
String.format(
|
||||||
type,
|
"%s:%s:%s?transport=%s",
|
||||||
IP.wrapIPv6(host),
|
type,
|
||||||
port,
|
IP.wrapIPv6(host),
|
||||||
transport);
|
port,
|
||||||
|
transport);
|
||||||
}
|
}
|
||||||
|
|
||||||
final PeerConnection.IceServer.Builder iceServerBuilder =
|
final PeerConnection.IceServer.Builder iceServerBuilder =
|
||||||
|
|
Loading…
Reference in a new issue