add generator for JMI finish message

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/generator/MessageGenerator.java                | 16 
src/main/java/eu/siacs/conversations/parser/MessageParser.java                      |  4 
src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java | 12 
src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java       | 10 
4 files changed, 40 insertions(+), 2 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/generator/MessageGenerator.java 🔗

@@ -21,6 +21,7 @@ import eu.siacs.conversations.xmpp.chatstate.ChatState;
 import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager;
 import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection;
 import eu.siacs.conversations.xmpp.jingle.Media;
+import eu.siacs.conversations.xmpp.jingle.stanzas.Reason;
 import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
 
 public class MessageGenerator extends AbstractGenerator {
@@ -228,6 +229,20 @@ public class MessageGenerator extends AbstractGenerator {
         return packet;
     }
 
+    public MessagePacket sessionFinish(
+            final Jid with, final String sessionId, final Reason reason) {
+        final MessagePacket packet = new MessagePacket();
+        packet.setType(MessagePacket.TYPE_CHAT);
+        packet.setTo(with);
+        packet.setId(JingleRtpConnection.JINGLE_MESSAGE_PROPOSE_ID_PREFIX + sessionId);
+        final Element finish = packet.addChild("finish", Namespace.JINGLE_MESSAGE);
+        finish.setAttribute("id", sessionId);
+        final Element reasonElement = finish.addChild("reason", Namespace.JINGLE);
+        reasonElement.addChild(reason.toString());
+        packet.addChild("store", "urn:xmpp:hints");
+        return packet;
+    }
+
     public MessagePacket sessionProposal(final JingleConnectionManager.RtpSessionProposal proposal) {
         final MessagePacket packet = new MessagePacket();
         packet.setType(MessagePacket.TYPE_CHAT); //we want to carbon copy those
@@ -238,7 +253,6 @@ public class MessageGenerator extends AbstractGenerator {
         for (final Media media : proposal.media) {
             propose.addChild("description", Namespace.JINGLE_APPS_RTP).setAttribute("media", media.toString());
         }
-
         packet.addChild("request", "urn:xmpp:receipts");
         packet.addChild("store", "urn:xmpp:hints");
         return packet;

src/main/java/eu/siacs/conversations/parser/MessageParser.java 🔗

@@ -53,7 +53,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
     private static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("HH:mm:ss", Locale.ENGLISH);
 
     private static final List<String> JINGLE_MESSAGE_ELEMENT_NAMES =
-            Arrays.asList("accept", "propose", "proceed", "reject", "retract", "ringing");
+            Arrays.asList("accept", "propose", "proceed", "reject", "retract", "ringing", "finish");
 
     public MessageParser(XmppConnectionService service) {
         super(service);
@@ -913,6 +913,8 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
                                     Log.d(Config.LOGTAG, "unable to find original rtp session message for received propose");
                                 }
 
+                            } else if ("finish".equals(action)) {
+                                Log.d(Config.LOGTAG,"received JMI 'finish' during MAM catch-up. Can be used to update success/failure and duration");
                             }
                         } else {
                             //MAM reloads (non catchups

src/main/java/eu/siacs/conversations/services/CallIntegrationConnectionService.java 🔗

@@ -30,16 +30,19 @@ import com.google.common.util.concurrent.SettableFuture;
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.Contact;
 import eu.siacs.conversations.ui.RtpSessionActivity;
 import eu.siacs.conversations.xmpp.Jid;
 import eu.siacs.conversations.xmpp.jingle.AbstractJingleConnection;
 import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection;
 import eu.siacs.conversations.xmpp.jingle.Media;
 import eu.siacs.conversations.xmpp.jingle.RtpEndUserState;
+import eu.siacs.conversations.xmpp.jingle.stanzas.Reason;
 
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Set;
+import java.util.UUID;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
@@ -108,6 +111,9 @@ public class CallIntegrationConnectionService extends ConnectionService {
                 callIntegration =
                         Connection.createFailedConnection(
                                 new DisconnectCause(DisconnectCause.ERROR, "contact is offline"));
+                // we can use a JMI 'finish' message to notify the contact of a call we never
+                // actually attempted
+                // sendJingleFinishMessage(service, contact, Reason.CONNECTIVITY_ERROR);
             } else {
                 final var proposal =
                         service.getJingleConnectionManager()
@@ -137,6 +143,12 @@ public class CallIntegrationConnectionService extends ConnectionService {
         return callIntegration;
     }
 
+    private static void sendJingleFinishMessage(
+            final XmppConnectionService service, final Contact contact, final Reason reason) {
+        service.getJingleConnectionManager()
+                .sendJingleMessageFinish(contact, UUID.randomUUID().toString(), reason);
+    }
+
     @Override
     public Connection onCreateOutgoingConnection(
             final PhoneAccountHandle phoneAccountHandle, final ConnectionRequest request) {

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

@@ -746,6 +746,16 @@ public class JingleConnectionManager extends AbstractConnectionManager {
         }
     }
 
+    public void sendJingleMessageFinish(
+            final Contact contact, final String sessionId, final Reason reason) {
+        final var account = contact.getAccount();
+        final MessagePacket messagePacket =
+                mXmppConnectionService
+                        .getMessageGenerator()
+                        .sessionFinish(contact.getJid(), sessionId, reason);
+        mXmppConnectionService.sendMessagePacket(account, messagePacket);
+    }
+
     public boolean hasMatchingProposal(final Account account, final Jid with) {
         synchronized (this.rtpSessionProposals) {
             for (Map.Entry<RtpSessionProposal, DeviceDiscoveryState> entry :