diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/IceServers.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/IceServers.java index d852bd5f96017f6e3c65e14a1fa309a8ff4341b8..cc383be716bab5e16a06fbfd98a13b2c3a72c77e 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/IceServers.java +++ b/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 parse(final Iq response) { + public static Collection parse(final Iq response) { if (response.getType() != Iq.Type.RESULT) { return Collections.emptySet(); } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java index 895cadcf8087d3b08a209a698720efcf49009314..13ecb8d2c60e840764df7de04692a007755df304 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java +++ b/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, final SessionDescription offer, - final Set iceServers) { + final Collection iceServers) { if (isTerminated()) { Log.w( Config.LOGTAG, @@ -1824,7 +1824,7 @@ public class JingleRtpConnection extends AbstractJingleConnection private synchronized void sendSessionInitiate( final Set media, final State targetState, - final Set iceServers) { + final Collection iceServers) { if (isTerminated()) { Log.w( Config.LOGTAG, @@ -2321,7 +2321,7 @@ public class JingleRtpConnection extends AbstractJingleConnection private void setupWebRTC( final Set media, - final Set iceServers, + final Collection 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 iceServers); + void onIceServersDiscovered(Collection iceServers); } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java index d5c3be53fe411a6c68550d04ed6285e93457b12c..09e95a7cd89e5d055e068fa48597aed5c700f03e 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java +++ b/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, - final Set iceServers, + final Collection 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 iceServers, final boolean trickle) { + final Collection 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); diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/transports/WebRTCDataChannelTransport.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/transports/WebRTCDataChannelTransport.java index 62d06f796510d53fb5c691d7af012a1f1d944105..80b0322f4727cf418d7f37de58d9cb6feb1ddbf6 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/transports/WebRTCDataChannelTransport.java +++ b/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> getIceServers() { + private ListenableFuture> getIceServers() { if (Config.DISABLE_PROXY_LOOKUP) { return Futures.immediateFuture(Collections.emptySet()); } if (xmppConnection.getFeatures().externalServiceDiscovery()) { - final SettableFuture> iceServerFuture = + final SettableFuture> 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 iceServers, final boolean trickle) { + final Collection iceServers, final boolean trickle) { final PeerConnection.RTCConfiguration rtcConfig = buildConfiguration(iceServers, trickle); final PeerConnection peerConnection = requirePeerConnectionFactory() diff --git a/src/main/java/im/conversations/android/xmpp/model/disco/external/Services.java b/src/main/java/im/conversations/android/xmpp/model/disco/external/Services.java index 61045a2b8c8dd939d288499c053f3cd5e5f45a37..ecf24606cc97bd60ff71b86f71b5674443ea705e 100644 --- a/src/main/java/im/conversations/android/xmpp/model/disco/external/Services.java +++ b/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 getIceServers() { - final var builder = new ImmutableSet.Builder(); + public Collection getIceServers() { + final var builder = new ImmutableSet.Builder(); 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(); + } } }