add timeout to ICE gathering

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java |  6 
src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java       | 51 
2 files changed, 37 insertions(+), 20 deletions(-)

Detailed changes

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

@@ -1528,11 +1528,10 @@ public class JingleRtpConnection extends AbstractJingleConnection
                                             + candidates.size()
                                             + " candidates in session accept");
                             sendSessionAccept(outgoingContentMap.withCandidates(candidates));
-                            webRTCWrapper.resetPendingCandidates();
                         } else {
                             sendSessionAccept(outgoingContentMap);
-                            webRTCWrapper.setIsReadyToReceiveIceCandidates(true);
                         }
+                        webRTCWrapper.setIsReadyToReceiveIceCandidates(true);
                     }
 
                     @Override
@@ -1994,11 +1993,10 @@ public class JingleRtpConnection extends AbstractJingleConnection
                                             + " candidates in session initiate");
                             sendSessionInitiate(
                                     outgoingContentMap.withCandidates(candidates), targetState);
-                            webRTCWrapper.resetPendingCandidates();
                         } else {
                             sendSessionInitiate(outgoingContentMap, targetState);
-                            webRTCWrapper.setIsReadyToReceiveIceCandidates(true);
                         }
+                        webRTCWrapper.setIsReadyToReceiveIceCandidates(true);
                     }
 
                     @Override

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

@@ -44,6 +44,8 @@ import java.util.Queue;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import javax.annotation.Nonnull;
@@ -456,11 +458,6 @@ public class WebRTCWrapper {
                 "setIsReadyToReceiveCandidates(" + ready + ") was=" + was + " is=" + is);
     }
 
-    public void resetPendingCandidates() {
-        this.readyToReceivedIceCandidates.set(true);
-        this.iceCandidates.clear();
-    }
-
     synchronized void close() {
         final PeerConnection peerConnection = this.peerConnection;
         final PeerConnectionFactory peerConnectionFactory = this.peerConnectionFactory;
@@ -579,7 +576,8 @@ public class WebRTCWrapper {
         throw new IllegalStateException("Local video track does not exist");
     }
 
-    synchronized ListenableFuture<SessionDescription> setLocalDescription(final boolean waitForCandidates) {
+    synchronized ListenableFuture<SessionDescription> setLocalDescription(
+            final boolean waitForCandidates) {
         this.setIsReadyToReceiveIceCandidates(false);
         return Futures.transformAsync(
                 getPeerConnectionFuture(),
@@ -593,16 +591,20 @@ public class WebRTCWrapper {
                             new SetSdpObserver() {
                                 @Override
                                 public void onSetSuccess() {
-                                    final var delay =
-                                            waitForCandidates
-                                                    ? iceGatheringComplete
-                                                    : Futures.immediateVoidFuture();
-                                    final var delayedSessionDescription =
-                                            Futures.transformAsync(
-                                                    delay,
-                                                    v -> getLocalDescriptionFuture(),
-                                                    MoreExecutors.directExecutor());
-                                    future.setFuture(delayedSessionDescription);
+                                    if (waitForCandidates) {
+                                        final var delay = getIceGatheringCompleteOrTimeout();
+                                        final var delayedSessionDescription =
+                                                Futures.transformAsync(
+                                                        delay,
+                                                        v -> {
+                                                            iceCandidates.clear();
+                                                            return getLocalDescriptionFuture();
+                                                        },
+                                                        MoreExecutors.directExecutor());
+                                        future.setFuture(delayedSessionDescription);
+                                    } else {
+                                        future.setFuture(getLocalDescriptionFuture());
+                                    }
                                 }
 
                                 @Override
@@ -616,6 +618,23 @@ public class WebRTCWrapper {
                 MoreExecutors.directExecutor());
     }
 
+    private ListenableFuture<Void> getIceGatheringCompleteOrTimeout() {
+        return Futures.catching(
+                Futures.withTimeout(
+                        iceGatheringComplete,
+                        2,
+                        TimeUnit.SECONDS,
+                        JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE),
+                TimeoutException.class,
+                ex -> {
+                    Log.d(
+                            EXTENDED_LOGGING_TAG,
+                            "timeout while waiting for ICE gathering to complete");
+                    return null;
+                },
+                MoreExecutors.directExecutor());
+    }
+
     private ListenableFuture<SessionDescription> getLocalDescriptionFuture() {
         return Futures.submit(
                 () -> {