use modern api to retrieve ice servers

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/xmpp/jingle/IceServers.java                            | 83 
src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java                   |  3 
src/main/java/eu/siacs/conversations/xmpp/jingle/transports/WebRTCDataChannelTransport.java |  4 
src/main/java/im/conversations/android/xmpp/model/disco/external/Services.java              | 80 
4 files changed, 89 insertions(+), 81 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/xmpp/jingle/IceServers.java 🔗

@@ -1,17 +1,8 @@
 package eu.siacs.conversations.xmpp.jingle;
 
-import android.util.Log;
-import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.primitives.Ints;
-import eu.siacs.conversations.Config;
-import eu.siacs.conversations.utils.IP;
-import eu.siacs.conversations.xml.Element;
-import eu.siacs.conversations.xml.Namespace;
+import im.conversations.android.xmpp.model.disco.external.Services;
 import im.conversations.android.xmpp.model.stanza.Iq;
-import java.util.Arrays;
 import java.util.Collections;
-import java.util.List;
 import java.util.Set;
 import org.webrtc.PeerConnection;
 
@@ -21,74 +12,10 @@ public final class IceServers {
         if (response.getType() != Iq.Type.RESULT) {
             return Collections.emptySet();
         }
-        final var builder = new ImmutableSet.Builder<PeerConnection.IceServer>();
-        final Element services =
-                response.findChild("services", Namespace.EXTERNAL_SERVICE_DISCOVERY);
-        final List<Element> children =
-                services == null ? Collections.emptyList() : services.getChildren();
-        for (final Element child : children) {
-            if ("service".equals(child.getName())) {
-                final String type = child.getAttribute("type");
-                final String host = child.getAttribute("host");
-                final String sport = child.getAttribute("port");
-                final Integer port = sport == null ? null : Ints.tryParse(sport);
-                final String transport = child.getAttribute("transport");
-                final String username = child.getAttribute("username");
-                final String password = child.getAttribute("password");
-                if (Strings.isNullOrEmpty(host) || port == null) {
-                    continue;
-                }
-                if (port < 0 || port > 65535) {
-                    continue;
-                }
-
-                if (Arrays.asList("stun", "stuns", "turn", "turns").contains(type)
-                        && Arrays.asList("udp", "tcp").contains(transport)) {
-                    if (Arrays.asList("stuns", "turns").contains(type) && "udp".equals(transport)) {
-                        Log.w(
-                                Config.LOGTAG,
-                                "skipping invalid combination of udp/tls in external services");
-                        continue;
-                    }
-
-                    // STUN URLs do not support a query section since M110
-                    final String uri;
-                    if (Arrays.asList("stun", "stuns").contains(type)) {
-                        uri = String.format("%s:%s:%s", type, IP.wrapIPv6(host), port);
-                    } else {
-                        uri =
-                                String.format(
-                                        "%s:%s:%s?transport=%s",
-                                        type, IP.wrapIPv6(host), port, transport);
-                    }
-
-                    final PeerConnection.IceServer.Builder iceServerBuilder =
-                            PeerConnection.IceServer.builder(uri);
-                    iceServerBuilder.setTlsCertPolicy(
-                            PeerConnection.TlsCertPolicy.TLS_CERT_POLICY_INSECURE_NO_CHECK);
-                    if (username != null && password != null) {
-                        iceServerBuilder.setUsername(username);
-                        iceServerBuilder.setPassword(password);
-                    } else if (Arrays.asList("turn", "turns").contains(type)) {
-                        // The WebRTC spec requires throwing an
-                        // InvalidAccessError when username (from libwebrtc
-                        // source coder)
-                        // https://chromium.googlesource.com/external/webrtc/+/master/pc/ice_server_parsing.cc
-                        Log.w(
-                                Config.LOGTAG,
-                                "skipping "
-                                        + type
-                                        + "/"
-                                        + transport
-                                        + " without username and password");
-                        continue;
-                    }
-                    final PeerConnection.IceServer iceServer = iceServerBuilder.createIceServer();
-                    Log.w(Config.LOGTAG, "discovered ICE Server: " + iceServer);
-                    builder.add(iceServer);
-                }
-            }
+        final var services = response.getExtension(Services.class);
+        if (services == null) {
+            return Collections.emptySet();
         }
-        return builder.build();
+        return services.getIceServers();
     }
 }

src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java 🔗

@@ -44,6 +44,7 @@ import eu.siacs.conversations.xmpp.jingle.stanzas.Proceed;
 import eu.siacs.conversations.xmpp.jingle.stanzas.Propose;
 import eu.siacs.conversations.xmpp.jingle.stanzas.Reason;
 import eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription;
+import im.conversations.android.xmpp.model.disco.external.Services;
 import im.conversations.android.xmpp.model.jingle.Jingle;
 import im.conversations.android.xmpp.model.stanza.Iq;
 import java.util.Arrays;
@@ -2824,7 +2825,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
         if (id.account.getXmppConnection().getFeatures().externalServiceDiscovery()) {
             final Iq request = new Iq(Iq.Type.GET);
             request.setTo(id.account.getDomain());
-            request.addChild("services", Namespace.EXTERNAL_SERVICE_DISCOVERY);
+            request.addExtension(new Services());
             xmppConnectionService.sendIqPacket(
                     id.account,
                     request,

src/main/java/eu/siacs/conversations/xmpp/jingle/transports/WebRTCDataChannelTransport.java 🔗

@@ -14,12 +14,12 @@ import com.google.common.util.concurrent.MoreExecutors;
 import com.google.common.util.concurrent.SettableFuture;
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.entities.Account;
-import eu.siacs.conversations.xml.Namespace;
 import eu.siacs.conversations.xmpp.XmppConnection;
 import eu.siacs.conversations.xmpp.jingle.IceServers;
 import eu.siacs.conversations.xmpp.jingle.WebRTCWrapper;
 import eu.siacs.conversations.xmpp.jingle.stanzas.IceUdpTransportInfo;
 import eu.siacs.conversations.xmpp.jingle.stanzas.WebRTCDataChannelTransportInfo;
+import im.conversations.android.xmpp.model.disco.external.Services;
 import im.conversations.android.xmpp.model.stanza.Iq;
 import java.io.IOException;
 import java.io.InputStream;
@@ -233,7 +233,7 @@ public class WebRTCDataChannelTransport implements Transport {
                     SettableFuture.create();
             final Iq request = new Iq(Iq.Type.GET);
             request.setTo(this.account.getDomain());
-            request.addChild("services", Namespace.EXTERNAL_SERVICE_DISCOVERY);
+            request.addExtension(new Services());
             xmppConnection.sendIqPacket(
                     request,
                     (response) -> {

src/main/java/im/conversations/android/xmpp/model/disco/external/Services.java 🔗

@@ -1,7 +1,17 @@
 package im.conversations.android.xmpp.model.disco.external;
 
+import android.util.Log;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.primitives.Ints;
+import eu.siacs.conversations.Config;
+import eu.siacs.conversations.utils.IP;
 import im.conversations.android.annotation.XmlElement;
 import im.conversations.android.xmpp.model.Extension;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Set;
+import org.webrtc.PeerConnection;
 
 @XmlElement
 public class Services extends Extension {
@@ -9,4 +19,74 @@ public class Services extends Extension {
     public Services() {
         super(Services.class);
     }
+
+    public Collection<Service> getServices() {
+        return this.getExtensions(Service.class);
+    }
+
+    public Set<PeerConnection.IceServer> getIceServers() {
+        final var builder = new ImmutableSet.Builder<PeerConnection.IceServer>();
+        for (final var service : this.getServices()) {
+            final String type = service.getAttribute("type");
+            final String host = service.getAttribute("host");
+            final String sport = service.getAttribute("port");
+            final Integer port = sport == null ? null : Ints.tryParse(sport);
+            final String transport = service.getAttribute("transport");
+            final String username = service.getAttribute("username");
+            final String password = service.getAttribute("password");
+            if (Strings.isNullOrEmpty(host) || port == null) {
+                continue;
+            }
+            if (port < 0 || port > 65535) {
+                continue;
+            }
+
+            if (Arrays.asList("stun", "stuns", "turn", "turns").contains(type)
+                    && Arrays.asList("udp", "tcp").contains(transport)) {
+                if (Arrays.asList("stuns", "turns").contains(type) && "udp".equals(transport)) {
+                    Log.w(
+                            Config.LOGTAG,
+                            "skipping invalid combination of udp/tls in external services");
+                    continue;
+                }
+
+                // STUN URLs do not support a query section since M110
+                final String uri;
+                if (Arrays.asList("stun", "stuns").contains(type)) {
+                    uri = String.format("%s:%s:%s", type, IP.wrapIPv6(host), port);
+                } else {
+                    uri =
+                            String.format(
+                                    "%s:%s:%s?transport=%s",
+                                    type, IP.wrapIPv6(host), port, transport);
+                }
+
+                final PeerConnection.IceServer.Builder iceServerBuilder =
+                        PeerConnection.IceServer.builder(uri);
+                iceServerBuilder.setTlsCertPolicy(
+                        PeerConnection.TlsCertPolicy.TLS_CERT_POLICY_INSECURE_NO_CHECK);
+                if (username != null && password != null) {
+                    iceServerBuilder.setUsername(username);
+                    iceServerBuilder.setPassword(password);
+                } else if (Arrays.asList("turn", "turns").contains(type)) {
+                    // The WebRTC spec requires throwing an
+                    // InvalidAccessError when username (from libwebrtc
+                    // source coder)
+                    // https://chromium.googlesource.com/external/webrtc/+/master/pc/ice_server_parsing.cc
+                    Log.w(
+                            Config.LOGTAG,
+                            "skipping "
+                                    + type
+                                    + "/"
+                                    + transport
+                                    + " without username and password");
+                    continue;
+                }
+                final PeerConnection.IceServer iceServer = iceServerBuilder.createIceServer();
+                Log.w(Config.LOGTAG, "discovered ICE Server: " + iceServer);
+                builder.add(iceServer);
+            }
+        }
+        return builder.build();
+    }
 }