@@ -81,7 +81,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
final AbstractJingleConnection existingJingleConnection = connections.get(id);
if (existingJingleConnection != null) {
if (existingJingleConnection instanceof JingleRtpConnection) {
- ((JingleRtpConnection) existingJingleConnection).deliveryMessage(to, from, message);
+ ((JingleRtpConnection) existingJingleConnection).deliveryMessage(from, message);
} else {
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": " + existingJingleConnection.getClass().getName() + " does not support jingle messages");
}
@@ -91,7 +91,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
if (Namespace.JINGLE_APPS_RTP.equals(namespace)) {
final JingleRtpConnection rtpConnection = new JingleRtpConnection(this, id, with);
this.connections.put(id, rtpConnection);
- rtpConnection.deliveryMessage(to, from, message);
+ rtpConnection.deliveryMessage(from, message);
} else {
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": unable to react to proposed " + namespace + " session");
}
@@ -2,8 +2,12 @@ package eu.siacs.conversations.xmpp.jingle;
import android.util.Log;
+import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+
+import org.checkerframework.checker.nullness.compatqual.NullableDecl;
import java.util.Collection;
import java.util.Map;
@@ -11,7 +15,12 @@ import java.util.Map;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.Namespace;
+import eu.siacs.conversations.xmpp.jingle.stanzas.Content;
+import eu.siacs.conversations.xmpp.jingle.stanzas.GenericDescription;
+import eu.siacs.conversations.xmpp.jingle.stanzas.GenericTransportInfo;
+import eu.siacs.conversations.xmpp.jingle.stanzas.IceUdpTransportInfo;
import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
+import eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
import rocks.xmpp.addr.Jid;
@@ -23,6 +32,7 @@ public class JingleRtpConnection extends AbstractJingleConnection {
final ImmutableMap.Builder<State, Collection<State>> transitionBuilder = new ImmutableMap.Builder<>();
transitionBuilder.put(State.NULL, ImmutableList.of(State.PROPOSED, State.SESSION_INITIALIZED));
transitionBuilder.put(State.PROPOSED, ImmutableList.of(State.ACCEPTED, State.PROCEED));
+ transitionBuilder.put(State.PROCEED, ImmutableList.of(State.SESSION_INITIALIZED));
VALID_TRANSITIONS = transitionBuilder.build();
}
@@ -36,28 +46,91 @@ public class JingleRtpConnection extends AbstractJingleConnection {
@Override
void deliverPacket(final JinglePacket jinglePacket) {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": packet delivered to JingleRtpConnection");
- Log.d(Config.LOGTAG, jinglePacket.toString());
+ switch (jinglePacket.getAction()) {
+ case SESSION_INITIATE:
+ receiveSessionInitiate(jinglePacket);
+ break;
+ default:
+ Log.d(Config.LOGTAG, String.format("%s: received unhandled jingle action %s", id.account.getJid().asBareJid(), jinglePacket.getAction()));
+ break;
+ }
+ }
+
+ private void receiveSessionInitiate(final JinglePacket jinglePacket) {
+ if (isInitiator()) {
+ Log.d(Config.LOGTAG, String.format("%s: received session-initiate even though we were initiating", id.account.getJid().asBareJid()));
+ //TODO respond with out-of-order
+ return;
+ }
+ final Map<String, DescriptionTransport> contents;
+ try {
+ contents = DescriptionTransport.of(jinglePacket.getJingleContents());
+ } catch (IllegalArgumentException | NullPointerException e) {
+ Log.d(Config.LOGTAG,id.account.getJid().asBareJid()+": improperly formatted contents",e);
+ return;
+ }
+ Log.d(Config.LOGTAG,"processing session-init with "+contents.size()+" contents");
+ final State oldState = this.state;
+ if (transition(State.SESSION_INITIALIZED)) {
+ if (oldState == State.PROCEED) {
+ sendSessionAccept();
+ } else {
+ //TODO start ringing
+ }
+ } else {
+ Log.d(Config.LOGTAG, String.format("%s: received session-initiate while in state %s", id.account.getJid().asBareJid(), state));
+ }
}
- void deliveryMessage(final Jid to, final Jid from, final Element message) {
+ void deliveryMessage(final Jid from, final Element message) {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": delivered message to JingleRtpConnection " + message);
- final boolean originatedFromMyself = from.asBareJid().equals(id.account.getJid().asBareJid());
switch (message.getName()) {
case "propose":
- if (originatedFromMyself) {
- Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": saw proposal from mysql. ignoring");
- } else if (transition(State.PROPOSED)) {
- //TODO start ringing or something
- pickUpCall();
- } else {
- Log.d(Config.LOGTAG, id.account.getJid() + ": ignoring session proposal because already in " + state);
- }
+ receivePropose(from, message);
break;
+ case "proceed":
+ receiveProceed(from, message);
default:
break;
}
}
+ private void receivePropose(final Jid from, final Element propose) {
+ final boolean originatedFromMyself = from.asBareJid().equals(id.account.getJid().asBareJid());
+ if (originatedFromMyself) {
+ Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": saw proposal from mysql. ignoring");
+ } else if (transition(State.PROPOSED)) {
+ //TODO start ringing or something
+ pickUpCall();
+ } else {
+ Log.d(Config.LOGTAG, id.account.getJid() + ": ignoring session proposal because already in " + state);
+ }
+ }
+
+ private void receiveProceed(final Jid from, final Element proceed) {
+ if (from.equals(id.with)) {
+ if (isInitiator()) {
+ if (transition(State.SESSION_INITIALIZED)) {
+ this.sendSessionInitiate();
+ } else {
+ Log.d(Config.LOGTAG, String.format("%s: ignoring proceed because already in %s", id.account.getJid().asBareJid(), this.state));
+ }
+ } else {
+ Log.d(Config.LOGTAG, String.format("%s: ignoring proceed because we were not initializing", id.account.getJid().asBareJid()));
+ }
+ } else {
+ Log.d(Config.LOGTAG, String.format("%s: ignoring proceed from %s. was expected from %s", id.account.getJid().asBareJid(), from, id.with));
+ }
+ }
+
+ private void sendSessionInitiate() {
+
+ }
+
+ private void sendSessionAccept() {
+ Log.d(Config.LOGTAG,"sending session-accept");
+ }
+
public void pickUpCall() {
switch (this.state) {
case PROPOSED:
@@ -75,8 +148,8 @@ public class JingleRtpConnection extends AbstractJingleConnection {
transitionOrThrow(State.PROCEED);
final MessagePacket messagePacket = new MessagePacket();
messagePacket.setTo(id.with);
- //Note that Movim needs 'accept'
- messagePacket.addChild("proceed", Namespace.JINGLE_MESSAGE).setAttribute("id", id.sessionId);
+ //Note that Movim needs 'accept', correct is 'proceed' https://github.com/movim/movim/issues/916
+ messagePacket.addChild("accept", Namespace.JINGLE_MESSAGE).setAttribute("id", id.sessionId);
Log.d(Config.LOGTAG, messagePacket.toString());
xmppConnectionService.sendMessagePacket(id.account, messagePacket);
}
@@ -102,4 +175,42 @@ public class JingleRtpConnection extends AbstractJingleConnection {
}
}
+ private static class DescriptionTransport {
+ private final RtpDescription description;
+ private final IceUdpTransportInfo transport;
+
+ public DescriptionTransport(final RtpDescription description, final IceUdpTransportInfo transport) {
+ this.description = description;
+ this.transport = transport;
+ }
+
+ public static DescriptionTransport of(final Content content) {
+ final GenericDescription description = content.getDescription();
+ final GenericTransportInfo transportInfo = content.getTransport();
+ final RtpDescription rtpDescription;
+ final IceUdpTransportInfo iceUdpTransportInfo;
+ if (description instanceof RtpDescription) {
+ rtpDescription = (RtpDescription) description;
+ } else {
+ throw new IllegalArgumentException("Content does not contain RtpDescription");
+ }
+ if (transportInfo instanceof IceUdpTransportInfo) {
+ iceUdpTransportInfo = (IceUdpTransportInfo) transportInfo;
+ } else {
+ throw new IllegalArgumentException("Content does not contain ICE-UDP transport");
+ }
+ return new DescriptionTransport(rtpDescription, iceUdpTransportInfo);
+ }
+
+ public static Map<String, DescriptionTransport> of(final Map<String,Content> contents) {
+ return Maps.transformValues(contents, new Function<Content, DescriptionTransport>() {
+ @NullableDecl
+ @Override
+ public DescriptionTransport apply(@NullableDecl Content content) {
+ return content == null ? null : of(content);
+ }
+ });
+ }
+ }
+
}
@@ -1,10 +1,15 @@
package eu.siacs.conversations.xmpp.jingle.stanzas;
import android.support.annotation.NonNull;
+import android.util.Log;
import com.google.common.base.CaseFormat;
import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+
+import eu.siacs.conversations.Config;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
@@ -39,6 +44,18 @@ public class JinglePacket extends IqPacket {
return content == null ? null : Content.upgrade(content);
}
+ public Map<String, Content> getJingleContents() {
+ final Element jingle = findChild("jingle", Namespace.JINGLE);
+ ImmutableMap.Builder<String, Content> builder = new ImmutableMap.Builder<>();
+ for (final Element child : jingle.getChildren()) {
+ if ("content".equals(child.getName())) {
+ final Content content = Content.upgrade(child);
+ builder.put(content.getContentName(), content);
+ }
+ }
+ return builder.build();
+ }
+
public void setJingleContent(final Content content) { //take content interface
setJingleChild(content);
}