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}