use custom equals method for ice server set

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/xmpp/jingle/IceServers.java                            |  4 
src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java                   |  8 
src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java                         |  7 
src/main/java/eu/siacs/conversations/xmpp/jingle/transports/WebRTCDataChannelTransport.java |  8 
src/main/java/im/conversations/android/xmpp/model/disco/external/Services.java              | 48 
5 files changed, 55 insertions(+), 20 deletions(-)

Detailed changes

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

@@ -2,13 +2,13 @@ package eu.siacs.conversations.xmpp.jingle;
 
 import im.conversations.android.xmpp.model.disco.external.Services;
 import im.conversations.android.xmpp.model.stanza.Iq;
+import java.util.Collection;
 import java.util.Collections;
-import java.util.Set;
 import org.webrtc.PeerConnection;
 
 public final class IceServers {
 
-    public static Set<PeerConnection.IceServer> parse(final Iq response) {
+    public static Collection<PeerConnection.IceServer> parse(final Iq response) {
         if (response.getType() != Iq.Type.RESULT) {
             return Collections.emptySet();
         }

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

@@ -1340,7 +1340,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
     private synchronized void sendSessionAccept(
             final Set<Media> media,
             final SessionDescription offer,
-            final Set<PeerConnection.IceServer> iceServers) {
+            final Collection<PeerConnection.IceServer> iceServers) {
         if (isTerminated()) {
             Log.w(
                     Config.LOGTAG,
@@ -1824,7 +1824,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
     private synchronized void sendSessionInitiate(
             final Set<Media> media,
             final State targetState,
-            final Set<PeerConnection.IceServer> iceServers) {
+            final Collection<PeerConnection.IceServer> iceServers) {
         if (isTerminated()) {
             Log.w(
                     Config.LOGTAG,
@@ -2321,7 +2321,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
 
     private void setupWebRTC(
             final Set<Media> media,
-            final Set<PeerConnection.IceServer> iceServers,
+            final Collection<PeerConnection.IceServer> iceServers,
             final boolean trickle)
             throws WebRTCWrapper.InitializationException {
         this.jingleConnectionManager.ensureConnectionIsRegistered(this);
@@ -2957,6 +2957,6 @@ public class JingleRtpConnection extends AbstractJingleConnection
     }
 
     private interface OnIceServersDiscovered {
-        void onIceServersDiscovered(Set<PeerConnection.IceServer> iceServers);
+        void onIceServersDiscovered(Collection<PeerConnection.IceServer> iceServers);
     }
 }

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

@@ -13,6 +13,7 @@ import com.google.common.util.concurrent.MoreExecutors;
 import com.google.common.util.concurrent.SettableFuture;
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.services.XmppConnectionService;
+import java.util.Collection;
 import java.util.LinkedList;
 import java.util.Queue;
 import java.util.Set;
@@ -230,7 +231,7 @@ public class WebRTCWrapper {
 
     synchronized void initializePeerConnection(
             final Set<Media> media,
-            final Set<PeerConnection.IceServer> iceServers,
+            final Collection<PeerConnection.IceServer> iceServers,
             final boolean trickle)
             throws InitializationException {
         Preconditions.checkState(this.eglBase != null);
@@ -373,7 +374,7 @@ public class WebRTCWrapper {
     }
 
     public static PeerConnection.RTCConfiguration buildConfiguration(
-            final Set<PeerConnection.IceServer> iceServers, final boolean trickle) {
+            final Collection<PeerConnection.IceServer> iceServers, final boolean trickle) {
         final PeerConnection.RTCConfiguration rtcConfig =
                 new PeerConnection.RTCConfiguration(ImmutableList.copyOf(iceServers));
         rtcConfig.tcpCandidatePolicy =
@@ -749,7 +750,7 @@ public class WebRTCWrapper {
         }
     }
 
-    static class InitializationException extends Exception {
+    public static class InitializationException extends Exception {
 
         private InitializationException(final String message, final Throwable throwable) {
             super(message, throwable);

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

@@ -30,11 +30,11 @@ import java.io.PipedOutputStream;
 import java.nio.ByteBuffer;
 import java.nio.channels.Channels;
 import java.nio.channels.WritableByteChannel;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Queue;
-import java.util.Set;
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
@@ -224,12 +224,12 @@ public class WebRTCDataChannelTransport implements Transport {
         }
     }
 
-    private ListenableFuture<Set<PeerConnection.IceServer>> getIceServers() {
+    private ListenableFuture<Collection<PeerConnection.IceServer>> getIceServers() {
         if (Config.DISABLE_PROXY_LOOKUP) {
             return Futures.immediateFuture(Collections.emptySet());
         }
         if (xmppConnection.getFeatures().externalServiceDiscovery()) {
-            final SettableFuture<Set<PeerConnection.IceServer>> iceServerFuture =
+            final SettableFuture<Collection<PeerConnection.IceServer>> iceServerFuture =
                     SettableFuture.create();
             final Iq request = new Iq(Iq.Type.GET);
             request.setTo(this.account.getDomain());
@@ -254,7 +254,7 @@ public class WebRTCDataChannelTransport implements Transport {
     }
 
     private PeerConnection createPeerConnection(
-            final Set<PeerConnection.IceServer> iceServers, final boolean trickle) {
+            final Collection<PeerConnection.IceServer> iceServers, final boolean trickle) {
         final PeerConnection.RTCConfiguration rtcConfig = buildConfiguration(iceServers, trickle);
         final PeerConnection peerConnection =
                 requirePeerConnectionFactory()

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

@@ -1,7 +1,12 @@
 package im.conversations.android.xmpp.model.disco.external;
 
 import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import com.google.common.base.Objects;
 import com.google.common.base.Strings;
+import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.primitives.Ints;
 import eu.siacs.conversations.Config;
@@ -10,7 +15,6 @@ 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
@@ -24,8 +28,8 @@ public class Services extends Extension {
         return this.getExtensions(Service.class);
     }
 
-    public Set<PeerConnection.IceServer> getIceServers() {
-        final var builder = new ImmutableSet.Builder<PeerConnection.IceServer>();
+    public Collection<PeerConnection.IceServer> getIceServers() {
+        final var builder = new ImmutableSet.Builder<IceServerWrapper>();
         for (final var service : this.getServices()) {
             final String type = service.getAttribute("type");
             final String host = service.getAttribute("host");
@@ -70,8 +74,7 @@ public class Services extends Extension {
                     iceServerBuilder.setPassword(password);
                 } else if (Arrays.asList("turn", "turns").contains(type)) {
                     // The WebRTC spec requires throwing an
-                    // InvalidAccessError when username (from libwebrtc
-                    // source coder)
+                    // InvalidAccessError on empty username or password
                     // https://chromium.googlesource.com/external/webrtc/+/master/pc/ice_server_parsing.cc
                     Log.w(
                             Config.LOGTAG,
@@ -82,11 +85,42 @@ public class Services extends Extension {
                                     + " without username and password");
                     continue;
                 }
-                final PeerConnection.IceServer iceServer = iceServerBuilder.createIceServer();
+                final var iceServer = new IceServerWrapper(iceServerBuilder.createIceServer());
                 Log.w(Config.LOGTAG, "discovered ICE Server: " + iceServer);
                 builder.add(iceServer);
             }
         }
-        return builder.build();
+        final var set = builder.build();
+        Log.d(Config.LOGTAG, "discovered " + set.size() + " ice servers");
+        return Collections2.transform(set, i -> i.iceServer);
+    }
+
+    private static class IceServerWrapper {
+
+        private final PeerConnection.IceServer iceServer;
+
+        private IceServerWrapper(final PeerConnection.IceServer iceServer) {
+            this.iceServer = iceServer;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof IceServerWrapper that)) return false;
+            return Objects.equal(iceServer.urls, that.iceServer.urls)
+                    && Objects.equal(iceServer.username, that.iceServer.username)
+                    && Objects.equal(iceServer.password, that.iceServer.password);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hashCode(iceServer.urls, iceServer.urls, iceServer.password);
+        }
+
+        @Override
+        @NonNull
+        public String toString() {
+            return this.iceServer.toString();
+        }
     }
 }