introduce extra RTP state to handle going from sending proceed to receiving retract

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/parser/MessageParser.java                 |  2 
src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java                |  3 
src/main/java/eu/siacs/conversations/xmpp/jingle/AbstractJingleConnection.java |  1 
src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java  | 20 
src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java      | 12 
5 files changed, 29 insertions(+), 9 deletions(-)

Detailed changes

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

@@ -843,7 +843,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
                             if (serverMsgId == null) {
                                 serverMsgId = extractStanzaId(account, packet);
                             }
-                            mXmppConnectionService.getJingleConnectionManager().deliverMessage(account, packet.getTo(), packet.getFrom(), child, serverMsgId, timestamp);
+                            mXmppConnectionService.getJingleConnectionManager().deliverMessage(account, packet.getTo(), packet.getFrom(), child, remoteMsgId, serverMsgId, timestamp);
                         } else if (query.isCatchup()) {
                             final String sessionId = child.getAttribute("id");
                             if (sessionId == null) {

src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java 🔗

@@ -74,7 +74,8 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
     private static final List<RtpEndUserState> END_CARD = Arrays.asList(
             RtpEndUserState.APPLICATION_ERROR,
             RtpEndUserState.DECLINED_OR_BUSY,
-            RtpEndUserState.CONNECTIVITY_ERROR
+            RtpEndUserState.CONNECTIVITY_ERROR,
+            RtpEndUserState.RETRACTED
     );
     private static final String PROXIMITY_WAKE_LOCK_TAG = "conversations:in-rtp-session";
     private static final int REQUEST_ACCEPT_CALL = 0x1111;

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

@@ -154,7 +154,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
         account.getXmppConnection().sendIqPacket(response, null);
     }
 
-    public void deliverMessage(final Account account, final Jid to, final Jid from, final Element message, String serverMsgId, long timestamp) {
+    public void deliverMessage(final Account account, final Jid to, final Jid from, final Element message, String remoteMsgId, String serverMsgId, long timestamp) {
         Preconditions.checkArgument(Namespace.JINGLE_MESSAGE.equals(message.getNamespace()));
         final String sessionId = message.getAttribute("id");
         if (sessionId == null) {
@@ -174,6 +174,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
             return;
         }
         final boolean fromSelf = from.asBareJid().equals(account.getJid().asBareJid());
+        final boolean addressedDirectly = to != null && to.equals(account.getJid());
         final AbstractJingleConnection.Id id;
         if (fromSelf) {
             if (to != null && to.isFullJid()) {
@@ -250,7 +251,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
             } else {
                 Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": unable to react to proposed session with " + rtpDescriptions.size() + " rtp descriptions of " + descriptions.size() + " total descriptions");
             }
-        } else if ("proceed".equals(message.getName())) {
+        } else if (addressedDirectly && "proceed".equals(message.getName())) {
             synchronized (rtpSessionProposals) {
                 final RtpSessionProposal proposal = getRtpSessionProposal(account, from.asBareJid(), sessionId);
                 if (proposal != null) {
@@ -262,10 +263,21 @@ public class JingleConnectionManager extends AbstractConnectionManager {
                     rtpConnection.deliveryMessage(from, message, serverMsgId, timestamp);
                 } else {
                     Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": no rtp session proposal found for " + from + " to deliver proceed");
-                    //TODO return error message "item-not-found"
+                    if (remoteMsgId == null) {
+                        return;
+                    }
+                    final MessagePacket errorMessage = new MessagePacket();
+                    errorMessage.setTo(from);
+                    errorMessage.setId(remoteMsgId);
+                    errorMessage.setType(MessagePacket.TYPE_ERROR);
+                    final Element error = errorMessage.addChild("error");
+                    error.setAttribute("code", "404");
+                    error.setAttribute("type", "cancel");
+                    error.addChild("item-not-found", "urn:ietf:params:xml:ns:xmpp-stanzas");
+                    mXmppConnectionService.sendMessagePacket(account, errorMessage);
                 }
             }
-        } else if ("reject".equals(message.getName())) {
+        } else if (addressedDirectly && "reject".equals(message.getName())) {
             final RtpSessionProposal proposal = new RtpSessionProposal(account, from.asBareJid(), sessionId);
             synchronized (rtpSessionProposals) {
                 if (rtpSessionProposals.remove(proposal) != null) {

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

@@ -61,6 +61,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
             State.ACCEPTED,
             State.REJECTED,
             State.RETRACTED,
+            State.RETRACTED_RACED,
             State.TERMINATED_SUCCESS,
             State.TERMINATED_DECLINED_OR_BUSY,
             State.TERMINATED_CONNECTIVITY_ERROR,
@@ -86,6 +87,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
                 State.TERMINATED_CONNECTIVITY_ERROR //only used when the xmpp connection rebinds
         ));
         transitionBuilder.put(State.PROCEED, ImmutableList.of(
+                State.RETRACTED_RACED,
                 State.SESSION_INITIALIZED_PRE_APPROVED,
                 State.TERMINATED_SUCCESS,
                 State.TERMINATED_APPLICATION_FAILURE,
@@ -486,7 +488,6 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
     }
 
     void deliverFailedProceed() {
-        //TODO do we want a special State.ITEM_NOT_FOUND to track retracted calls during network outages
         Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": receive message error for proceed message");
         if (transition(State.TERMINATED_CONNECTIVITY_ERROR)) {
             webRTCWrapper.close();
@@ -617,14 +618,17 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
 
     private void receiveRetract(final Jid from, final String serverMsgId, final long timestamp) {
         if (from.equals(id.with)) {
-            if (transition(State.RETRACTED)) {
+            final State target = this.state == State.PROCEED ? State.RETRACTED_RACED : State.RETRACTED;
+            if (transition(target)) {
                 xmppConnectionService.getNotificationService().cancelIncomingCallNotification();
                 Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": session with " + id.with + " has been retracted (serverMsgId=" + serverMsgId + ")");
                 if (serverMsgId != null) {
                     this.message.setServerMsgId(serverMsgId);
                 }
                 this.message.setTime(timestamp);
-                this.message.markUnread();
+                if (target == State.RETRACTED) {
+                    this.message.markUnread();
+                }
                 writeLogMessageMissed();
                 finish();
             } else {
@@ -811,6 +815,8 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
             case RETRACTED:
             case TERMINATED_CANCEL_OR_TIMEOUT:
                 return RtpEndUserState.ENDED;
+            case RETRACTED_RACED:
+                return RtpEndUserState.RETRACTED;
             case TERMINATED_CONNECTIVITY_ERROR:
                 return RtpEndUserState.CONNECTIVITY_ERROR;
             case TERMINATED_APPLICATION_FAILURE: