JingleRtpConnection code clean up

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java | 534 
1 file changed, 283 insertions(+), 251 deletions(-)

Detailed changes

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

@@ -18,7 +18,6 @@ import com.google.common.collect.ImmutableMultimap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Maps;
-import com.google.common.collect.Multimap;
 import com.google.common.collect.Sets;
 import com.google.common.primitives.Ints;
 import com.google.common.util.concurrent.FutureCallback;
@@ -26,23 +25,6 @@ import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.MoreExecutors;
 
-import org.webrtc.EglBase;
-import org.webrtc.IceCandidate;
-import org.webrtc.PeerConnection;
-import org.webrtc.VideoTrack;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Queue;
-import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-
 import eu.siacs.conversations.BuildConfig;
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.crypto.axolotl.AxolotlService;
@@ -61,7 +43,6 @@ import eu.siacs.conversations.utils.IP;
 import eu.siacs.conversations.xml.Element;
 import eu.siacs.conversations.xml.Namespace;
 import eu.siacs.conversations.xmpp.Jid;
-import eu.siacs.conversations.xmpp.OnIqPacketReceived;
 import eu.siacs.conversations.xmpp.jingle.stanzas.Content;
 import eu.siacs.conversations.xmpp.jingle.stanzas.Group;
 import eu.siacs.conversations.xmpp.jingle.stanzas.IceUdpTransportInfo;
@@ -73,6 +54,23 @@ import eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription;
 import eu.siacs.conversations.xmpp.stanzas.IqPacket;
 import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
 
+import org.webrtc.EglBase;
+import org.webrtc.IceCandidate;
+import org.webrtc.PeerConnection;
+import org.webrtc.VideoTrack;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
 public class JingleRtpConnection extends AbstractJingleConnection
         implements WebRTCWrapper.EventCallback {
 
@@ -195,64 +193,37 @@ public class JingleRtpConnection extends AbstractJingleConnection
     }
 
     private static State reasonToState(Reason reason) {
-        switch (reason) {
-            case SUCCESS:
-                return State.TERMINATED_SUCCESS;
-            case DECLINE:
-            case BUSY:
-                return State.TERMINATED_DECLINED_OR_BUSY;
-            case CANCEL:
-            case TIMEOUT:
-                return State.TERMINATED_CANCEL_OR_TIMEOUT;
-            case SECURITY_ERROR:
-                return State.TERMINATED_SECURITY_ERROR;
-            case FAILED_APPLICATION:
-            case UNSUPPORTED_TRANSPORTS:
-            case UNSUPPORTED_APPLICATIONS:
-                return State.TERMINATED_APPLICATION_FAILURE;
-            default:
-                return State.TERMINATED_CONNECTIVITY_ERROR;
-        }
+        return switch (reason) {
+            case SUCCESS -> State.TERMINATED_SUCCESS;
+            case DECLINE, BUSY -> State.TERMINATED_DECLINED_OR_BUSY;
+            case CANCEL, TIMEOUT -> State.TERMINATED_CANCEL_OR_TIMEOUT;
+            case SECURITY_ERROR -> State.TERMINATED_SECURITY_ERROR;
+            case FAILED_APPLICATION, UNSUPPORTED_TRANSPORTS, UNSUPPORTED_APPLICATIONS -> State
+                    .TERMINATED_APPLICATION_FAILURE;
+            default -> State.TERMINATED_CONNECTIVITY_ERROR;
+        };
     }
 
     @Override
     synchronized void deliverPacket(final JinglePacket jinglePacket) {
         switch (jinglePacket.getAction()) {
-            case SESSION_INITIATE:
-                receiveSessionInitiate(jinglePacket);
-                break;
-            case TRANSPORT_INFO:
-                receiveTransportInfo(jinglePacket);
-                break;
-            case SESSION_ACCEPT:
-                receiveSessionAccept(jinglePacket);
-                break;
-            case SESSION_TERMINATE:
-                receiveSessionTerminate(jinglePacket);
-                break;
-            case CONTENT_ADD:
-                receiveContentAdd(jinglePacket);
-                break;
-            case CONTENT_ACCEPT:
-                receiveContentAccept(jinglePacket);
-                break;
-            case CONTENT_REJECT:
-                receiveContentReject(jinglePacket);
-                break;
-            case CONTENT_REMOVE:
-                receiveContentRemove(jinglePacket);
-                break;
-            case CONTENT_MODIFY:
-                receiveContentModify(jinglePacket);
-                break;
-            default:
+            case SESSION_INITIATE -> receiveSessionInitiate(jinglePacket);
+            case TRANSPORT_INFO -> receiveTransportInfo(jinglePacket);
+            case SESSION_ACCEPT -> receiveSessionAccept(jinglePacket);
+            case SESSION_TERMINATE -> receiveSessionTerminate(jinglePacket);
+            case CONTENT_ADD -> receiveContentAdd(jinglePacket);
+            case CONTENT_ACCEPT -> receiveContentAccept(jinglePacket);
+            case CONTENT_REJECT -> receiveContentReject(jinglePacket);
+            case CONTENT_REMOVE -> receiveContentRemove(jinglePacket);
+            case CONTENT_MODIFY -> receiveContentModify(jinglePacket);
+            default -> {
                 respondOk(jinglePacket);
                 Log.d(
                         Config.LOGTAG,
                         String.format(
                                 "%s: received unhandled jingle action %s",
                                 id.account.getJid().asBareJid(), jinglePacket.getAction()));
-                break;
+            }
         }
     }
 
@@ -354,15 +325,22 @@ public class JingleRtpConnection extends AbstractJingleConnection
         final Set<Map.Entry<String, RtpContentMap.DescriptionTransport>> candidates =
                 contentMap.contents.entrySet();
         final RtpContentMap remote = getRemoteContentMap();
-        final Set<String> remoteContentIds = remote == null ? Collections.emptySet() : remote.contents.keySet();
+        final Set<String> remoteContentIds =
+                remote == null ? Collections.emptySet() : remote.contents.keySet();
         if (Collections.disjoint(remoteContentIds, contentMap.contents.keySet())) {
-            Log.d(Config.LOGTAG,"received transport-info for unknown contents "+contentMap.contents.keySet()+" (known: "+remoteContentIds+")");
+            Log.d(
+                    Config.LOGTAG,
+                    "received transport-info for unknown contents "
+                            + contentMap.contents.keySet()
+                            + " (known: "
+                            + remoteContentIds
+                            + ")");
             respondOk(jinglePacket);
             pendingIceCandidates.addAll(candidates);
             return;
         }
         if (this.state != State.SESSION_ACCEPTED) {
-            Log.d(Config.LOGTAG,"received transport-info prematurely. adding to backlog");
+            Log.d(Config.LOGTAG, "received transport-info prematurely. adding to backlog");
             respondOk(jinglePacket);
             pendingIceCandidates.addAll(candidates);
             return;
@@ -401,26 +379,31 @@ public class JingleRtpConnection extends AbstractJingleConnection
             final boolean hasFullTransportInfo = modification.hasFullTransportInfo();
             final ListenableFuture<RtpContentMap> future =
                     receiveRtpContentMap(
-                            modification, this.omemoVerification.hasFingerprint() && hasFullTransportInfo);
-            Futures.addCallback(future, new FutureCallback<RtpContentMap>() {
-                @Override
-                public void onSuccess(final RtpContentMap rtpContentMap) {
-                    receiveContentAdd(jinglePacket, rtpContentMap);
-                }
+                            modification,
+                            this.omemoVerification.hasFingerprint() && hasFullTransportInfo);
+            Futures.addCallback(
+                    future,
+                    new FutureCallback<>() {
+                        @Override
+                        public void onSuccess(final RtpContentMap rtpContentMap) {
+                            receiveContentAdd(jinglePacket, rtpContentMap);
+                        }
 
-                @Override
-                public void onFailure(@NonNull Throwable throwable) {
-                    respondOk(jinglePacket);
-                    final Throwable rootCause = Throwables.getRootCause(throwable);
-                    Log.d(
-                            Config.LOGTAG,
-                            id.account.getJid().asBareJid()
-                                    + ": improperly formatted contents in content-add",
-                            throwable);
-                    webRTCWrapper.close();
-                    sendSessionTerminate(Reason.ofThrowable(rootCause), rootCause.getMessage());
-                }
-            }, MoreExecutors.directExecutor());
+                        @Override
+                        public void onFailure(@NonNull Throwable throwable) {
+                            respondOk(jinglePacket);
+                            final Throwable rootCause = Throwables.getRootCause(throwable);
+                            Log.d(
+                                    Config.LOGTAG,
+                                    id.account.getJid().asBareJid()
+                                            + ": improperly formatted contents in content-add",
+                                    throwable);
+                            webRTCWrapper.close();
+                            sendSessionTerminate(
+                                    Reason.ofThrowable(rootCause), rootCause.getMessage());
+                        }
+                    },
+                    MoreExecutors.directExecutor());
         } else {
             terminateWithOutOfOrder(jinglePacket);
         }
@@ -508,19 +491,24 @@ public class JingleRtpConnection extends AbstractJingleConnection
             final boolean hasFullTransportInfo = receivedContentAccept.hasFullTransportInfo();
             final ListenableFuture<RtpContentMap> future =
                     receiveRtpContentMap(
-                            receivedContentAccept, this.omemoVerification.hasFingerprint() && hasFullTransportInfo);
-            Futures.addCallback(future, new FutureCallback<RtpContentMap>() {
-                @Override
-                public void onSuccess(final RtpContentMap result) {
-                    receiveContentAccept(result);
-                }
+                            receivedContentAccept,
+                            this.omemoVerification.hasFingerprint() && hasFullTransportInfo);
+            Futures.addCallback(
+                    future,
+                    new FutureCallback<>() {
+                        @Override
+                        public void onSuccess(final RtpContentMap result) {
+                            receiveContentAccept(result);
+                        }
 
-                @Override
-                public void onFailure(@NonNull final Throwable throwable) {
-                    webRTCWrapper.close();
-                    sendSessionTerminate(Reason.ofThrowable(throwable), throwable.getMessage());
-                }
-            }, MoreExecutors.directExecutor());
+                        @Override
+                        public void onFailure(@NonNull final Throwable throwable) {
+                            webRTCWrapper.close();
+                            sendSessionTerminate(
+                                    Reason.ofThrowable(throwable), throwable.getMessage());
+                        }
+                    },
+                    MoreExecutors.directExecutor());
         } else {
             Log.d(Config.LOGTAG, "received content-accept did not match our outgoing content-add");
             terminateWithOutOfOrder(jinglePacket);
@@ -573,25 +561,34 @@ public class JingleRtpConnection extends AbstractJingleConnection
         final boolean isInitiator = isInitiator();
         final RtpContentMap currentOutgoing = this.outgoingContentAdd;
         final RtpContentMap remoteContentMap = this.getRemoteContentMap();
-        final Set<String> currentOutgoingMediaIds = currentOutgoing == null ? Collections.emptySet() : currentOutgoing.contents.keySet();
+        final Set<String> currentOutgoingMediaIds =
+                currentOutgoing == null
+                        ? Collections.emptySet()
+                        : currentOutgoing.contents.keySet();
         Log.d(Config.LOGTAG, "receiveContentModification(" + modification + ")");
         if (currentOutgoing != null && currentOutgoingMediaIds.containsAll(modification.keySet())) {
             respondOk(jinglePacket);
             final RtpContentMap modifiedContentMap;
             try {
-                modifiedContentMap = currentOutgoing.modifiedSendersChecked(isInitiator, modification);
+                modifiedContentMap =
+                        currentOutgoing.modifiedSendersChecked(isInitiator, modification);
             } catch (final IllegalArgumentException e) {
                 webRTCWrapper.close();
                 sendSessionTerminate(Reason.FAILED_APPLICATION, e.getMessage());
                 return;
             }
             this.outgoingContentAdd = modifiedContentMap;
-            Log.d(Config.LOGTAG, id.account.getJid().asBareJid()+": processed content-modification for pending content-add");
-        } else if (remoteContentMap != null && remoteContentMap.contents.keySet().containsAll(modification.keySet())) {
+            Log.d(
+                    Config.LOGTAG,
+                    id.account.getJid().asBareJid()
+                            + ": processed content-modification for pending content-add");
+        } else if (remoteContentMap != null
+                && remoteContentMap.contents.keySet().containsAll(modification.keySet())) {
             respondOk(jinglePacket);
             final RtpContentMap modifiedRemoteContentMap;
             try {
-                modifiedRemoteContentMap = remoteContentMap.modifiedSendersChecked(isInitiator, modification);
+                modifiedRemoteContentMap =
+                        remoteContentMap.modifiedSendersChecked(isInitiator, modification);
             } catch (final IllegalArgumentException e) {
                 webRTCWrapper.close();
                 sendSessionTerminate(Reason.FAILED_APPLICATION, e.getMessage());
@@ -601,20 +598,27 @@ public class JingleRtpConnection extends AbstractJingleConnection
             try {
                 offer = SessionDescription.of(modifiedRemoteContentMap, !isInitiator());
             } catch (final IllegalArgumentException | NullPointerException e) {
-                Log.d(Config.LOGTAG, id.getAccount().getJid().asBareJid() + ": unable convert offer from content-modify to SDP", e);
+                Log.d(
+                        Config.LOGTAG,
+                        id.getAccount().getJid().asBareJid()
+                                + ": unable convert offer from content-modify to SDP",
+                        e);
                 webRTCWrapper.close();
                 sendSessionTerminate(Reason.FAILED_APPLICATION, e.getMessage());
                 return;
             }
-            Log.d(Config.LOGTAG, id.account.getJid().asBareJid()+": auto accepting content-modification");
+            Log.d(
+                    Config.LOGTAG,
+                    id.account.getJid().asBareJid() + ": auto accepting content-modification");
             this.autoAcceptContentModify(modifiedRemoteContentMap, offer);
         } else {
-            Log.d(Config.LOGTAG,"received unsupported content modification "+modification);
+            Log.d(Config.LOGTAG, "received unsupported content modification " + modification);
             respondWithItemNotFound(jinglePacket);
         }
     }
 
-    private void autoAcceptContentModify(final RtpContentMap modifiedRemoteContentMap, final SessionDescription offer) {
+    private void autoAcceptContentModify(
+            final RtpContentMap modifiedRemoteContentMap, final SessionDescription offer) {
         this.setRemoteContentMap(modifiedRemoteContentMap);
         final org.webrtc.SessionDescription sdp =
                 new org.webrtc.SessionDescription(
@@ -625,8 +629,9 @@ public class JingleRtpConnection extends AbstractJingleConnection
             final SessionDescription answer = setLocalSessionDescription();
             final RtpContentMap rtpContentMap = RtpContentMap.of(answer, isInitiator());
             modifyLocalContentMap(rtpContentMap);
-            // we do not need to send an answer but do we have to resend the candidates currently in SDP?
-            //resendCandidatesFromSdp(answer);
+            // we do not need to send an answer but do we have to resend the candidates currently in
+            // SDP?
+            // resendCandidatesFromSdp(answer);
             webRTCWrapper.setIsReadyToReceiveIceCandidates(true);
         } catch (final Exception e) {
             Log.d(Config.LOGTAG, "unable to accept content add", Throwables.getRootCause(e));
@@ -635,17 +640,20 @@ public class JingleRtpConnection extends AbstractJingleConnection
         }
     }
 
-    private static ImmutableMultimap<String, IceUdpTransportInfo.Candidate> parseCandidates(final SessionDescription answer) {
-        final ImmutableMultimap.Builder<String, IceUdpTransportInfo.Candidate> candidateBuilder = new ImmutableMultimap.Builder<>();
-        for(final SessionDescription.Media media : answer.media) {
+    private static ImmutableMultimap<String, IceUdpTransportInfo.Candidate> parseCandidates(
+            final SessionDescription answer) {
+        final ImmutableMultimap.Builder<String, IceUdpTransportInfo.Candidate> candidateBuilder =
+                new ImmutableMultimap.Builder<>();
+        for (final SessionDescription.Media media : answer.media) {
             final String mid = Iterables.getFirst(media.attributes.get("mid"), null);
             if (Strings.isNullOrEmpty(mid)) {
                 continue;
             }
-            for(final String sdpCandidate : media.attributes.get("candidate")) {
-                final IceUdpTransportInfo.Candidate candidate = IceUdpTransportInfo.Candidate.fromSdpAttributeValue(sdpCandidate, null);
+            for (final String sdpCandidate : media.attributes.get("candidate")) {
+                final IceUdpTransportInfo.Candidate candidate =
+                        IceUdpTransportInfo.Candidate.fromSdpAttributeValue(sdpCandidate, null);
                 if (candidate != null) {
-                    candidateBuilder.put(mid,candidate);
+                    candidateBuilder.put(mid, candidate);
                 }
             }
         }
@@ -677,7 +685,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
         if (ourSummary.equals(ContentAddition.summary(receivedContentReject))) {
             this.outgoingContentAdd = null;
             respondOk(jinglePacket);
-            Log.d(Config.LOGTAG,jinglePacket.toString());
+            Log.d(Config.LOGTAG, jinglePacket.toString());
             receiveContentReject(ourSummary);
         } else {
             Log.d(Config.LOGTAG, "received content-reject did not match our outgoing content-add");
@@ -813,7 +821,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
                 "Unexpected rollback condition. Senders were not uniformly none");
     }
 
-    public synchronized void acceptContentAdd(@NonNull final Set<ContentAddition.Summary> contentAddition) {
+    public synchronized void acceptContentAdd(
+            @NonNull final Set<ContentAddition.Summary> contentAddition) {
         final RtpContentMap incomingContentAdd = this.incomingContentAdd;
         if (incomingContentAdd == null) {
             throw new IllegalStateException("No incoming content add");
@@ -822,37 +831,61 @@ public class JingleRtpConnection extends AbstractJingleConnection
         if (contentAddition.equals(ContentAddition.summary(incomingContentAdd))) {
             this.incomingContentAdd = null;
             final Set<Content.Senders> senders = incomingContentAdd.getSenders();
-            Log.d(Config.LOGTAG,"senders of incoming content-add: "+senders);
+            Log.d(Config.LOGTAG, "senders of incoming content-add: " + senders);
             if (senders.equals(Content.Senders.receiveOnly(isInitiator()))) {
-                Log.d(Config.LOGTAG,"content addition is receive only. we want to upgrade to 'both'");
-                final RtpContentMap modifiedSenders = incomingContentAdd.modifiedSenders(Content.Senders.BOTH);
-                final JinglePacket proposedContentModification =  modifiedSenders.toStub().toJinglePacket(JinglePacket.Action.CONTENT_MODIFY, id.sessionId);
+                Log.d(
+                        Config.LOGTAG,
+                        "content addition is receive only. we want to upgrade to 'both'");
+                final RtpContentMap modifiedSenders =
+                        incomingContentAdd.modifiedSenders(Content.Senders.BOTH);
+                final JinglePacket proposedContentModification =
+                        modifiedSenders
+                                .toStub()
+                                .toJinglePacket(JinglePacket.Action.CONTENT_MODIFY, id.sessionId);
                 proposedContentModification.setTo(id.with);
-                xmppConnectionService.sendIqPacket(id.account, proposedContentModification, (account, response) -> {
-                    if (response.getType() == IqPacket.TYPE.RESULT) {
-                        Log.d(Config.LOGTAG,id.account.getJid().asBareJid()+": remote has accepted our upgrade to senders=both");
-                        acceptContentAdd(ContentAddition.summary(modifiedSenders), modifiedSenders);
-                    } else {
-                        Log.d(Config.LOGTAG,id.account.getJid().asBareJid()+": remote has rejected our upgrade to senders=both");
-                        acceptContentAdd(contentAddition, incomingContentAdd);
-                    }
-                });
+                xmppConnectionService.sendIqPacket(
+                        id.account,
+                        proposedContentModification,
+                        (account, response) -> {
+                            if (response.getType() == IqPacket.TYPE.RESULT) {
+                                Log.d(
+                                        Config.LOGTAG,
+                                        id.account.getJid().asBareJid()
+                                                + ": remote has accepted our upgrade to senders=both");
+                                acceptContentAdd(
+                                        ContentAddition.summary(modifiedSenders), modifiedSenders);
+                            } else {
+                                Log.d(
+                                        Config.LOGTAG,
+                                        id.account.getJid().asBareJid()
+                                                + ": remote has rejected our upgrade to senders=both");
+                                acceptContentAdd(contentAddition, incomingContentAdd);
+                            }
+                        });
             }
         } else {
-            throw new IllegalStateException("Accepted content add does not match pending content-add");
+            throw new IllegalStateException(
+                    "Accepted content add does not match pending content-add");
         }
     }
 
-    private void acceptContentAdd(@NonNull final Set<ContentAddition.Summary> contentAddition, final RtpContentMap incomingContentAdd) {
+    private void acceptContentAdd(
+            @NonNull final Set<ContentAddition.Summary> contentAddition,
+            final RtpContentMap incomingContentAdd) {
         final IceUdpTransportInfo.Setup setup = getPeerDtlsSetup();
-        final RtpContentMap modifiedContentMap = getRemoteContentMap().addContent(incomingContentAdd, setup);
+        final RtpContentMap modifiedContentMap =
+                getRemoteContentMap().addContent(incomingContentAdd, setup);
         this.setRemoteContentMap(modifiedContentMap);
 
         final SessionDescription offer;
         try {
             offer = SessionDescription.of(modifiedContentMap, !isInitiator());
         } catch (final IllegalArgumentException | NullPointerException e) {
-            Log.d(Config.LOGTAG, id.getAccount().getJid().asBareJid() + ": unable convert offer from content-add to SDP", e);
+            Log.d(
+                    Config.LOGTAG,
+                    id.getAccount().getJid().asBareJid()
+                            + ": unable convert offer from content-add to SDP",
+                    e);
             webRTCWrapper.close();
             sendSessionTerminate(Reason.FAILED_APPLICATION, e.getMessage());
             return;
@@ -893,10 +926,11 @@ public class JingleRtpConnection extends AbstractJingleConnection
             addIceCandidatesFromBlackLog();
 
             modifyLocalContentMap(rtpContentMap);
-            final ListenableFuture<RtpContentMap> future = prepareOutgoingContentMap(contentAcceptMap);
+            final ListenableFuture<RtpContentMap> future =
+                    prepareOutgoingContentMap(contentAcceptMap);
             Futures.addCallback(
                     future,
-                    new FutureCallback<RtpContentMap>() {
+                    new FutureCallback<>() {
                         @Override
                         public void onSuccess(final RtpContentMap rtpContentMap) {
                             sendContentAccept(rtpContentMap);
@@ -917,7 +951,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
     }
 
     private void sendContentAccept(final RtpContentMap contentAcceptMap) {
-        final JinglePacket jinglePacket = contentAcceptMap.toJinglePacket(JinglePacket.Action.CONTENT_ACCEPT, id.sessionId);
+        final JinglePacket jinglePacket =
+                contentAcceptMap.toJinglePacket(JinglePacket.Action.CONTENT_ACCEPT, id.sessionId);
         send(jinglePacket);
     }
 
@@ -963,7 +998,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
         // ICE-restart
         // and if that's the case we are seeing an answer.
         // This might be more spec compliant but also more error prone potentially
-        final boolean isSignalStateStable = this.webRTCWrapper.getSignalingState() == PeerConnection.SignalingState.STABLE;
+        final boolean isSignalStateStable =
+                this.webRTCWrapper.getSignalingState() == PeerConnection.SignalingState.STABLE;
         // TODO a stable signal state can be another indicator that we have an offer to restart ICE
         final boolean isOffer = rtpContentMap.emptyCandidates();
         final RtpContentMap restartContentMap;
@@ -1027,7 +1063,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
             final RtpContentMap restartContentMap,
             final boolean isOffer)
             throws ExecutionException, InterruptedException {
-        final SessionDescription sessionDescription = SessionDescription.of(restartContentMap, !isInitiator());
+        final SessionDescription sessionDescription =
+                SessionDescription.of(restartContentMap, !isInitiator());
         final org.webrtc.SessionDescription.Type type =
                 isOffer
                         ? org.webrtc.SessionDescription.Type.OFFER
@@ -1126,8 +1163,10 @@ public class JingleRtpConnection extends AbstractJingleConnection
         } catch (final Exception e) {
             return Futures.immediateFailedFuture(e);
         }
-        }
-        private ListenableFuture<RtpContentMap> receiveRtpContentMap(final RtpContentMap receivedContentMap, final boolean expectVerification) {
+    }
+
+    private ListenableFuture<RtpContentMap> receiveRtpContentMap(
+            final RtpContentMap receivedContentMap, final boolean expectVerification) {
         Log.d(
                 Config.LOGTAG,
                 "receiveRtpContentMap("
@@ -1183,7 +1222,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
         final ListenableFuture<RtpContentMap> future = receiveRtpContentMap(jinglePacket, false);
         Futures.addCallback(
                 future,
-                new FutureCallback<RtpContentMap>() {
+                new FutureCallback<>() {
                     @Override
                     public void onSuccess(@Nullable RtpContentMap rtpContentMap) {
                         receiveSessionInitiate(jinglePacket, rtpContentMap);
@@ -1272,7 +1311,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
                 receiveRtpContentMap(jinglePacket, this.omemoVerification.hasFingerprint());
         Futures.addCallback(
                 future,
-                new FutureCallback<RtpContentMap>() {
+                new FutureCallback<>() {
                     @Override
                     public void onSuccess(@Nullable RtpContentMap rtpContentMap) {
                         receiveSessionAccept(jinglePacket, rtpContentMap);
@@ -1438,7 +1477,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
         sendSessionTerminate(Reason.ofThrowable(rootCause), rootCause.getMessage());
     }
 
-    private void failureToPerformAction(final JinglePacket.Action action, final Throwable throwable) {
+    private void failureToPerformAction(
+            final JinglePacket.Action action, final Throwable throwable) {
         if (isTerminated()) {
             return;
         }
@@ -1459,7 +1499,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
     }
 
     private void prepareSessionAccept(
-            final org.webrtc.SessionDescription webRTCSessionDescription, final boolean includeCandidates) {
+            final org.webrtc.SessionDescription webRTCSessionDescription,
+            final boolean includeCandidates) {
         final SessionDescription sessionDescription =
                 SessionDescription.parse(webRTCSessionDescription.description);
         final RtpContentMap respondingRtpContentMap = RtpContentMap.of(sessionDescription, false);
@@ -1475,7 +1516,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
                 prepareOutgoingContentMap(respondingRtpContentMap);
         Futures.addCallback(
                 outgoingContentMapFuture,
-                new FutureCallback<RtpContentMap>() {
+                new FutureCallback<>() {
                     @Override
                     public void onSuccess(final RtpContentMap outgoingContentMap) {
                         if (includeCandidates) {
@@ -1548,30 +1589,23 @@ public class JingleRtpConnection extends AbstractJingleConnection
                         + ": delivered message to JingleRtpConnection "
                         + message);
         switch (message.getName()) {
-            case "propose":
-                receivePropose(from, Propose.upgrade(message), serverMessageId, timestamp);
-                break;
-            case "proceed":
-                receiveProceed(from, Proceed.upgrade(message), serverMessageId, timestamp);
-                break;
-            case "retract":
-                receiveRetract(from, serverMessageId, timestamp);
-                break;
-            case "reject":
-                receiveReject(from, serverMessageId, timestamp);
-                break;
-            case "accept":
-                receiveAccept(from, serverMessageId, timestamp);
-                break;
-            default:
-                break;
+            case "propose" -> receivePropose(
+                    from, Propose.upgrade(message), serverMessageId, timestamp);
+            case "proceed" -> receiveProceed(
+                    from, Proceed.upgrade(message), serverMessageId, timestamp);
+            case "retract" -> receiveRetract(from, serverMessageId, timestamp);
+            case "reject" -> receiveReject(from, serverMessageId, timestamp);
+            case "accept" -> receiveAccept(from, serverMessageId, timestamp);
         }
     }
 
     void deliverFailedProceed(final String message) {
         Log.d(
                 Config.LOGTAG,
-                id.account.getJid().asBareJid() + ": receive message error for proceed message ("+Strings.nullToEmpty(message)+")");
+                id.account.getJid().asBareJid()
+                        + ": receive message error for proceed message ("
+                        + Strings.nullToEmpty(message)
+                        + ")");
         if (transition(State.TERMINATED_CONNECTIVITY_ERROR)) {
             webRTCWrapper.close();
             Log.d(
@@ -1609,9 +1643,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
         this.message.setTime(timestamp);
         this.message.setCarbon(true); // indicate that call was accepted on other device
         this.writeLogMessageSuccess(0);
-        this.xmppConnectionService
-                .getNotificationService()
-                .cancelIncomingCallNotification();
+        this.xmppConnectionService.getNotificationService().cancelIncomingCallNotification();
         this.finish();
     }
 
@@ -1749,14 +1781,14 @@ public class JingleRtpConnection extends AbstractJingleConnection
     private synchronized void ringingTimeout() {
         Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": timeout reached for ringing");
         switch (this.state) {
-            case PROPOSED:
+            case PROPOSED -> {
                 message.markUnread();
                 rejectCallFromProposed();
-                break;
-            case SESSION_INITIALIZED:
+            }
+            case SESSION_INITIALIZED -> {
                 message.markUnread();
                 rejectCallFromSessionInitiate();
-                break;
+            }
         }
         xmppConnectionService.getNotificationService().pushMissedCallNow(message);
     }
@@ -1932,7 +1964,9 @@ public class JingleRtpConnection extends AbstractJingleConnection
     }
 
     private void prepareSessionInitiate(
-            final org.webrtc.SessionDescription webRTCSessionDescription, final boolean includeCandidates, final State targetState) {
+            final org.webrtc.SessionDescription webRTCSessionDescription,
+            final boolean includeCandidates,
+            final State targetState) {
         final SessionDescription sessionDescription =
                 SessionDescription.parse(webRTCSessionDescription.description);
         final RtpContentMap rtpContentMap = RtpContentMap.of(sessionDescription, true);
@@ -1947,7 +1981,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
                 encryptSessionInitiate(rtpContentMap);
         Futures.addCallback(
                 outgoingContentMapFuture,
-                new FutureCallback<RtpContentMap>() {
+                new FutureCallback<>() {
                     @Override
                     public void onSuccess(final RtpContentMap outgoingContentMap) {
                         if (includeCandidates) {
@@ -1956,7 +1990,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
                                     "including "
                                             + candidates.size()
                                             + " candidates in session initiate");
-                            sendSessionInitiate(outgoingContentMap.withCandidates(candidates), targetState);
+                            sendSessionInitiate(
+                                    outgoingContentMap.withCandidates(candidates), targetState);
                             webRTCWrapper.resetPendingCandidates();
                         } else {
                             sendSessionInitiate(outgoingContentMap, targetState);
@@ -2170,59 +2205,62 @@ public class JingleRtpConnection extends AbstractJingleConnection
 
     public RtpEndUserState getEndUserState() {
         switch (this.state) {
-            case NULL:
-            case PROPOSED:
-            case SESSION_INITIALIZED:
+            case NULL, PROPOSED, SESSION_INITIALIZED -> {
                 if (isInitiator()) {
                     return RtpEndUserState.RINGING;
                 } else {
                     return RtpEndUserState.INCOMING_CALL;
                 }
-            case PROCEED:
+            }
+            case PROCEED -> {
                 if (isInitiator()) {
                     return RtpEndUserState.RINGING;
                 } else {
                     return RtpEndUserState.ACCEPTING_CALL;
                 }
-            case SESSION_INITIALIZED_PRE_APPROVED:
+            }
+            case SESSION_INITIALIZED_PRE_APPROVED -> {
                 if (isInitiator()) {
                     return RtpEndUserState.RINGING;
                 } else {
                     return RtpEndUserState.CONNECTING;
                 }
-            case SESSION_ACCEPTED:
+            }
+            case SESSION_ACCEPTED -> {
                 final ContentAddition ca = getPendingContentAddition();
                 if (ca != null && ca.direction == ContentAddition.Direction.INCOMING) {
                     return RtpEndUserState.INCOMING_CONTENT_ADD;
                 }
                 return getPeerConnectionStateAsEndUserState();
-            case REJECTED:
-            case REJECTED_RACED:
-            case TERMINATED_DECLINED_OR_BUSY:
+            }
+            case REJECTED, REJECTED_RACED, TERMINATED_DECLINED_OR_BUSY -> {
                 if (isInitiator()) {
                     return RtpEndUserState.DECLINED_OR_BUSY;
                 } else {
                     return RtpEndUserState.ENDED;
                 }
-            case TERMINATED_SUCCESS:
-            case ACCEPTED:
-            case RETRACTED:
-            case TERMINATED_CANCEL_OR_TIMEOUT:
+            }
+            case TERMINATED_SUCCESS, ACCEPTED, RETRACTED, TERMINATED_CANCEL_OR_TIMEOUT -> {
                 return RtpEndUserState.ENDED;
-            case RETRACTED_RACED:
+            }
+            case RETRACTED_RACED -> {
                 if (isInitiator()) {
                     return RtpEndUserState.ENDED;
                 } else {
                     return RtpEndUserState.RETRACTED;
                 }
-            case TERMINATED_CONNECTIVITY_ERROR:
+            }
+            case TERMINATED_CONNECTIVITY_ERROR -> {
                 return zeroDuration()
                         ? RtpEndUserState.CONNECTIVITY_ERROR
                         : RtpEndUserState.CONNECTIVITY_LOST_ERROR;
-            case TERMINATED_APPLICATION_FAILURE:
+            }
+            case TERMINATED_APPLICATION_FAILURE -> {
                 return RtpEndUserState.APPLICATION_ERROR;
-            case TERMINATED_SECURITY_ERROR:
+            }
+            case TERMINATED_SECURITY_ERROR -> {
                 return RtpEndUserState.SECURITY_ERROR;
+            }
         }
         throw new IllegalStateException(
                 String.format("%s has no equivalent EndUserState", this.state));
@@ -2237,19 +2275,14 @@ public class JingleRtpConnection extends AbstractJingleConnection
             // be in SESSION_ACCEPTED even though the peerConnection has been torn down
             return RtpEndUserState.ENDING_CALL;
         }
-        switch (state) {
-            case CONNECTED:
-                return RtpEndUserState.CONNECTED;
-            case NEW:
-            case CONNECTING:
-                return RtpEndUserState.CONNECTING;
-            case CLOSED:
-                return RtpEndUserState.ENDING_CALL;
-            default:
-                return zeroDuration()
-                        ? RtpEndUserState.CONNECTIVITY_ERROR
-                        : RtpEndUserState.RECONNECTING;
-        }
+        return switch (state) {
+            case CONNECTED -> RtpEndUserState.CONNECTED;
+            case NEW, CONNECTING -> RtpEndUserState.CONNECTING;
+            case CLOSED -> RtpEndUserState.ENDING_CALL;
+            default -> zeroDuration()
+                    ? RtpEndUserState.CONNECTIVITY_ERROR
+                    : RtpEndUserState.RECONNECTING;
+        };
     }
 
     public ContentAddition getPendingContentAddition() {
@@ -2284,9 +2317,10 @@ public class JingleRtpConnection extends AbstractJingleConnection
         } else if (initiatorContentMap != null) {
             return initiatorContentMap.getMedia();
         } else if (isTerminated()) {
-            return Collections.emptySet(); //we might fail before we ever got a chance to set media
+            return Collections.emptySet(); // we might fail before we ever got a chance to set media
         } else {
-            return Preconditions.checkNotNull(this.proposedMedia, "RTP connection has not been initialized properly");
+            return Preconditions.checkNotNull(
+                    this.proposedMedia, "RTP connection has not been initialized properly");
         }
     }
 
@@ -2306,35 +2340,29 @@ public class JingleRtpConnection extends AbstractJingleConnection
             throw new IllegalStateException(String.format("%s has already been proposed", media));
         }
         // TODO add state protection - can only add while ACCEPTED or so
-        Log.d(Config.LOGTAG,"adding media: "+media);
+        Log.d(Config.LOGTAG, "adding media: " + media);
         return webRTCWrapper.addTrack(media);
     }
 
     public synchronized void acceptCall() {
         switch (this.state) {
-            case PROPOSED:
+            case PROPOSED -> {
                 cancelRingingTimeout();
                 acceptCallFromProposed();
-                break;
-            case SESSION_INITIALIZED:
+            }
+            case SESSION_INITIALIZED -> {
                 cancelRingingTimeout();
                 acceptCallFromSessionInitialized();
-                break;
-            case ACCEPTED:
-                Log.w(
-                        Config.LOGTAG,
-                        id.account.getJid().asBareJid()
-                                + ": the call has already been accepted  with another client. UI was just lagging behind");
-                break;
-            case PROCEED:
-            case SESSION_ACCEPTED:
-                Log.w(
-                        Config.LOGTAG,
-                        id.account.getJid().asBareJid()
-                                + ": the call has already been accepted. user probably double tapped the UI");
-                break;
-            default:
-                throw new IllegalStateException("Can not accept call from " + this.state);
+            }
+            case ACCEPTED -> Log.w(
+                    Config.LOGTAG,
+                    id.account.getJid().asBareJid()
+                            + ": the call has already been accepted  with another client. UI was just lagging behind");
+            case PROCEED, SESSION_ACCEPTED -> Log.w(
+                    Config.LOGTAG,
+                    id.account.getJid().asBareJid()
+                            + ": the call has already been accepted. user probably double tapped the UI");
+            default -> throw new IllegalStateException("Can not accept call from " + this.state);
         }
     }
 
@@ -2356,14 +2384,9 @@ public class JingleRtpConnection extends AbstractJingleConnection
             return;
         }
         switch (this.state) {
-            case PROPOSED:
-                rejectCallFromProposed();
-                break;
-            case SESSION_INITIALIZED:
-                rejectCallFromSessionInitiate();
-                break;
-            default:
-                throw new IllegalStateException("Can not reject call from " + this.state);
+            case PROPOSED -> rejectCallFromProposed();
+            case SESSION_INITIALIZED -> rejectCallFromSessionInitiate();
+            default -> throw new IllegalStateException("Can not reject call from " + this.state);
         }
     }
 
@@ -2428,9 +2451,14 @@ public class JingleRtpConnection extends AbstractJingleConnection
         finish();
     }
 
-    private void setupWebRTC(final Set<Media> media, final List<PeerConnection.IceServer> iceServers, final boolean trickle) throws WebRTCWrapper.InitializationException {
+    private void setupWebRTC(
+            final Set<Media> media,
+            final List<PeerConnection.IceServer> iceServers,
+            final boolean trickle)
+            throws WebRTCWrapper.InitializationException {
         this.jingleConnectionManager.ensureConnectionIsRegistered(this);
-        this.webRTCWrapper.setup(this.xmppConnectionService, AppRTCAudioManager.SpeakerPhonePreference.of(media));
+        this.webRTCWrapper.setup(
+                this.xmppConnectionService, AppRTCAudioManager.SpeakerPhonePreference.of(media));
         this.webRTCWrapper.initializePeerConnection(media, iceServers, trickle);
     }
 
@@ -2606,7 +2634,10 @@ public class JingleRtpConnection extends AbstractJingleConnection
             final Throwable cause = Throwables.getRootCause(e);
             webRTCWrapper.close();
             if (isTerminated()) {
-                Log.d(Config.LOGTAG, "failed to renegotiate. session was already terminated", cause);
+                Log.d(
+                        Config.LOGTAG,
+                        "failed to renegotiate. session was already terminated",
+                        cause);
                 return;
             }
             Log.d(Config.LOGTAG, "failed to renegotiate. sending session-terminate", cause);
@@ -2691,7 +2722,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
                 prepareOutgoingContentMap(contentAdd);
         Futures.addCallback(
                 outgoingContentMapFuture,
-                new FutureCallback<RtpContentMap>() {
+                new FutureCallback<>() {
                     @Override
                     public void onSuccess(final RtpContentMap outgoingContentMap) {
                         sendContentAdd(outgoingContentMap);
@@ -2889,9 +2920,6 @@ public class JingleRtpConnection extends AbstractJingleConnection
                                         continue;
                                     }
 
-
-
-
                                     if (Arrays.asList("stun", "stuns", "turn", "turns")
                                                     .contains(type)
                                             && Arrays.asList("udp", "tcp").contains(transport)) {
@@ -2906,15 +2934,19 @@ public class JingleRtpConnection extends AbstractJingleConnection
 
                                         // 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);
+                                        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);
+                                            uri =
+                                                    String.format(
+                                                            "%s:%s:%s?transport=%s",
+                                                            type,
+                                                            IP.wrapIPv6(host),
+                                                            port,
+                                                            transport);
                                         }
 
                                         final PeerConnection.IceServer.Builder iceServerBuilder =