Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-3.0-or-later
2 : : // SPDX-FileCopyrightText: Andy Holmes <andrew.g.r.holmes@gmail.com>
3 : :
4 : : #define G_LOG_DOMAIN "valent-telephony"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <gio/gio.h>
9 : : #include <json-glib/json-glib.h>
10 : : #include <valent.h>
11 : :
12 : : #include "valent-telephony.h"
13 : :
14 : :
15 : : /**
16 : : * ValentTelephony:
17 : : *
18 : : * A class for controlling pointer and keyboard devices.
19 : : *
20 : : * `ValentTelephony` is an abstraction of telephony support, intended for use by
21 : : * [class@Valent.DevicePlugin] implementations.
22 : : */
23 : :
24 : : struct _ValentTelephony
25 : : {
26 : : ValentObject parent_instance;
27 : :
28 : : GDBusObjectManager *manager;
29 : : GDBusProxy *modem;
30 : : GHashTable *modems;
31 : : };
32 : :
33 [ + + + - ]: 22 : G_DEFINE_FINAL_TYPE (ValentTelephony, valent_telephony, VALENT_TYPE_OBJECT)
34 : :
35 : : enum {
36 : : CHANGED,
37 : : N_SIGNALS
38 : : };
39 : :
40 : : static guint signals[N_SIGNALS] = { 0, };
41 : :
42 : :
43 : : static ValentTelephony *default_telephony = NULL;
44 : :
45 : : /*
46 : : * ModemManager-enums.h
47 : : */
48 : : typedef enum { /*< underscore_name=mm_modem_access_technology >*/
49 : : MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN = 0,
50 : : MM_MODEM_ACCESS_TECHNOLOGY_POTS = 1 << 0,
51 : : MM_MODEM_ACCESS_TECHNOLOGY_GSM = 1 << 1,
52 : : MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT = 1 << 2,
53 : : MM_MODEM_ACCESS_TECHNOLOGY_GPRS = 1 << 3,
54 : : MM_MODEM_ACCESS_TECHNOLOGY_EDGE = 1 << 4,
55 : : MM_MODEM_ACCESS_TECHNOLOGY_UMTS = 1 << 5,
56 : : MM_MODEM_ACCESS_TECHNOLOGY_HSDPA = 1 << 6,
57 : : MM_MODEM_ACCESS_TECHNOLOGY_HSUPA = 1 << 7,
58 : : MM_MODEM_ACCESS_TECHNOLOGY_HSPA = 1 << 8,
59 : : MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS = 1 << 9,
60 : : MM_MODEM_ACCESS_TECHNOLOGY_1XRTT = 1 << 10,
61 : : MM_MODEM_ACCESS_TECHNOLOGY_EVDO0 = 1 << 11,
62 : : MM_MODEM_ACCESS_TECHNOLOGY_EVDOA = 1 << 12,
63 : : MM_MODEM_ACCESS_TECHNOLOGY_EVDOB = 1 << 13,
64 : : MM_MODEM_ACCESS_TECHNOLOGY_LTE = 1 << 14,
65 : : MM_MODEM_ACCESS_TECHNOLOGY_5GNR = 1 << 15,
66 : : MM_MODEM_ACCESS_TECHNOLOGY_ANY = 0xFFFFFFFF,
67 : : } ValentModemAccessTechnology;
68 : :
69 : : typedef enum { /*< underscore_name=mm_modem_state >*/
70 : : MM_MODEM_STATE_FAILED = -1,
71 : : MM_MODEM_STATE_UNKNOWN = 0,
72 : : MM_MODEM_STATE_INITIALIZING = 1,
73 : : MM_MODEM_STATE_LOCKED = 2,
74 : : MM_MODEM_STATE_DISABLED = 3,
75 : : MM_MODEM_STATE_DISABLING = 4,
76 : : MM_MODEM_STATE_ENABLING = 5,
77 : : MM_MODEM_STATE_ENABLED = 6,
78 : : MM_MODEM_STATE_SEARCHING = 7,
79 : : MM_MODEM_STATE_REGISTERED = 8,
80 : : MM_MODEM_STATE_DISCONNECTING = 9,
81 : : MM_MODEM_STATE_CONNECTING = 10,
82 : : MM_MODEM_STATE_CONNECTED = 11,
83 : : } ValentModemState;
84 : :
85 : :
86 : : static inline const char *
87 : 6 : get_telephony_type_string (unsigned int flags)
88 : : {
89 [ - - - - : 6 : switch (flags)
- - + -
+ ]
90 : : {
91 : : case MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN:
92 : : case MM_MODEM_ACCESS_TECHNOLOGY_POTS:
93 : : return "Unknown";
94 : :
95 : 0 : case MM_MODEM_ACCESS_TECHNOLOGY_GSM:
96 : : case MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT:
97 : 0 : return "GSM";
98 : :
99 : 0 : case MM_MODEM_ACCESS_TECHNOLOGY_GPRS:
100 : 0 : return "GPRS";
101 : :
102 : 0 : case MM_MODEM_ACCESS_TECHNOLOGY_EDGE:
103 : 0 : return "EDGE";
104 : :
105 : 0 : case MM_MODEM_ACCESS_TECHNOLOGY_UMTS:
106 : 0 : return "UMTS";
107 : :
108 : 0 : case MM_MODEM_ACCESS_TECHNOLOGY_HSDPA:
109 : : case MM_MODEM_ACCESS_TECHNOLOGY_HSUPA:
110 : : case MM_MODEM_ACCESS_TECHNOLOGY_HSUPA | MM_MODEM_ACCESS_TECHNOLOGY_HSDPA:
111 : : case MM_MODEM_ACCESS_TECHNOLOGY_HSPA:
112 : : case MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS:
113 : 0 : return "HSPA";
114 : :
115 : 0 : case MM_MODEM_ACCESS_TECHNOLOGY_1XRTT:
116 : : case MM_MODEM_ACCESS_TECHNOLOGY_EVDO0:
117 : : case MM_MODEM_ACCESS_TECHNOLOGY_EVDOA:
118 : : case MM_MODEM_ACCESS_TECHNOLOGY_EVDOB:
119 : 0 : return "CDMA2000";
120 : :
121 : 2 : case MM_MODEM_ACCESS_TECHNOLOGY_LTE:
122 : 2 : return "LTE";
123 : :
124 : 0 : case MM_MODEM_ACCESS_TECHNOLOGY_5GNR:
125 : 0 : return "5G";
126 : :
127 : : case MM_MODEM_ACCESS_TECHNOLOGY_ANY:
128 : : return "Unknown";
129 : :
130 : : default:
131 : : return "Unknown";
132 : : }
133 : : }
134 : :
135 : : static void
136 : 4 : on_properties_changed (GDBusProxy *proxy,
137 : : GVariant *changed_properties,
138 : : GStrv invalidated,
139 : : ValentTelephony *self)
140 : : {
141 : 4 : GVariantIter iter;
142 : 4 : const char *property;
143 : 4 : GVariant *value;
144 : 4 : gboolean changed = FALSE;
145 : :
146 : 4 : g_variant_iter_init (&iter, changed_properties);
147 : :
148 [ + + ]: 44 : while (g_variant_iter_loop (&iter, "{sv}", &property, &value))
149 : : {
150 [ + + ]: 40 : if (g_str_equal (property, "AccessTechnologies"))
151 : : changed = TRUE;
152 [ + + ]: 36 : else if (g_str_equal (property, "SignalQuality"))
153 : : changed = TRUE;
154 [ + + ]: 32 : else if (g_str_equal (property, "State"))
155 : 12 : changed = TRUE;
156 : : }
157 : :
158 [ + - ]: 4 : if (changed)
159 : 4 : g_signal_emit (G_OBJECT (self), signals [CHANGED], 0);
160 : 4 : }
161 : :
162 : : static void
163 : 2 : g_dbus_proxy_new_for_bus_cb (GObject *object,
164 : : GAsyncResult *result,
165 : : gpointer user_data)
166 : : {
167 : 2 : g_autoptr (ValentTelephony) self = VALENT_TELEPHONY (user_data);
168 [ - - ]: 2 : g_autoptr (GDBusProxy) proxy = NULL;
169 [ - - ]: 2 : g_autoptr (GError) error = NULL;
170 : 2 : const char *object_path;
171 : :
172 : 2 : proxy = g_dbus_proxy_new_for_bus_finish (result, &error);
173 : :
174 [ - + ]: 2 : if (proxy == NULL)
175 : : {
176 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
177 [ # # ]: 0 : return;
178 : : }
179 : :
180 : 2 : g_signal_connect_object (proxy,
181 : : "g-properties-changed",
182 : : G_CALLBACK (on_properties_changed),
183 : : self, 0);
184 : :
185 : 2 : object_path = g_dbus_proxy_get_object_path (proxy);
186 [ - + ]: 2 : g_hash_table_replace (self->modems,
187 : 2 : g_strdup (object_path),
188 : : g_steal_pointer (&proxy));
189 : :
190 [ - + ]: 2 : g_signal_emit (G_OBJECT (self), signals [CHANGED], 0);
191 : : }
192 : :
193 : : static void
194 : 2 : on_modem_added (GDBusObjectManager *manager,
195 : : GDBusObject *object,
196 : : ValentTelephony *self)
197 : : {
198 : 2 : const char *object_path;
199 : :
200 [ + - + - : 2 : g_assert (G_IS_DBUS_OBJECT_MANAGER (manager));
+ - - + ]
201 [ + - + - : 2 : g_assert (G_IS_DBUS_OBJECT (object));
+ - - + ]
202 [ - + ]: 2 : g_assert (VALENT_IS_TELEPHONY (self));
203 : :
204 : 2 : object_path = g_dbus_object_get_object_path (object);
205 : :
206 [ + - + - : 2 : if (!g_str_has_prefix (object_path, "/org/freedesktop/ModemManager1/Modem"))
- + ]
207 : 0 : return;
208 : :
209 : 2 : g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
210 : : G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
211 : : NULL,
212 : : "org.freedesktop.ModemManager1",
213 : : object_path,
214 : : "org.freedesktop.ModemManager1.Modem",
215 : : NULL,
216 : : (GAsyncReadyCallback)g_dbus_proxy_new_for_bus_cb,
217 : : g_object_ref (self));
218 : : }
219 : :
220 : : static void
221 : 2 : on_modem_removed (GDBusObjectManager *manager,
222 : : GDBusObject *object,
223 : : ValentTelephony *self)
224 : : {
225 : 2 : const char *object_path;
226 : :
227 [ + - + - : 2 : g_assert (G_IS_DBUS_OBJECT_MANAGER (manager));
+ - - + ]
228 [ + - + - : 2 : g_assert (G_IS_DBUS_OBJECT (object));
+ - - + ]
229 [ - + ]: 2 : g_assert (VALENT_IS_TELEPHONY (self));
230 : :
231 : 2 : object_path = g_dbus_object_get_object_path (object);
232 : :
233 [ + - + - : 2 : if (!g_str_has_prefix (object_path, "/org/freedesktop/ModemManager1/Modem"))
- + ]
234 : 0 : return;
235 : :
236 [ + - ]: 2 : if (g_hash_table_remove (self->modems, object_path))
237 : 2 : g_signal_emit (G_OBJECT (self), signals [CHANGED], 0);
238 : : }
239 : :
240 : : static void
241 : 2 : g_dbus_object_manager_client_new_for_bus_cb (GObject *object,
242 : : GAsyncResult *result,
243 : : gpointer user_data)
244 : : {
245 : 2 : g_autoptr (ValentTelephony) self = VALENT_TELEPHONY (user_data);
246 : 2 : g_autolist (GDBusObject) modems = NULL;
247 : 2 : g_autoptr (GError) error = NULL;
248 : :
249 : 2 : self->manager = g_dbus_object_manager_client_new_for_bus_finish (result,
250 : : &error);
251 : :
252 [ - + ]: 2 : if (self->manager == NULL)
253 : : {
254 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
255 [ # # ]: 0 : return;
256 : : }
257 : :
258 : 2 : modems = g_dbus_object_manager_get_objects (self->manager);
259 : :
260 [ + + ]: 3 : for (const GList *iter = modems; iter; iter = iter->next)
261 : 1 : on_modem_added (self->manager, iter->data, self);
262 : :
263 : 2 : g_signal_connect_object (self->manager,
264 : : "object-added",
265 : : G_CALLBACK (on_modem_added),
266 : : self, 0);
267 [ - + ]: 2 : g_signal_connect_object (self->manager,
268 : : "object-removed",
269 : : G_CALLBACK (on_modem_removed),
270 : : self, 0);
271 : : }
272 : :
273 : : /*
274 : : * ValentObject
275 : : */
276 : : static void
277 : 1 : valent_telephony_destroy (ValentObject *object)
278 : : {
279 : 1 : ValentTelephony *self = VALENT_TELEPHONY (object);
280 : :
281 : 1 : g_signal_handlers_disconnect_by_data (self->manager, self);
282 [ + - ]: 1 : g_clear_object (&self->manager);
283 : :
284 : 1 : VALENT_OBJECT_CLASS (valent_telephony_parent_class)->destroy (object);
285 : 1 : }
286 : :
287 : : /*
288 : : * GObject
289 : : */
290 : : static void
291 : 2 : valent_telephony_constructed (GObject *object)
292 : : {
293 : 2 : ValentTelephony *self = VALENT_TELEPHONY (object);
294 : :
295 : 2 : g_dbus_object_manager_client_new_for_bus (G_BUS_TYPE_SYSTEM,
296 : : G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START,
297 : : "org.freedesktop.ModemManager1",
298 : : "/org/freedesktop/ModemManager1",
299 : : NULL, NULL, NULL,
300 : : NULL,
301 : : (GAsyncReadyCallback)g_dbus_object_manager_client_new_for_bus_cb,
302 : : g_object_ref (self));
303 : :
304 : 2 : G_OBJECT_CLASS (valent_telephony_parent_class)->constructed (object);
305 : 2 : }
306 : :
307 : : static void
308 : 1 : valent_telephony_finalize (GObject *object)
309 : : {
310 : 1 : ValentTelephony *self = VALENT_TELEPHONY (object);
311 : :
312 [ + - ]: 1 : g_clear_pointer (&self->modems, g_hash_table_unref);
313 : :
314 : 1 : G_OBJECT_CLASS (valent_telephony_parent_class)->finalize (object);
315 : 1 : }
316 : :
317 : : static void
318 : 2 : valent_telephony_class_init (ValentTelephonyClass *klass)
319 : : {
320 : 2 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
321 : 2 : ValentObjectClass *vobject_class = VALENT_OBJECT_CLASS (klass);
322 : :
323 : 2 : object_class->constructed = valent_telephony_constructed;
324 : 2 : object_class->finalize = valent_telephony_finalize;
325 : :
326 : 2 : vobject_class->destroy = valent_telephony_destroy;
327 : :
328 : : /**
329 : : * ValentTelephony::changed:
330 : : * @self: a `ValentTelephony`
331 : : *
332 : : * `ValentTelephony`::changed is emitted whenever a relevant property changes.
333 : : */
334 : 4 : signals [CHANGED] =
335 : 2 : g_signal_new ("changed",
336 : : G_TYPE_FROM_CLASS (klass),
337 : : G_SIGNAL_RUN_LAST,
338 : : 0,
339 : : NULL, NULL, NULL,
340 : : G_TYPE_NONE, 0);
341 : 2 : }
342 : :
343 : : static void
344 : 2 : valent_telephony_init (ValentTelephony *self)
345 : : {
346 : 2 : self->modems = g_hash_table_new_full (g_str_hash,
347 : : g_str_equal,
348 : : g_free,
349 : : g_object_unref);
350 : 2 : }
351 : :
352 : : /**
353 : : * valent_telephony_get_default:
354 : : *
355 : : * Get the default [class@Valent.Network].
356 : : *
357 : : * Returns: (transfer none) (not nullable): a `ValentTelephony`
358 : : *
359 : : * Since: 1.0
360 : : */
361 : : ValentTelephony *
362 : 3 : valent_telephony_get_default (void)
363 : : {
364 [ + + ]: 3 : if (default_telephony == NULL)
365 : : {
366 : 2 : default_telephony = g_object_new (VALENT_TYPE_TELEPHONY, NULL);
367 : :
368 : 2 : g_object_add_weak_pointer (G_OBJECT (default_telephony),
369 : : (gpointer)&default_telephony);
370 : : }
371 : :
372 : 3 : return default_telephony;
373 : : }
374 : :
375 : : static JsonNode *
376 : 6 : valent_telephony_serialize_modem (GDBusProxy *proxy)
377 : : {
378 : 12 : g_autoptr (JsonBuilder) builder = NULL;
379 : 6 : GVariant *value;
380 : 6 : unsigned int access_technologies;
381 : 6 : uint32_t signal_quality;
382 : 6 : gboolean signal_recent;
383 : 6 : int32_t state;
384 : 6 : const char *telephony_type;
385 : 6 : int64_t signal_strength = -1;
386 : :
387 [ + - + - : 6 : g_assert (G_IS_DBUS_PROXY (proxy));
- + - - ]
388 : :
389 : : /* Extract the relevant properties */
390 : 6 : value = g_dbus_proxy_get_cached_property (proxy, "AccessTechnologies");
391 : 6 : g_variant_get (value, "u", &access_technologies);
392 [ + - ]: 6 : g_clear_pointer (&value, g_variant_unref);
393 : :
394 : 6 : value = g_dbus_proxy_get_cached_property (proxy, "SignalQuality");
395 : 6 : g_variant_get (value, "(ub)", &signal_quality, &signal_recent);
396 [ + - ]: 6 : g_clear_pointer (&value, g_variant_unref);
397 : :
398 : 6 : value = g_dbus_proxy_get_cached_property (proxy, "State");
399 : 6 : g_variant_get (value, "i", &state);
400 [ + - ]: 6 : g_clear_pointer (&value, g_variant_unref);
401 : :
402 : : /* Convert to KDE Connect values (`networkType`, `signalStrength`) */
403 : 6 : telephony_type = get_telephony_type_string (access_technologies);
404 : :
405 [ + + ]: 6 : if (state >= MM_MODEM_STATE_ENABLED)
406 : 2 : signal_strength = (int64_t)(signal_quality / 20);
407 : :
408 : : /* Serialize to a JsonNode */
409 : 6 : builder = json_builder_new ();
410 : 6 : json_builder_begin_object (builder);
411 : 6 : json_builder_set_member_name (builder, "networkType");
412 : 6 : json_builder_add_string_value (builder, telephony_type);
413 : 6 : json_builder_set_member_name (builder, "signalStrength");
414 : 6 : json_builder_add_int_value (builder, signal_strength);
415 : 6 : json_builder_end_object (builder);
416 : :
417 [ + - ]: 6 : return json_builder_get_root (builder);
418 : : }
419 : :
420 : : /**
421 : : * valent_telephony_get_signal_strengths:
422 : : * @telephony: a `ValentTelephony`
423 : : *
424 : : * Get a serialized dictionary of the known modems' status.
425 : : *
426 : : * Returns: (transfer full) (nullable): a `JsonNode`
427 : : *
428 : : * Since: 1.0
429 : : */
430 : : JsonNode *
431 : 8 : valent_telephony_get_signal_strengths (ValentTelephony *telephony)
432 : : {
433 : 16 : g_autoptr (JsonBuilder) builder = NULL;
434 : 8 : GHashTableIter iter;
435 : 8 : gpointer key, value;
436 : 8 : unsigned int m = 0;
437 : :
438 [ + - ]: 8 : g_return_val_if_fail (VALENT_IS_TELEPHONY (telephony), NULL);
439 : :
440 : 8 : builder = json_builder_new ();
441 : 8 : json_builder_begin_object (builder);
442 : :
443 : 8 : g_hash_table_iter_init (&iter, telephony->modems);
444 : :
445 [ + + ]: 14 : while (g_hash_table_iter_next (&iter, &key, &value))
446 : : {
447 : 6 : g_autoptr (JsonNode) modem = NULL;
448 : 6 : g_autofree char *id = NULL;
449 : :
450 : 6 : id = g_strdup_printf ("%u", m++);
451 : 6 : modem = valent_telephony_serialize_modem (G_DBUS_PROXY (value));
452 : :
453 : 6 : json_builder_set_member_name (builder, id);
454 : 6 : json_builder_add_value (builder, g_steal_pointer (&modem));
455 : : }
456 : :
457 : 8 : json_builder_end_object (builder);
458 : :
459 : 8 : return json_builder_get_root (builder);
460 : : }
461 : :
|