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