~singpolyma/cheogram-android

b4e883f7aa43e1ffb29f7f499ec3f7cc39b6dae0 — Daniel Gultsch 4 months ago b434c5d
bring back ICE Renomination via negotiation
M src/main/java/eu/siacs/conversations/xml/Namespace.java => src/main/java/eu/siacs/conversations/xml/Namespace.java +1 -0
@@ 65,6 65,7 @@ public final class Namespace {
    public static final String PARS = "urn:xmpp:pars:0";
    public static final String EASY_ONBOARDING_INVITE = "urn:xmpp:invite#invite";
    public static final String OMEMO_DTLS_SRTP_VERIFICATION = "http://gultsch.de/xmpp/drafts/omemo/dlts-srtp-verification";
    public static final String JINGLE_TRANSPORT_ICE_OPTION = "http://gultsch.de/xmpp/drafts/jingle/transports/ice-udp/option";
    public static final String UNIFIED_PUSH = "http://gultsch.de/xmpp/drafts/unified-push";
    public static final String VCARD4 = "urn:ietf:params:xml:ns:vcard-4.0";
}

M src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java => src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java +12 -0
@@ 16,6 16,7 @@ import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.primitives.Ints;
import com.google.common.util.concurrent.FutureCallback;


@@ 242,6 243,9 @@ public class JingleRtpConnection extends AbstractJingleConnection
            case CONTENT_REMOVE:
                receiveContentRemove(jinglePacket);
                break;
            case CONTENT_MODIFY:
                receiveContentModify(jinglePacket);
                break;
            default:
                respondOk(jinglePacket);
                Log.d(


@@ 516,6 520,14 @@ public class JingleRtpConnection extends AbstractJingleConnection
                        + ContentAddition.summary(receivedContentAccept));
    }

    private void receiveContentModify(final JinglePacket jinglePacket) {
        final Map<String, Content.Senders> modification =
                Maps.transformEntries(
                        jinglePacket.getJingleContents(), (key, value) -> value.getSenders());
        respondOk(jinglePacket);
        Log.d(Config.LOGTAG, "receiveContentModification(" + modification + ")");
    }

    private void receiveContentReject(final JinglePacket jinglePacket) {
        final RtpContentMap receivedContentReject;
        try {

M src/main/java/eu/siacs/conversations/xmpp/jingle/SessionDescription.java => src/main/java/eu/siacs/conversations/xmpp/jingle/SessionDescription.java +16 -8
@@ 11,23 11,26 @@ import com.google.common.base.Strings;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;

import java.util.List;
import java.util.Locale;
import java.util.Map;

import eu.siacs.conversations.Config;
import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.jingle.stanzas.Group;
import eu.siacs.conversations.xmpp.jingle.stanzas.IceUdpTransportInfo;
import eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;

public class SessionDescription {

    public static final String LINE_DIVIDER = "\r\n";
    private static final String HARDCODED_MEDIA_PROTOCOL =
            "UDP/TLS/RTP/SAVPF"; // probably only true for DTLS-SRTP aka when we have a fingerprint
    private static final int HARDCODED_MEDIA_PORT = 9;
    private static final String HARDCODED_ICE_OPTIONS = "trickle";
    private static final Collection<String> HARDCODED_ICE_OPTIONS =
            Collections.singleton("trickle");
    private static final String HARDCODED_CONNECTION = "IN IP4 0.0.0.0";

    public final int version;


@@ 128,7 131,8 @@ public class SessionDescription {
        return sessionDescriptionBuilder.createSessionDescription();
    }

    public static SessionDescription of(final RtpContentMap contentMap, final boolean isInitiatorContentMap) {
    public static SessionDescription of(
            final RtpContentMap contentMap, final boolean isInitiatorContentMap) {
        final SessionDescriptionBuilder sessionDescriptionBuilder = new SessionDescriptionBuilder();
        final ArrayListMultimap<String, String> attributeMap = ArrayListMultimap.create();
        final ImmutableList.Builder<Media> mediaListBuilder = new ImmutableList.Builder<>();


@@ 166,7 170,10 @@ public class SessionDescription {
            }
            checkNoWhitespace(pwd, "pwd value must not contain any whitespaces");
            mediaAttributes.put("ice-pwd", pwd);
            mediaAttributes.put("ice-options", HARDCODED_ICE_OPTIONS);
            final List<String> negotiatedIceOptions = transport.getIceOptions();
            final Collection<String> iceOptions =
                    negotiatedIceOptions.isEmpty() ? HARDCODED_ICE_OPTIONS : negotiatedIceOptions;
            mediaAttributes.put("ice-options", Joiner.on(' ').join(iceOptions));
            final IceUdpTransportInfo.Fingerprint fingerprint = transport.getFingerprint();
            if (fingerprint != null) {
                mediaAttributes.put(


@@ 297,7 304,8 @@ public class SessionDescription {

            mediaAttributes.put("mid", name);

            mediaAttributes.put(descriptionTransport.senders.asMediaAttribute(isInitiatorContentMap), "");
            mediaAttributes.put(
                    descriptionTransport.senders.asMediaAttribute(isInitiatorContentMap), "");
            if (description.hasChild("rtcp-mux", Namespace.JINGLE_APPS_RTP) || group != null) {
                mediaAttributes.put("rtcp-mux", "");
            }

M src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/IceUdpTransportInfo.java => src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/IceUdpTransportInfo.java +45 -0
@@ 1,17 1,23 @@
package eu.siacs.conversations.xmpp.jingle.stanzas;

import android.util.Log;

import androidx.annotation.NonNull;

import com.google.common.base.Joiner;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.LinkedHashMap;


@@ 20,6 26,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.UUID;

import eu.siacs.conversations.Config;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.jingle.SessionDescription;


@@ 59,6 66,9 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
        if (fingerprint != null) {
            iceUdpTransportInfo.addChild(fingerprint);
        }
        for (final String iceOption : IceOption.of(media)) {
            iceUdpTransportInfo.addChild(new IceOption(iceOption));
        }
        return iceUdpTransportInfo;
    }



@@ 76,6 86,16 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
        return fingerprint == null ? null : Fingerprint.upgrade(fingerprint);
    }

    public List<String> getIceOptions() {
        final ImmutableList.Builder<String> optionBuilder = new ImmutableList.Builder<>();
        for(final Element child : this.children) {
            if (Namespace.JINGLE_TRANSPORT_ICE_OPTION.equals(child.getNamespace()) && IceOption.WELL_KNOWN.contains(child.getName())) {
                optionBuilder.add(child.getName());
            }
        }
        return optionBuilder.build();
    }

    public Credentials getCredentials() {
        final String ufrag = this.getAttribute("ufrag");
        final String password = this.getAttribute("pwd");


@@ 408,4 428,29 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
            throw new IllegalStateException(this.name() + " can not be flipped");
        }
    }

    public static class IceOption extends Element {

        public static final List<String> WELL_KNOWN = Arrays.asList("trickle", "renomination");

        public IceOption(final String name) {
            super(name, Namespace.JINGLE_TRANSPORT_ICE_OPTION);
        }

        public static Collection<String> of(SessionDescription.Media media) {
            final String iceOptions = Iterables.getFirst(media.attributes.get("ice-options"), null);
            if (Strings.isNullOrEmpty(iceOptions)) {
                return Collections.emptyList();
            }
            final ImmutableList.Builder<String> optionBuilder = new ImmutableList.Builder<>();
            for (final String iceOption : Splitter.on(' ').split(iceOptions)) {
                if (WELL_KNOWN.contains(iceOption)) {
                    optionBuilder.add(iceOption);
                } else {
                    Log.w(Config.LOGTAG, "unrecognized ice option: " + iceOption);
                }
            }
            return optionBuilder.build();
        }
    }
}