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