store entire transport info for after session was accepted. fixes #3790

This commit is contained in:
Daniel Gultsch 2020-06-22 18:07:27 +02:00
parent a5430d5ce1
commit fada3a63c9
2 changed files with 33 additions and 27 deletions

View file

@ -39,6 +39,7 @@ import eu.siacs.conversations.services.AppRTCAudioManager;
import eu.siacs.conversations.utils.IP; 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.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;
import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket; import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
@ -47,7 +48,6 @@ import eu.siacs.conversations.xmpp.jingle.stanzas.Reason;
import eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription; 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 eu.siacs.conversations.xmpp.Jid;
public class JingleRtpConnection extends AbstractJingleConnection implements WebRTCWrapper.EventCallback { public class JingleRtpConnection extends AbstractJingleConnection implements WebRTCWrapper.EventCallback {
@ -120,7 +120,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
} }
private final WebRTCWrapper webRTCWrapper = new WebRTCWrapper(this); private final WebRTCWrapper webRTCWrapper = new WebRTCWrapper(this);
private final ArrayDeque<IceCandidate> pendingIceCandidates = new ArrayDeque<>(); private final ArrayDeque<Set<Map.Entry<String, RtpContentMap.DescriptionTransport>>> pendingIceCandidates = new ArrayDeque<>();
private final Message message; private final Message message;
private State state = State.NULL; private State state = State.NULL;
private StateTransitionException stateTransitionException; private StateTransitionException stateTransitionException;
@ -237,13 +237,12 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": improperly formatted contents; ignoring", e); Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": improperly formatted contents; ignoring", e);
return; return;
} }
final RtpContentMap rtpContentMap = isInitiator() ? this.responderRtpContentMap : this.initiatorRtpContentMap; final Set<Map.Entry<String, RtpContentMap.DescriptionTransport>> candidates = contentMap.contents.entrySet();
final Group originalGroup = rtpContentMap != null ? rtpContentMap.group : null; if (this.state == State.SESSION_ACCEPTED) {
final List<String> identificationTags = originalGroup == null ? Collections.emptyList() : originalGroup.getIdentificationTags(); processCandidates(candidates);
if (identificationTags.size() == 0) { } else {
Log.w(Config.LOGTAG, id.account.getJid().asBareJid() + ": no identification tags found in initial offer. we won't be able to calculate mLineIndices"); pendingIceCandidates.push(candidates);
} }
receiveCandidates(identificationTags, contentMap.contents.entrySet());
} else { } else {
if (isTerminated()) { if (isTerminated()) {
respondOk(jinglePacket); respondOk(jinglePacket);
@ -255,7 +254,17 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
} }
} }
private void receiveCandidates(final List<String> identificationTags, final Set<Map.Entry<String, RtpContentMap.DescriptionTransport>> contents) { private void processCandidates(final Set<Map.Entry<String, RtpContentMap.DescriptionTransport>> contents) {
final RtpContentMap rtpContentMap = isInitiator() ? this.responderRtpContentMap : this.initiatorRtpContentMap;
final Group originalGroup = rtpContentMap.group;
final List<String> identificationTags = originalGroup == null ? rtpContentMap.getNames() : originalGroup.getIdentificationTags();
if (identificationTags.size() == 0) {
Log.w(Config.LOGTAG, id.account.getJid().asBareJid() + ": no identification tags found in initial offer. we won't be able to calculate mLineIndices");
}
processCandidates(identificationTags, contents);
}
private void processCandidates(final List<String> indices, final Set<Map.Entry<String, RtpContentMap.DescriptionTransport>> contents) {
for (final Map.Entry<String, RtpContentMap.DescriptionTransport> content : contents) { for (final Map.Entry<String, RtpContentMap.DescriptionTransport> content : contents) {
final String ufrag = content.getValue().transport.getAttribute("ufrag"); final String ufrag = content.getValue().transport.getAttribute("ufrag");
for (final IceUdpTransportInfo.Candidate candidate : content.getValue().transport.getCandidates()) { for (final IceUdpTransportInfo.Candidate candidate : content.getValue().transport.getCandidates()) {
@ -267,15 +276,10 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
continue; continue;
} }
final String sdpMid = content.getKey(); final String sdpMid = content.getKey();
final int mLineIndex = identificationTags.indexOf(sdpMid); final int mLineIndex = indices.indexOf(sdpMid);
final IceCandidate iceCandidate = new IceCandidate(sdpMid, mLineIndex, sdp); final IceCandidate iceCandidate = new IceCandidate(sdpMid, mLineIndex, sdp);
if (isInState(State.SESSION_ACCEPTED)) {
Log.d(Config.LOGTAG, "received candidate: " + iceCandidate); Log.d(Config.LOGTAG, "received candidate: " + iceCandidate);
this.webRTCWrapper.addIceCandidate(iceCandidate); this.webRTCWrapper.addIceCandidate(iceCandidate);
} else {
this.pendingIceCandidates.offer(iceCandidate);
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": put ICE candidate on backlog");
}
} }
} }
} }
@ -318,8 +322,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
} }
if (transition(target, () -> this.initiatorRtpContentMap = contentMap)) { if (transition(target, () -> this.initiatorRtpContentMap = contentMap)) {
respondOk(jinglePacket); respondOk(jinglePacket);
final List<String> identificationTags = contentMap.group == null ? Collections.emptyList() : contentMap.group.getIdentificationTags(); pendingIceCandidates.push(contentMap.contents.entrySet());
receiveCandidates(identificationTags, contentMap.contents.entrySet());
if (target == State.SESSION_INITIALIZED_PRE_APPROVED) { if (target == State.SESSION_INITIALIZED_PRE_APPROVED) {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": automatically accepting session-initiate"); Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": automatically accepting session-initiate");
sendSessionAccept(); sendSessionAccept();
@ -364,8 +367,8 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
if (transition(State.SESSION_ACCEPTED)) { if (transition(State.SESSION_ACCEPTED)) {
respondOk(jinglePacket); respondOk(jinglePacket);
receiveSessionAccept(contentMap); receiveSessionAccept(contentMap);
final List<String> identificationTags = contentMap.group == null ? Collections.emptyList() : contentMap.group.getIdentificationTags(); final List<String> identificationTags = contentMap.group == null ? contentMap.getNames() : contentMap.group.getIdentificationTags();
receiveCandidates(identificationTags, contentMap.contents.entrySet()); processCandidates(identificationTags, contentMap.contents.entrySet());
} else { } else {
Log.d(Config.LOGTAG, String.format("%s: received session-accept while in state %s", id.account.getJid().asBareJid(), state)); Log.d(Config.LOGTAG, String.format("%s: received session-accept while in state %s", id.account.getJid().asBareJid(), state));
respondOk(jinglePacket); respondOk(jinglePacket);
@ -451,9 +454,8 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
private void addIceCandidatesFromBlackLog() { private void addIceCandidatesFromBlackLog() {
while (!this.pendingIceCandidates.isEmpty()) { while (!this.pendingIceCandidates.isEmpty()) {
final IceCandidate iceCandidate = this.pendingIceCandidates.poll(); processCandidates(this.pendingIceCandidates.poll());
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": added ICE candidate from back log " + iceCandidate); Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": added candidates from back log");
this.webRTCWrapper.addIceCandidate(iceCandidate);
} }
} }
@ -461,7 +463,6 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
this.responderRtpContentMap = rtpContentMap; this.responderRtpContentMap = rtpContentMap;
this.transitionOrThrow(State.SESSION_ACCEPTED); this.transitionOrThrow(State.SESSION_ACCEPTED);
final JinglePacket sessionAccept = rtpContentMap.toJinglePacket(JinglePacket.Action.SESSION_ACCEPT, id.sessionId); final JinglePacket sessionAccept = rtpContentMap.toJinglePacket(JinglePacket.Action.SESSION_ACCEPT, id.sessionId);
Log.d(Config.LOGTAG, sessionAccept.toString());
send(sessionAccept); send(sessionAccept);
} }
@ -890,7 +891,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
public synchronized void rejectCall() { public synchronized void rejectCall() {
if (isTerminated()) { if (isTerminated()) {
Log.w(Config.LOGTAG,id.account.getJid().asBareJid()+": received rejectCall() when session has already been terminated. nothing to do"); Log.w(Config.LOGTAG, id.account.getJid().asBareJid() + ": received rejectCall() when session has already been terminated. nothing to do");
return; return;
} }
switch (this.state) { switch (this.state) {

View file

@ -6,6 +6,7 @@ import com.google.common.base.Function;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.collect.Collections2; import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
@ -13,6 +14,7 @@ import com.google.common.collect.Sets;
import org.checkerframework.checker.nullness.compatqual.NullableDecl; import org.checkerframework.checker.nullness.compatqual.NullableDecl;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -23,7 +25,6 @@ import eu.siacs.conversations.xmpp.jingle.stanzas.GenericTransportInfo;
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;
import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket; import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
import eu.siacs.conversations.xmpp.jingle.stanzas.Reason;
import eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription; import eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription;
public class RtpContentMap { public class RtpContentMap {
@ -59,6 +60,10 @@ public class RtpContentMap {
})); }));
} }
public List<String> getNames() {
return ImmutableList.copyOf(contents.keySet());
}
void requireContentDescriptions() { void requireContentDescriptions() {
if (this.contents.size() == 0) { if (this.contents.size() == 0) {
throw new IllegalStateException("No contents available"); throw new IllegalStateException("No contents available");