bring back ICE Renomination via negotiation
This commit is contained in:
parent
d235633cc7
commit
fd4b8ba188
|
@ -65,5 +65,6 @@ public final class Namespace {
|
||||||
public static final String PARS = "urn:xmpp:pars:0";
|
public static final String PARS = "urn:xmpp:pars:0";
|
||||||
public static final String EASY_ONBOARDING_INVITE = "urn:xmpp:invite#invite";
|
public static final String EASY_ONBOARDING_INVITE = "urn:xmpp:invite#invite";
|
||||||
public static final String OMEMO_DTLS_SRTP_VERIFICATION = "http://gultsch.de/xmpp/drafts/omemo/dlts-srtp-verification";
|
public static final String OMEMO_DTLS_SRTP_VERIFICATION = "http://gultsch.de/xmpp/drafts/omemo/dlts-srtp-verification";
|
||||||
|
public static final String JINGLE_TRANSPORT_ICE_OPTION = "http://gultsch.de/xmpp/drafts/jingle/transports/ice-udp/option";
|
||||||
public static final String UNIFIED_PUSH = "http://gultsch.de/xmpp/drafts/unified-push";
|
public static final String UNIFIED_PUSH = "http://gultsch.de/xmpp/drafts/unified-push";
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import com.google.common.collect.Collections2;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
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;
|
||||||
|
@ -237,6 +238,9 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
case CONTENT_REMOVE:
|
case CONTENT_REMOVE:
|
||||||
receiveContentRemove(jinglePacket);
|
receiveContentRemove(jinglePacket);
|
||||||
break;
|
break;
|
||||||
|
case CONTENT_MODIFY:
|
||||||
|
receiveContentModify(jinglePacket);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
respondOk(jinglePacket);
|
respondOk(jinglePacket);
|
||||||
Log.d(
|
Log.d(
|
||||||
|
@ -507,6 +511,14 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
||||||
+ ContentAddition.summary(receivedContentAccept));
|
+ ContentAddition.summary(receivedContentAccept));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void receiveContentModify(final JinglePacket jinglePacket) {
|
||||||
|
final Map<String, Content.Senders> modification =
|
||||||
|
Maps.transformEntries(
|
||||||
|
jinglePacket.getJingleContents(), (key, value) -> value.getSenders());
|
||||||
|
respondOk(jinglePacket);
|
||||||
|
Log.d(Config.LOGTAG, "receiveContentModification(" + modification + ")");
|
||||||
|
}
|
||||||
|
|
||||||
private void receiveContentReject(final JinglePacket jinglePacket) {
|
private void receiveContentReject(final JinglePacket jinglePacket) {
|
||||||
final RtpContentMap receivedContentReject;
|
final RtpContentMap receivedContentReject;
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -11,23 +11,26 @@ import com.google.common.base.Strings;
|
||||||
import com.google.common.collect.ArrayListMultimap;
|
import com.google.common.collect.ArrayListMultimap;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import eu.siacs.conversations.Config;
|
import eu.siacs.conversations.Config;
|
||||||
import eu.siacs.conversations.xml.Namespace;
|
import eu.siacs.conversations.xml.Namespace;
|
||||||
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.RtpDescription;
|
import eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class SessionDescription {
|
public class SessionDescription {
|
||||||
|
|
||||||
public static final String LINE_DIVIDER = "\r\n";
|
public static final String LINE_DIVIDER = "\r\n";
|
||||||
private static final String HARDCODED_MEDIA_PROTOCOL =
|
private static final String HARDCODED_MEDIA_PROTOCOL =
|
||||||
"UDP/TLS/RTP/SAVPF"; // probably only true for DTLS-SRTP aka when we have a fingerprint
|
"UDP/TLS/RTP/SAVPF"; // probably only true for DTLS-SRTP aka when we have a fingerprint
|
||||||
private static final int HARDCODED_MEDIA_PORT = 9;
|
private static final int HARDCODED_MEDIA_PORT = 9;
|
||||||
private static final String HARDCODED_ICE_OPTIONS = "trickle";
|
private static final Collection<String> HARDCODED_ICE_OPTIONS =
|
||||||
|
Collections.singleton("trickle");
|
||||||
private static final String HARDCODED_CONNECTION = "IN IP4 0.0.0.0";
|
private static final String HARDCODED_CONNECTION = "IN IP4 0.0.0.0";
|
||||||
|
|
||||||
public final int version;
|
public final int version;
|
||||||
|
@ -128,7 +131,8 @@ public class SessionDescription {
|
||||||
return sessionDescriptionBuilder.createSessionDescription();
|
return sessionDescriptionBuilder.createSessionDescription();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SessionDescription of(final RtpContentMap contentMap, final boolean isInitiatorContentMap) {
|
public static SessionDescription of(
|
||||||
|
final RtpContentMap contentMap, final boolean isInitiatorContentMap) {
|
||||||
final SessionDescriptionBuilder sessionDescriptionBuilder = new SessionDescriptionBuilder();
|
final SessionDescriptionBuilder sessionDescriptionBuilder = new SessionDescriptionBuilder();
|
||||||
final ArrayListMultimap<String, String> attributeMap = ArrayListMultimap.create();
|
final ArrayListMultimap<String, String> attributeMap = ArrayListMultimap.create();
|
||||||
final ImmutableList.Builder<Media> mediaListBuilder = new ImmutableList.Builder<>();
|
final ImmutableList.Builder<Media> mediaListBuilder = new ImmutableList.Builder<>();
|
||||||
|
@ -166,7 +170,10 @@ public class SessionDescription {
|
||||||
}
|
}
|
||||||
checkNoWhitespace(pwd, "pwd value must not contain any whitespaces");
|
checkNoWhitespace(pwd, "pwd value must not contain any whitespaces");
|
||||||
mediaAttributes.put("ice-pwd", pwd);
|
mediaAttributes.put("ice-pwd", pwd);
|
||||||
mediaAttributes.put("ice-options", HARDCODED_ICE_OPTIONS);
|
final List<String> negotiatedIceOptions = transport.getIceOptions();
|
||||||
|
final Collection<String> iceOptions =
|
||||||
|
negotiatedIceOptions.isEmpty() ? HARDCODED_ICE_OPTIONS : negotiatedIceOptions;
|
||||||
|
mediaAttributes.put("ice-options", Joiner.on(' ').join(iceOptions));
|
||||||
final IceUdpTransportInfo.Fingerprint fingerprint = transport.getFingerprint();
|
final IceUdpTransportInfo.Fingerprint fingerprint = transport.getFingerprint();
|
||||||
if (fingerprint != null) {
|
if (fingerprint != null) {
|
||||||
mediaAttributes.put(
|
mediaAttributes.put(
|
||||||
|
@ -291,13 +298,15 @@ public class SessionDescription {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"A source specific media attribute is missing its value");
|
"A source specific media attribute is missing its value");
|
||||||
}
|
}
|
||||||
mediaAttributes.put("ssrc", id + " " + parameterName + ":" + parameterValue.trim());
|
mediaAttributes.put(
|
||||||
|
"ssrc", id + " " + parameterName + ":" + parameterValue.trim());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mediaAttributes.put("mid", name);
|
mediaAttributes.put("mid", name);
|
||||||
|
|
||||||
mediaAttributes.put(descriptionTransport.senders.asMediaAttribute(isInitiatorContentMap), "");
|
mediaAttributes.put(
|
||||||
|
descriptionTransport.senders.asMediaAttribute(isInitiatorContentMap), "");
|
||||||
if (description.hasChild("rtcp-mux", Namespace.JINGLE_APPS_RTP) || group != null) {
|
if (description.hasChild("rtcp-mux", Namespace.JINGLE_APPS_RTP) || group != null) {
|
||||||
mediaAttributes.put("rtcp-mux", "");
|
mediaAttributes.put("rtcp-mux", "");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,23 @@
|
||||||
package eu.siacs.conversations.xmpp.jingle.stanzas;
|
package eu.siacs.conversations.xmpp.jingle.stanzas;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import com.google.common.base.Joiner;
|
import com.google.common.base.Joiner;
|
||||||
import com.google.common.base.MoreObjects;
|
import com.google.common.base.MoreObjects;
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.google.common.base.Splitter;
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.collect.ArrayListMultimap;
|
import com.google.common.collect.ArrayListMultimap;
|
||||||
import com.google.common.collect.Collections2;
|
import com.google.common.collect.Collections2;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Hashtable;
|
import java.util.Hashtable;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
@ -20,6 +26,7 @@ import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.Config;
|
||||||
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.jingle.SessionDescription;
|
import eu.siacs.conversations.xmpp.jingle.SessionDescription;
|
||||||
|
@ -59,6 +66,9 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
|
||||||
if (fingerprint != null) {
|
if (fingerprint != null) {
|
||||||
iceUdpTransportInfo.addChild(fingerprint);
|
iceUdpTransportInfo.addChild(fingerprint);
|
||||||
}
|
}
|
||||||
|
for (final String iceOption : IceOption.of(media)) {
|
||||||
|
iceUdpTransportInfo.addChild(new IceOption(iceOption));
|
||||||
|
}
|
||||||
return iceUdpTransportInfo;
|
return iceUdpTransportInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,6 +86,16 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
|
||||||
return fingerprint == null ? null : Fingerprint.upgrade(fingerprint);
|
return fingerprint == null ? null : Fingerprint.upgrade(fingerprint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<String> getIceOptions() {
|
||||||
|
final ImmutableList.Builder<String> optionBuilder = new ImmutableList.Builder<>();
|
||||||
|
for(final Element child : this.children) {
|
||||||
|
if (Namespace.JINGLE_TRANSPORT_ICE_OPTION.equals(child.getNamespace()) && IceOption.WELL_KNOWN.contains(child.getName())) {
|
||||||
|
optionBuilder.add(child.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return optionBuilder.build();
|
||||||
|
}
|
||||||
|
|
||||||
public Credentials getCredentials() {
|
public Credentials getCredentials() {
|
||||||
final String ufrag = this.getAttribute("ufrag");
|
final String ufrag = this.getAttribute("ufrag");
|
||||||
final String password = this.getAttribute("pwd");
|
final String password = this.getAttribute("pwd");
|
||||||
|
@ -408,4 +428,29 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
|
||||||
throw new IllegalStateException(this.name() + " can not be flipped");
|
throw new IllegalStateException(this.name() + " can not be flipped");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class IceOption extends Element {
|
||||||
|
|
||||||
|
public static final List<String> WELL_KNOWN = Arrays.asList("trickle", "renomination");
|
||||||
|
|
||||||
|
public IceOption(final String name) {
|
||||||
|
super(name, Namespace.JINGLE_TRANSPORT_ICE_OPTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Collection<String> of(SessionDescription.Media media) {
|
||||||
|
final String iceOptions = Iterables.getFirst(media.attributes.get("ice-options"), null);
|
||||||
|
if (Strings.isNullOrEmpty(iceOptions)) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
final ImmutableList.Builder<String> optionBuilder = new ImmutableList.Builder<>();
|
||||||
|
for (final String iceOption : Splitter.on(' ').split(iceOptions)) {
|
||||||
|
if (WELL_KNOWN.contains(iceOption)) {
|
||||||
|
optionBuilder.add(iceOption);
|
||||||
|
} else {
|
||||||
|
Log.w(Config.LOGTAG, "unrecognized ice option: " + iceOption);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return optionBuilder.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue