RtpCapability.java

  1package eu.siacs.conversations.xmpp.jingle;
  2
  3import com.google.common.base.Strings;
  4import com.google.common.collect.Collections2;
  5import com.google.common.collect.ImmutableSet;
  6import eu.siacs.conversations.entities.Contact;
  7import eu.siacs.conversations.entities.Presences;
  8import eu.siacs.conversations.xml.Namespace;
  9import eu.siacs.conversations.xmpp.manager.DiscoManager;
 10import im.conversations.android.xmpp.model.disco.info.InfoQuery;
 11import java.util.ArrayList;
 12import java.util.Arrays;
 13import java.util.Collection;
 14import java.util.Collections;
 15import java.util.List;
 16import java.util.Set;
 17
 18import eu.siacs.conversations.entities.Contact;
 19import eu.siacs.conversations.entities.Presences;
 20import eu.siacs.conversations.xml.Namespace;
 21import eu.siacs.conversations.xmpp.Jid;
 22
 23public class RtpCapability {
 24
 25    private static final List<String> BASIC_RTP_REQUIREMENTS =
 26            Arrays.asList(
 27                    Namespace.JINGLE,
 28                    Namespace.JINGLE_TRANSPORT_ICE_UDP,
 29                    Namespace.JINGLE_APPS_RTP,
 30                    Namespace.JINGLE_APPS_DTLS);
 31    private static final Collection<String> VIDEO_REQUIREMENTS =
 32            Arrays.asList(Namespace.JINGLE_FEATURE_AUDIO, Namespace.JINGLE_FEATURE_VIDEO);
 33
 34    public static Capability check(final InfoQuery infoQuery) {
 35        final Set<String> features =
 36                infoQuery == null
 37                        ? Collections.emptySet()
 38                        : ImmutableSet.copyOf(infoQuery.getFeatureStrings());
 39        if (features.containsAll(BASIC_RTP_REQUIREMENTS)) {
 40            if (features.containsAll(VIDEO_REQUIREMENTS)) {
 41                return Capability.VIDEO;
 42            }
 43            if (features.contains(Namespace.JINGLE_FEATURE_AUDIO)) {
 44                return Capability.AUDIO;
 45            }
 46        }
 47        return Capability.NONE;
 48    }
 49
 50    public static String[] filterPresences(final Contact contact, Capability required) {
 51        final var connection = contact.getAccount().getXmppConnection();
 52        if (connection == null) {
 53            return new String[0];
 54        }
 55        final Presences presences = contact.getPresences();
 56        final ArrayList<String> resources = new ArrayList<>();
 57        for (final String resource : presences.getPresencesMap().keySet()) {
 58            final var jid =
 59                    Strings.isNullOrEmpty(resource)
 60                            ? contact.getJid().asBareJid()
 61                            : contact.getJid().withResource(resource);
 62            final Capability capability = check(connection.getManager(DiscoManager.class).get(jid));
 63            if (capability == Capability.NONE) {
 64                continue;
 65            }
 66            if (required == Capability.AUDIO || capability == required) {
 67                resources.add(resource);
 68            }
 69        }
 70        return resources.toArray(new String[0]);
 71    }
 72
 73    public static Capability check(final Contact contact) {
 74        return check(contact, true);
 75    }
 76
 77    public static Capability check(final Contact contact, final boolean allowFallback) {
 78        final Presences presences = contact.getPresences();
 79
 80        if (presences.isEmpty() && allowFallback && contact.getAccount().isEnabled()) {
 81            Contact gateway = contact.getAccount().getRoster().getContact(Jid.of(contact.getJid().getDomain()));
 82            if (gateway.showInRoster() && gateway.getPresences().anyIdentity("gateway", "pstn")) {
 83                return Capability.AUDIO;
 84            }
 85
 86            return contact.getRtpCapability();
 87        }
 88        final var connection = contact.getAccount().getXmppConnection();
 89        if (connection == null) {
 90            return Capability.NONE;
 91        }
 92        Capability result = Capability.NONE;
 93        for (final String resource : presences.getPresencesMap().keySet()) {
 94            final var jid =
 95                    Strings.isNullOrEmpty(resource)
 96                            ? contact.getJid().asBareJid()
 97                            : contact.getJid().withResource(resource);
 98            final Capability capability = check(connection.getManager(DiscoManager.class).get(jid));
 99            if (capability == Capability.VIDEO) {
100                result = capability;
101            } else if (capability == Capability.AUDIO && result == Capability.NONE) {
102                result = capability;
103            }
104        }
105        return result;
106    }
107
108    // do all devices that support Rtp Call also support JMI?
109    public static boolean jmiSupport(final Contact contact) {
110        final var connection = contact.getAccount().getXmppConnection();
111        if (connection == null) {
112            return false;
113        }
114        return !Collections2.transform(
115                        Collections2.filter(
116                                contact.getPresences().getPresencesMap().keySet(),
117                                p ->
118                                        RtpCapability.check(
119                                                        connection
120                                                                .getManager(DiscoManager.class)
121                                                                .get(
122                                                                        Strings.isNullOrEmpty(p)
123                                                                                ? contact.getJid()
124                                                                                        .asBareJid()
125                                                                                : contact.getJid()
126                                                                                        .withResource(
127                                                                                                p)))
128                                                != Capability.NONE),
129                        p -> {
130                            final var disco =
131                                    connection
132                                            .getManager(DiscoManager.class)
133                                            .get(
134                                                    Strings.isNullOrEmpty(p)
135                                                            ? contact.getJid().asBareJid()
136                                                            : contact.getJid().withResource(p));
137                            return disco != null
138                                    && disco.getFeatureStrings().contains(Namespace.JINGLE_MESSAGE);
139                        })
140                .contains(false);
141    }
142
143    public enum Capability {
144        NONE,
145        AUDIO,
146        VIDEO;
147
148        public static Capability of(String value) {
149            if (Strings.isNullOrEmpty(value)) {
150                return NONE;
151            }
152            try {
153                return valueOf(value);
154            } catch (IllegalArgumentException e) {
155                return NONE;
156            }
157        }
158    }
159}