avoid terminating twice

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/xmpp/jingle/JingleFileTransferConnection.java |  2 
src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java          | 24 
src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/JinglePacket.java         | 31 
3 files changed, 44 insertions(+), 13 deletions(-)

Detailed changes

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

@@ -245,7 +245,7 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
         if (action == JinglePacket.Action.SESSION_INITIATE) {
             init(packet);
         } else if (action == JinglePacket.Action.SESSION_TERMINATE) {
-            final Reason reason = packet.getReason();
+            final Reason reason = packet.getReason().reason;
             switch (reason) {
                 case CANCEL:
                     this.cancelled = true;

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

@@ -36,6 +36,14 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
             State.SESSION_INITIALIZED_PRE_APPROVED,
             State.SESSION_ACCEPTED
     );
+
+    private static final List<State> TERMINATED = Arrays.asList(
+            State.TERMINATED_DECLINED_OR_BUSY,
+            State.TERMINATED_CONNECTIVITY_ERROR,
+            State.TERMINATED_CANCEL_OR_TIMEOUT,
+            State.TERMINATED_APPLICATION_FAILURE
+    );
+
     private static final Map<State, Collection<State>> VALID_TRANSITIONS;
 
     static {
@@ -137,11 +145,15 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
 
     private void receiveSessionTerminate(final JinglePacket jinglePacket) {
         respondOk(jinglePacket);
-        final Reason reason = jinglePacket.getReason();
+        final JinglePacket.ReasonWrapper wrapper = jinglePacket.getReason();
         final State previous = this.state;
-        Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received session terminate reason=" + reason + " while in state " + previous);
+        Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received session terminate reason=" + wrapper.reason + "(" + Strings.nullToEmpty(wrapper.text) + ") while in state " + previous);
+        if (TERMINATED.contains(previous)) {
+            Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": ignoring session terminate because already in " + previous);
+            return;
+        }
         webRTCWrapper.close();
-        transitionOrThrow(reasonToState(reason));
+        transitionOrThrow(reasonToState(wrapper.reason));
         if (previous == State.PROPOSED || previous == State.SESSION_INITIALIZED) {
             xmppConnectionService.getNotificationService().cancelIncomingCallNotification();
         }
@@ -761,7 +773,11 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
     public void onConnectionChange(final PeerConnection.PeerConnectionState newState) {
         Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": PeerConnectionState changed to " + newState);
         updateEndUserState();
-        if (newState == PeerConnection.PeerConnectionState.FAILED) { //TODO guard this in isState(initiated,initiated_approved,accepted) otherwise it might fire too late
+        if (newState == PeerConnection.PeerConnectionState.FAILED) {
+            if (TERMINATED.contains(this.state)) {
+                Log.d(Config.LOGTAG,id.account.getJid().asBareJid()+": not sending session-terminate after connectivity error because session is already in state "+this.state);
+                return;
+            }
             sendSessionTerminate(Reason.CONNECTIVITY_ERROR);
         }
     }

src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/JinglePacket.java 🔗

@@ -69,17 +69,21 @@ public class JinglePacket extends IqPacket {
         addJingleChild(content);
     }
 
-    public Reason getReason() {
-        final Element reason = getJingleChild("reason");
-        if (reason == null) {
-            return Reason.UNKNOWN;
+    public ReasonWrapper getReason() {
+        final Element reasonElement = getJingleChild("reason");
+        if (reasonElement == null) {
+            return new ReasonWrapper(Reason.UNKNOWN,null);
         }
-        for(Element child : reason.getChildren()) {
-            if (!"text".equals(child.getName())) {
-                return Reason.of(child.getName());
+        String text = null;
+        Reason reason = Reason.UNKNOWN;
+        for(Element child : reasonElement.getChildren()) {
+            if ("text".equals(child.getName())) {
+                text = child.getContent();
+            } else {
+                reason = Reason.of(child.getName());
             }
         }
-        return Reason.UNKNOWN;
+        return new ReasonWrapper(reason, text);
     }
 
     public void setReason(final Reason reason, final String text) {
@@ -149,4 +153,15 @@ public class JinglePacket extends IqPacket {
             return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_HYPHEN, super.toString());
         }
     }
+
+
+    public static class ReasonWrapper {
+        public final Reason reason;
+        public final String text;
+
+        public ReasonWrapper(Reason reason, String text) {
+            this.reason = reason;
+            this.text = text;
+        }
+    }
 }