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-device-manager"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <gio/gio.h>
9 : : #include <libvalent-core.h>
10 : :
11 : : #include "../core/valent-component-private.h"
12 : : #include "valent-certificate.h"
13 : : #include "valent-channel.h"
14 : : #include "valent-channel-service.h"
15 : : #include "valent-device.h"
16 : : #include "valent-device-impl.h"
17 : : #include "valent-device-manager.h"
18 : : #include "valent-device-private.h"
19 : : #include "valent-packet.h"
20 : :
21 : : #define DEVICE_UNPAIRED_MAX (10)
22 : :
23 : :
24 : : /**
25 : : * ValentDeviceManager:
26 : : *
27 : : * A class for discovering and managing devices.
28 : : *
29 : : * `ValentDeviceManager` manages the available [class@Valent.Device] objects,
30 : : * connecting them when [signal@Valent.ChannelService::channel] is emitted by an
31 : : * enabled implementation, exporting them on D-Bus and removing them when they
32 : : * become unavailable.
33 : : *
34 : : * Since: 1.0
35 : : */
36 : :
37 : : struct _ValentDeviceManager
38 : : {
39 : : ValentApplicationPlugin parent_instance;
40 : :
41 : : GCancellable *cancellable;
42 : : ValentContext *context;
43 : : GSettings *settings;
44 : : GTlsCertificate *certificate;
45 : : const char *id;
46 : : char *name;
47 : :
48 : : GPtrArray *devices;
49 : : GHashTable *plugins;
50 : : ValentContext *plugins_context;
51 : : JsonNode *state;
52 : :
53 : : GDBusObjectManagerServer *dbus;
54 : : GHashTable *exported;
55 : : };
56 : :
57 : : static void valent_device_manager_add_device (ValentDeviceManager *manager,
58 : : ValentDevice *device);
59 : : static void valent_device_manager_remove_device (ValentDeviceManager *manager,
60 : : ValentDevice *device);
61 : : static ValentDevice * valent_device_manager_ensure_device (ValentDeviceManager *manager,
62 : : JsonNode *identity);
63 : :
64 : : static void g_list_model_iface_init (GListModelInterface *iface);
65 : :
66 [ + + + - ]: 528 : G_DEFINE_FINAL_TYPE_WITH_CODE (ValentDeviceManager, valent_device_manager, VALENT_TYPE_APPLICATION_PLUGIN,
67 : : G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, g_list_model_iface_init))
68 : :
69 : : enum {
70 : : PROP_0,
71 : : PROP_NAME,
72 : : N_PROPERTIES
73 : : };
74 : :
75 : : static GParamSpec *properties[N_PROPERTIES] = { NULL, };
76 : :
77 : : static ValentDeviceManager *default_manager = NULL;
78 : :
79 : :
80 : : /*
81 : : * GListModel
82 : : */
83 : : static gpointer
84 : 10 : valent_device_manager_get_item (GListModel *list,
85 : : unsigned int position)
86 : : {
87 : 10 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (list);
88 : :
89 [ + - ]: 10 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
90 : :
91 [ + - ]: 10 : if G_UNLIKELY (position >= self->devices->len)
92 : : return NULL;
93 : :
94 : 10 : return g_object_ref (g_ptr_array_index (self->devices, position));
95 : : }
96 : :
97 : : static GType
98 : 0 : valent_device_manager_get_item_type (GListModel *list)
99 : : {
100 : 0 : return VALENT_TYPE_DEVICE;
101 : : }
102 : :
103 : : static unsigned int
104 : 10 : valent_device_manager_get_n_items (GListModel *list)
105 : : {
106 : 10 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (list);
107 : :
108 [ + - ]: 10 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
109 : :
110 : 10 : return self->devices->len;
111 : : }
112 : :
113 : : static void
114 : 58 : g_list_model_iface_init (GListModelInterface *iface)
115 : : {
116 : 58 : iface->get_item = valent_device_manager_get_item;
117 : 58 : iface->get_item_type = valent_device_manager_get_item_type;
118 : 58 : iface->get_n_items = valent_device_manager_get_n_items;
119 : 58 : }
120 : :
121 : : /*
122 : : * DBus
123 : : */
124 : : typedef struct
125 : : {
126 : : GDBusConnection *connection;
127 : : char *object_path;
128 : : unsigned int actions_id;
129 : : unsigned int menu_id;
130 : : } ExportedDevice;
131 : :
132 : : static char *
133 : 1 : valent_device_manager_get_device_object_path (ValentDeviceManager *self,
134 : : ValentDevice *device)
135 : : {
136 : 1 : GDBusObjectManager *object_manager;
137 : 1 : GString *object_path = NULL;
138 : 1 : const char *base_path = NULL;
139 : 1 : const char *id = NULL;
140 : :
141 [ + - ]: 1 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
142 [ - + ]: 1 : g_assert (VALENT_IS_DEVICE (device));
143 : :
144 : 1 : object_manager = G_DBUS_OBJECT_MANAGER (self->dbus);
145 : 1 : base_path = g_dbus_object_manager_get_object_path (object_manager);
146 : :
147 : 1 : object_path = g_string_new (base_path);
148 [ - + ]: 1 : g_string_append (object_path, "/Device/");
149 : :
150 : 1 : id = valent_device_get_id (device);
151 : :
152 [ + + ]: 12 : while (*id)
153 : : {
154 [ + + ]: 11 : if G_LIKELY (g_ascii_isalnum (*id))
155 [ + - ]: 10 : g_string_append_c (object_path, *id);
156 : : else
157 [ + - ]: 1 : g_string_append_c (object_path, '_');
158 : :
159 : 11 : id++;
160 : : }
161 : :
162 : 1 : return g_string_free (object_path, FALSE);
163 : : }
164 : :
165 : : static void
166 : 1 : valent_device_manager_export_device (ValentDeviceManager *self,
167 : : ValentDevice *device)
168 : : {
169 : 2 : g_autoptr (GDBusObjectSkeleton) object = NULL;
170 [ + - ]: 1 : g_autoptr (GDBusInterfaceSkeleton) iface = NULL;
171 : 1 : ExportedDevice *info;
172 : 1 : GActionGroup *action_group;
173 : 1 : GMenuModel *menu_model;
174 : :
175 : 1 : VALENT_ENTRY;
176 : :
177 [ + - ]: 1 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
178 [ - + ]: 1 : g_assert (VALENT_IS_DEVICE (device));
179 : :
180 [ + - ]: 1 : if (g_hash_table_contains (self->exported, device))
181 : 1 : VALENT_EXIT;
182 : :
183 : 1 : info = g_new0 (ExportedDevice, 1);
184 : 1 : info->connection = g_dbus_object_manager_server_get_connection (self->dbus);
185 : 1 : info->object_path = valent_device_manager_get_device_object_path (self,
186 : : device);
187 : :
188 : : /* Export the ValentDevice, GActionGroup and GMenuModel interfaces on the same
189 : : * connection and path */
190 : 1 : object = g_dbus_object_skeleton_new (info->object_path);
191 : 1 : iface = valent_device_impl_new (device);
192 : 1 : g_dbus_object_skeleton_add_interface (object, iface);
193 : :
194 : 1 : action_group = G_ACTION_GROUP (device);
195 : 2 : info->actions_id = g_dbus_connection_export_action_group (info->connection,
196 : 1 : info->object_path,
197 : : action_group,
198 : : NULL);
199 : :
200 : 1 : menu_model = valent_device_get_menu (device);
201 : 2 : info->menu_id = g_dbus_connection_export_menu_model (info->connection,
202 : 1 : info->object_path,
203 : : menu_model,
204 : : NULL);
205 : :
206 : 1 : g_dbus_object_manager_server_export (self->dbus, object);
207 : 1 : g_hash_table_insert (self->exported, device, info);
208 : :
209 [ - + + - ]: 2 : VALENT_EXIT;
210 : : }
211 : :
212 : : static void
213 : 2 : valent_device_manager_unexport_device (ValentDeviceManager *self,
214 : : ValentDevice *device)
215 : : {
216 : 2 : gpointer data;
217 : 2 : ExportedDevice *info = NULL;
218 : :
219 : 2 : VALENT_ENTRY;
220 : :
221 [ + - ]: 2 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
222 [ - + ]: 2 : g_assert (VALENT_IS_DEVICE (device));
223 : :
224 [ + + ]: 2 : if (!g_hash_table_steal_extended (self->exported, device, NULL, &data))
225 : 1 : VALENT_EXIT;
226 : :
227 : 1 : info = (ExportedDevice *)data;
228 : :
229 : 1 : g_dbus_object_manager_server_unexport (self->dbus, info->object_path);
230 : 1 : g_dbus_connection_unexport_action_group (info->connection, info->actions_id);
231 : 1 : g_dbus_connection_unexport_menu_model (info->connection, info->menu_id);
232 : :
233 [ + - ]: 1 : g_clear_pointer (&info->object_path, g_free);
234 [ + - ]: 1 : g_clear_object (&info->connection);
235 : 1 : g_free (info);
236 : :
237 : 1 : VALENT_EXIT;
238 : : }
239 : :
240 : : /*
241 : : * Channel Services
242 : : */
243 : : static void
244 : 13 : manager_plugin_free (gpointer data)
245 : : {
246 : 13 : ValentPlugin *plugin = data;
247 : :
248 [ + - ]: 13 : if (plugin->extension != NULL)
249 : : {
250 : 13 : g_signal_handlers_disconnect_by_data (plugin->extension, plugin->parent);
251 : 13 : valent_object_destroy (VALENT_OBJECT (plugin->extension));
252 : : }
253 : :
254 : 13 : g_clear_pointer (&plugin, valent_plugin_free);
255 : 13 : }
256 : :
257 : : static gboolean
258 : 4 : valent_device_manager_check_device (ValentDeviceManager *self,
259 : : ValentDevice *device)
260 : : {
261 : 4 : unsigned int n_unpaired = 0;
262 : :
263 [ + + ]: 4 : if ((valent_device_get_state (device) & VALENT_DEVICE_STATE_PAIRED) != 0)
264 : : return TRUE;
265 : :
266 [ + + ]: 6 : for (unsigned int i = 0, len = self->devices->len; i < len; i++)
267 : : {
268 : 3 : ValentDevice *check = g_ptr_array_index (self->devices, i);
269 : :
270 [ + - ]: 3 : if ((valent_device_get_state (check) & VALENT_DEVICE_STATE_PAIRED) == 0)
271 : 3 : n_unpaired++;
272 : : }
273 : :
274 [ + - ]: 3 : if (n_unpaired >= DEVICE_UNPAIRED_MAX)
275 : : {
276 : 0 : g_warning ("%s(): too many unpaired devices", G_STRFUNC);
277 : 0 : return FALSE;
278 : : }
279 : :
280 : : return TRUE;
281 : : }
282 : :
283 : : static void
284 : 4 : on_channel (ValentChannelService *service,
285 : : ValentChannel *channel,
286 : : ValentDeviceManager *self)
287 : : {
288 : 4 : JsonNode *identity;
289 : 4 : ValentDevice *device;
290 : :
291 : 4 : VALENT_ENTRY;
292 : :
293 [ + - ]: 4 : g_assert (VALENT_IS_CHANNEL_SERVICE (service));
294 [ - + ]: 4 : g_assert (VALENT_IS_CHANNEL (channel));
295 [ - + ]: 4 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
296 : :
297 [ - + ]: 4 : if ((identity = valent_channel_get_peer_identity (channel)) == NULL)
298 : : {
299 : 0 : g_warning ("%s(): %s missing peer identity",
300 : : G_STRFUNC,
301 : : G_OBJECT_TYPE_NAME (channel));
302 : 0 : VALENT_EXIT;
303 : : }
304 : :
305 [ + - ]: 4 : if ((device = valent_device_manager_ensure_device (self, identity)) == NULL)
306 : 4 : VALENT_EXIT;
307 : :
308 [ + - ]: 4 : if (!valent_device_manager_check_device (self, device))
309 : 4 : VALENT_EXIT;
310 : :
311 : 4 : valent_device_set_channel (device, channel);
312 : :
313 : 4 : VALENT_EXIT;
314 : : }
315 : :
316 : : static void
317 : 0 : g_async_initable_init_async_cb (GAsyncInitable *initable,
318 : : GAsyncResult *result,
319 : : gpointer user_data)
320 : : {
321 : 0 : g_autoptr (GError) error = NULL;
322 : :
323 : 0 : VALENT_ENTRY;
324 : :
325 [ # # ]: 0 : g_assert (VALENT_IS_CHANNEL_SERVICE (initable));
326 : :
327 [ # # # # ]: 0 : if (!g_async_initable_init_finish (initable, result, &error) &&
328 : 0 : !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
329 : 0 : g_warning ("%s: %s", G_OBJECT_TYPE_NAME (initable), error->message);
330 : :
331 [ # # ]: 0 : VALENT_EXIT;
332 : : }
333 : :
334 : : static inline void
335 : 14 : valent_device_manager_enable_plugin (ValentDeviceManager *self,
336 : : ValentPlugin *plugin)
337 : : {
338 [ + - ]: 14 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
339 [ - + ]: 14 : g_assert (plugin != NULL);
340 : :
341 : 14 : plugin->extension = peas_engine_create_extension (valent_get_plugin_engine (),
342 : : plugin->info,
343 : : VALENT_TYPE_CHANNEL_SERVICE,
344 : : "context", plugin->context,
345 : : "certificate", self->certificate,
346 : : "name", self->name,
347 : : NULL);
348 [ - + ]: 14 : g_return_if_fail (G_IS_OBJECT (plugin->extension));
349 : :
350 : 14 : g_object_bind_property (self, "name",
351 : : plugin->extension, "name",
352 : : G_BINDING_DEFAULT);
353 : :
354 : 14 : g_signal_connect_object (plugin->extension,
355 : : "channel",
356 : : G_CALLBACK (on_channel),
357 : : self, 0);
358 : :
359 [ + - + - : 14 : if (G_IS_ASYNC_INITABLE (plugin->extension))
+ - - + ]
360 : : {
361 : 14 : g_autoptr (GCancellable) destroy = NULL;
362 : :
363 : : /* Use a cancellable in case the plugin is unloaded before the operation
364 : : * completes. Chain to the manager in case it's destroyed. */
365 : 0 : plugin->cancellable = g_cancellable_new ();
366 : 0 : destroy = valent_object_chain_cancellable (VALENT_OBJECT (self),
367 : : plugin->cancellable);
368 : :
369 [ # # ]: 0 : g_async_initable_init_async (G_ASYNC_INITABLE (plugin->extension),
370 : : G_PRIORITY_DEFAULT,
371 : : destroy,
372 : : (GAsyncReadyCallback)g_async_initable_init_async_cb,
373 : : NULL);
374 : : }
375 : : }
376 : :
377 : : static inline void
378 : 1 : valent_device_manager_disable_plugin (ValentDeviceManager *self,
379 : : ValentPlugin *plugin)
380 : : {
381 [ + - ]: 1 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
382 [ - + ]: 1 : g_assert (plugin != NULL);
383 [ - + ]: 1 : g_return_if_fail (G_IS_OBJECT (plugin->extension));
384 : :
385 [ + - ]: 1 : if (plugin->extension != NULL)
386 : : {
387 : 1 : g_signal_handlers_disconnect_by_data (plugin->extension, self);
388 : 1 : valent_object_destroy (VALENT_OBJECT (plugin->extension));
389 [ + - ]: 1 : g_clear_object (&plugin->extension);
390 : : }
391 : : }
392 : :
393 : : static void
394 : 2 : on_plugin_enabled_changed (ValentPlugin *plugin)
395 : : {
396 [ + - ]: 2 : g_assert (plugin != NULL);
397 [ - + ]: 2 : g_assert (VALENT_IS_DEVICE_MANAGER (plugin->parent));
398 : :
399 [ + + ]: 2 : if (valent_plugin_get_enabled (plugin))
400 : 1 : valent_device_manager_enable_plugin (plugin->parent, plugin);
401 : : else
402 : 1 : valent_device_manager_disable_plugin (plugin->parent, plugin);
403 : 2 : }
404 : :
405 : : static void
406 : 85 : on_load_service (PeasEngine *engine,
407 : : PeasPluginInfo *info,
408 : : ValentDeviceManager *self)
409 : : {
410 : 85 : ValentPlugin *plugin;
411 : :
412 [ + - ]: 85 : g_assert (PEAS_IS_ENGINE (engine));
413 [ - + ]: 85 : g_assert (info != NULL);
414 [ - + ]: 85 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
415 : :
416 : : /* We're only interested in one GType */
417 [ + + ]: 85 : if (!peas_engine_provides_extension (engine, info, VALENT_TYPE_CHANNEL_SERVICE))
418 : : return;
419 : :
420 : 13 : VALENT_NOTE ("%s: %s",
421 : : g_type_name (VALENT_TYPE_CHANNEL_SERVICE),
422 : : peas_plugin_info_get_module_name (info));
423 : :
424 : 13 : plugin = valent_plugin_new (self, self->plugins_context, info,
425 : : G_CALLBACK (on_plugin_enabled_changed));
426 : 13 : g_hash_table_insert (self->plugins, info, plugin);
427 : :
428 [ + - ]: 13 : if (valent_plugin_get_enabled (plugin))
429 : 13 : valent_device_manager_enable_plugin (self, plugin);
430 : : }
431 : :
432 : : static void
433 : 2 : on_unload_service (PeasEngine *engine,
434 : : PeasPluginInfo *info,
435 : : ValentDeviceManager *self)
436 : : {
437 [ + - ]: 2 : g_assert (PEAS_IS_ENGINE (engine));
438 [ - + ]: 2 : g_assert (info != NULL);
439 [ - + ]: 2 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
440 : :
441 : : /* We're only interested in one GType */
442 [ + - ]: 2 : if (!peas_engine_provides_extension (engine, info, VALENT_TYPE_CHANNEL_SERVICE))
443 : : return;
444 : :
445 : 2 : VALENT_NOTE ("%s: %s",
446 : : g_type_name (VALENT_TYPE_CHANNEL_SERVICE),
447 : : peas_plugin_info_get_module_name (info));
448 : :
449 : 2 : g_hash_table_remove (self->plugins, info);
450 : : }
451 : :
452 : : /*
453 : : * Device Management
454 : : */
455 : : static gboolean
456 : 1 : valent_device_manager_remove_device_main (gpointer data)
457 : : {
458 : 1 : g_object_unref (VALENT_DEVICE (data));
459 : :
460 : 1 : return G_SOURCE_REMOVE;
461 : : }
462 : :
463 : : static void
464 : 8 : on_device_state (ValentDevice *device,
465 : : GParamSpec *pspec,
466 : : ValentDeviceManager *self)
467 : : {
468 : 8 : ValentDeviceState state = valent_device_get_state (device);
469 : :
470 : : /* Devices that become connected and paired are remembered */
471 [ + + ]: 8 : if ((state & VALENT_DEVICE_STATE_CONNECTED) != 0 &&
472 : : (state & VALENT_DEVICE_STATE_PAIRED) != 0)
473 : : {
474 : 2 : g_autoptr (ValentChannel) channel = NULL;
475 : 2 : JsonNode *identity = NULL;
476 : :
477 [ + - ]: 2 : if ((channel = valent_device_ref_channel (device)) == NULL)
478 : : return;
479 : :
480 : 2 : identity = valent_channel_get_peer_identity (channel);
481 : :
482 : 4 : json_object_set_object_member (json_node_get_object (self->state),
483 : 2 : valent_device_get_id (device),
484 : : json_node_dup_object (identity));
485 : : }
486 : :
487 : : /* Devices that become disconnected and unpaired are forgotten */
488 [ + + ]: 8 : if ((state & VALENT_DEVICE_STATE_CONNECTED) == 0 &&
489 : : (state & VALENT_DEVICE_STATE_PAIRED) == 0)
490 : : {
491 : 1 : json_object_remove_member (json_node_get_object (self->state),
492 : 1 : valent_device_get_id (device));
493 : 1 : valent_device_manager_remove_device (self, device);
494 : : }
495 : : }
496 : :
497 : : static ValentDevice *
498 : 14 : valent_device_manager_lookup (ValentDeviceManager *manager,
499 : : const char *id)
500 : : {
501 [ + - ]: 14 : g_assert (VALENT_IS_DEVICE_MANAGER (manager));
502 [ - + ]: 14 : g_assert (id != NULL);
503 : :
504 [ + + ]: 14 : for (unsigned int i = 0, len = manager->devices->len; i < len; i++)
505 : : {
506 : 7 : ValentDevice *device = g_ptr_array_index (manager->devices, i);
507 : :
508 [ - + ]: 7 : if (g_str_equal (id, valent_device_get_id (device)))
509 : : return device;
510 : : }
511 : :
512 : : return NULL;
513 : : }
514 : :
515 : : static void
516 : 7 : valent_device_manager_add_device (ValentDeviceManager *self,
517 : : ValentDevice *device)
518 : : {
519 : 7 : unsigned int position = 0;
520 : :
521 : 7 : VALENT_ENTRY;
522 : :
523 [ + - ]: 7 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
524 [ - + ]: 7 : g_assert (VALENT_IS_DEVICE (device));
525 : :
526 [ + - ]: 7 : if (g_ptr_array_find (self->devices, device, NULL))
527 : 7 : VALENT_EXIT;
528 : :
529 : 7 : g_signal_connect_object (device,
530 : : "notify::state",
531 : : G_CALLBACK (on_device_state),
532 : : self,
533 : : 0);
534 : :
535 : 7 : position = self->devices->len;
536 : 7 : g_ptr_array_add (self->devices, g_object_ref (device));
537 : 7 : g_list_model_items_changed (G_LIST_MODEL (self), position, 0, 1);
538 : :
539 [ - + ]: 7 : if (self->dbus != NULL)
540 : 0 : valent_device_manager_export_device (self, device);
541 : :
542 : 7 : VALENT_EXIT;
543 : : }
544 : :
545 : : static void
546 : 1 : valent_device_manager_remove_device (ValentDeviceManager *manager,
547 : : ValentDevice *device)
548 : : {
549 : 1 : unsigned int position = 0;
550 : :
551 : 1 : VALENT_ENTRY;
552 : :
553 [ + - ]: 1 : g_assert (VALENT_IS_DEVICE_MANAGER (manager));
554 [ - + ]: 1 : g_assert (VALENT_IS_DEVICE (device));
555 : :
556 : 1 : g_object_ref (device);
557 : :
558 [ + - ]: 1 : if (g_ptr_array_find (manager->devices, device, &position))
559 : : {
560 : 1 : valent_device_manager_unexport_device (manager, device);
561 : 1 : g_signal_handlers_disconnect_by_data (device, manager);
562 : 1 : g_ptr_array_remove_index (manager->devices, position);
563 : 1 : g_list_model_items_changed (G_LIST_MODEL (manager), position, 1, 0);
564 : :
565 : : // HACK: we are in a signal handler of a device's `notify::state`
566 : : // emission, so if we drop the last reference the emitting object
567 : : // and other handlers may be setup for a use-after-free error.
568 : 1 : g_idle_add (valent_device_manager_remove_device_main, g_object_ref (device));
569 : : }
570 : :
571 : 1 : g_object_unref (device);
572 : :
573 : 1 : VALENT_EXIT;
574 : : }
575 : :
576 : : static ValentDevice *
577 : 7 : valent_device_manager_ensure_device (ValentDeviceManager *manager,
578 : : JsonNode *identity)
579 : : {
580 : 7 : const char *device_id;
581 : :
582 [ + - ]: 7 : g_assert (VALENT_IS_DEVICE_MANAGER (manager));
583 [ - + ]: 7 : g_assert (VALENT_IS_PACKET (identity));
584 : :
585 [ - + ]: 7 : if (!valent_packet_get_string (identity, "deviceId", &device_id))
586 : : {
587 : 0 : g_debug ("%s(): expected \"deviceId\" field holding a string",
588 : : G_STRFUNC);
589 : 0 : return NULL;
590 : : }
591 : :
592 [ + - ]: 7 : if (valent_device_manager_lookup (manager, device_id) == NULL)
593 : : {
594 : 14 : g_autoptr (ValentContext) context = NULL;
595 [ + - ]: 7 : g_autoptr (ValentDevice) device = NULL;
596 : :
597 : 7 : context = valent_context_new (manager->context, "device", device_id);
598 : 7 : device = valent_device_new_full (identity, context);
599 : :
600 [ + - ]: 7 : valent_device_manager_add_device (manager, device);
601 : : }
602 : :
603 : 7 : return valent_device_manager_lookup (manager, device_id);
604 : : }
605 : :
606 : : static void
607 : 11 : valent_device_manager_load_state (ValentDeviceManager *self)
608 : : {
609 : 11 : JsonObjectIter iter;
610 : 11 : const char *device_id;
611 : 11 : JsonNode *identity;
612 : :
613 [ + - ]: 11 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
614 : :
615 [ + - ]: 11 : if (self->state == NULL)
616 : : {
617 : 22 : g_autoptr (JsonParser) parser = NULL;
618 [ + - ]: 11 : g_autoptr (GFile) file = NULL;
619 : :
620 : 11 : file = valent_context_get_cache_file (self->context, "devices.json");
621 : :
622 : : /* Try to load the state file */
623 : 11 : parser = json_parser_new ();
624 : :
625 [ + + ]: 11 : if (json_parser_load_from_file (parser, g_file_peek_path (file), NULL))
626 : 3 : self->state = json_parser_steal_root (parser);
627 : :
628 [ + + - + ]: 11 : if (self->state == NULL || !JSON_NODE_HOLDS_OBJECT (self->state))
629 : : {
630 [ - + ]: 8 : g_clear_pointer (&self->state, json_node_unref);
631 : 8 : self->state = json_node_new (JSON_NODE_OBJECT);
632 : 8 : json_node_take_object (self->state, json_object_new ());
633 : : }
634 : : }
635 : :
636 : : /* Load devices */
637 : 11 : json_object_iter_init (&iter, json_node_get_object (self->state));
638 : :
639 [ + + ]: 14 : while (json_object_iter_next (&iter, &device_id, &identity))
640 : 3 : valent_device_manager_ensure_device (self, identity);
641 : 11 : }
642 : :
643 : : static void
644 : 11 : valent_device_manager_save_state (ValentDeviceManager *self)
645 : : {
646 : 22 : g_autoptr (JsonGenerator) generator = NULL;
647 [ + - ]: 11 : g_autoptr (GFile) file = NULL;
648 [ + - ]: 11 : g_autoptr (GError) error = NULL;
649 : :
650 [ + - ]: 11 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
651 : :
652 : 11 : generator = g_object_new (JSON_TYPE_GENERATOR,
653 : : "pretty", TRUE,
654 : : "root", self->state,
655 : : NULL);
656 : :
657 : 11 : file = valent_context_get_cache_file (self->context, "devices.json");
658 : :
659 [ - + ]: 11 : if (!json_generator_to_file (generator, g_file_peek_path (file), &error))
660 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
661 : 11 : }
662 : :
663 : : /*
664 : : * GActions
665 : : */
666 : : static void
667 : 0 : device_action (GSimpleAction *action,
668 : : GVariant *parameter,
669 : : gpointer user_data)
670 : : {
671 : 0 : ValentDeviceManager *manager = valent_device_manager_get_default ();
672 : 0 : const char *device_id;
673 : 0 : const char *name;
674 : 0 : g_autoptr (GVariantIter) targetv = NULL;
675 [ # # ]: 0 : g_autoptr (GVariant) target = NULL;
676 : :
677 [ # # ]: 0 : g_assert (VALENT_IS_DEVICE_MANAGER (manager));
678 : :
679 : : /* (<Valent.Device:id>, <Gio.Action:name>, [<GLib.Variant>]) */
680 : 0 : g_variant_get (parameter, "(&s&sav)", &device_id, &name, &targetv);
681 : 0 : g_variant_iter_next (targetv, "v", &target);
682 : :
683 [ # # ]: 0 : for (unsigned int i = 0, len = manager->devices->len; i < len; i++)
684 : : {
685 : 0 : ValentDevice *device = g_ptr_array_index (manager->devices, i);
686 : :
687 [ # # ]: 0 : if (g_strcmp0 (device_id, valent_device_get_id (device)) == 0)
688 : : {
689 : 0 : g_action_group_activate_action (G_ACTION_GROUP (device), name, target);
690 : 0 : break;
691 : : }
692 : : }
693 : 0 : }
694 : :
695 : : static const GActionEntry app_actions[] = {
696 : : { "device", device_action, "(ssav)", NULL, NULL },
697 : : };
698 : :
699 : : /*
700 : : * ValentApplicationPlugin
701 : : */
702 : : static gboolean
703 : 4 : valent_device_manager_dbus_register (ValentApplicationPlugin *plugin,
704 : : GDBusConnection *connection,
705 : : const char *object_path,
706 : : GError **error)
707 : : {
708 : 4 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (plugin);
709 : :
710 [ + - ]: 4 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
711 [ + - + - : 4 : g_assert (G_IS_DBUS_CONNECTION (connection));
- + - - ]
712 [ - + ]: 4 : g_assert (g_variant_is_object_path (object_path));
713 : :
714 [ + - ]: 4 : if (self->dbus != NULL)
715 : : return TRUE;
716 : :
717 : 4 : self->dbus = g_dbus_object_manager_server_new (object_path);
718 : 4 : g_dbus_object_manager_server_set_connection (self->dbus, connection);
719 : :
720 [ + + ]: 5 : for (unsigned int i = 0, len = self->devices->len; i < len; i++)
721 : : {
722 : 1 : ValentDevice *device = g_ptr_array_index (self->devices, i);
723 : :
724 : 1 : valent_device_manager_export_device (self, device);
725 : : }
726 : :
727 : : return TRUE;
728 : : }
729 : :
730 : : static void
731 : 4 : valent_device_manager_dbus_unregister (ValentApplicationPlugin *plugin,
732 : : GDBusConnection *connection,
733 : : const char *object_path)
734 : : {
735 : 4 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (plugin);
736 : :
737 [ + - ]: 4 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
738 [ + - + - : 4 : g_assert (G_IS_DBUS_CONNECTION (connection));
- + - - ]
739 [ - + ]: 4 : g_assert (g_variant_is_object_path (object_path));
740 : :
741 [ + - ]: 4 : if (self->dbus == NULL)
742 : : return;
743 : :
744 [ + + ]: 5 : for (unsigned int i = 0, len = self->devices->len; i < len; i++)
745 : : {
746 : 1 : ValentDevice *device = g_ptr_array_index (self->devices, i);
747 : :
748 : 1 : valent_device_manager_unexport_device (self, device);
749 : : }
750 : :
751 : 4 : g_dbus_object_manager_server_set_connection (self->dbus, NULL);
752 [ + - ]: 4 : g_clear_object (&self->dbus);
753 : : }
754 : :
755 : : static void
756 : 13 : valent_device_manager_shutdown (ValentApplicationPlugin *plugin)
757 : : {
758 : 13 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (plugin);
759 : 13 : PeasEngine *engine = NULL;
760 : 13 : unsigned int n_devices = 0;
761 : :
762 [ + - ]: 13 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
763 : :
764 : : /* We're already stopped */
765 [ + + ]: 13 : if (self->cancellable == NULL)
766 : : return;
767 : :
768 : : /* Cancel any running operations */
769 : 11 : g_cancellable_cancel (self->cancellable);
770 [ + - ]: 11 : g_clear_object (&self->cancellable);
771 : :
772 : : /* Stop and remove services */
773 : 11 : engine = valent_get_plugin_engine ();
774 : 11 : g_signal_handlers_disconnect_by_data (engine, self);
775 : 11 : g_hash_table_remove_all (self->plugins);
776 : :
777 : : /* Remove any devices */
778 : 11 : n_devices = self->devices->len;
779 : :
780 [ + + ]: 17 : for (unsigned int i = 0; i < n_devices; i++)
781 : : {
782 : 6 : ValentDevice *device = g_ptr_array_index (self->devices, i);
783 : 6 : g_signal_handlers_disconnect_by_data (device, self);
784 : : }
785 : :
786 : 11 : g_ptr_array_remove_range (self->devices, 0, n_devices);
787 : 11 : g_list_model_items_changed (G_LIST_MODEL (self), 0, n_devices, 0);
788 : :
789 : 11 : valent_device_manager_save_state (self);
790 [ + - ]: 11 : g_clear_object (&self->settings);
791 : :
792 : : /* Remove actions from the `app` group, if available */
793 [ + - ]: 11 : if (self == default_manager)
794 : : {
795 : 11 : GApplication *application = g_application_get_default ();
796 : :
797 [ + + ]: 11 : if (application != NULL)
798 : : {
799 [ + + ]: 6 : for (size_t i = 0; i < G_N_ELEMENTS (app_actions); i++)
800 : 3 : g_action_map_remove_action (G_ACTION_MAP (application),
801 : : app_actions[i].name);
802 : :
803 : : }
804 : : }
805 : : }
806 : :
807 : : static void
808 : 11 : valent_device_manager_startup (ValentApplicationPlugin *plugin)
809 : : {
810 : 11 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (plugin);
811 : 11 : PeasEngine *engine = NULL;
812 : 11 : g_autofree char *name = NULL;
813 : 11 : unsigned int n_plugins = 0;
814 : :
815 [ + - ]: 11 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
816 : :
817 : : /* We're already started */
818 [ - + ]: 11 : if (self->cancellable != NULL)
819 : 0 : return;
820 : :
821 : : /* Setup Manager */
822 : 11 : self->cancellable = g_cancellable_new ();
823 : 11 : self->settings = g_settings_new ("ca.andyholmes.Valent");
824 : 11 : g_settings_bind (self->settings, "name",
825 : : self, "name",
826 : : G_SETTINGS_BIND_DEFAULT);
827 : 11 : name = g_settings_get_string (self->settings, "name");
828 : 11 : valent_device_manager_set_name (self, name);
829 : 11 : valent_device_manager_load_state (self);
830 : :
831 : : /* Setup services */
832 : 11 : engine = valent_get_plugin_engine ();
833 : 11 : n_plugins = g_list_model_get_n_items (G_LIST_MODEL (engine));
834 : :
835 [ + + ]: 94 : for (unsigned int i = 0; i < n_plugins; i++)
836 : : {
837 : 83 : g_autoptr (PeasPluginInfo) info = NULL;
838 : :
839 : 83 : info = g_list_model_get_item (G_LIST_MODEL (engine), i);
840 : :
841 [ + - ]: 83 : if (peas_plugin_info_is_loaded (info))
842 : 83 : on_load_service (engine, info, self);
843 : : }
844 : :
845 : 11 : g_signal_connect_object (engine,
846 : : "load-plugin",
847 : : G_CALLBACK (on_load_service),
848 : : self,
849 : : G_CONNECT_AFTER);
850 : :
851 : 11 : g_signal_connect_object (engine,
852 : : "unload-plugin",
853 : : G_CALLBACK (on_unload_service),
854 : : self,
855 : : 0);
856 : :
857 : : /* Add actions to the `app` group, if available */
858 [ + - ]: 11 : if (self == default_manager)
859 : : {
860 : 11 : GApplication *application = g_application_get_default ();
861 : :
862 [ + + ]: 11 : if (application != NULL)
863 : : {
864 : 3 : g_action_map_add_action_entries (G_ACTION_MAP (application),
865 : : app_actions,
866 : : G_N_ELEMENTS (app_actions),
867 : : application);
868 : : }
869 : : }
870 : : }
871 : :
872 : : /*
873 : : * GObject
874 : : */
875 : : static void
876 : 13 : valent_device_manager_constructed (GObject *object)
877 : : {
878 : 13 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (object);
879 : 26 : g_autoptr (GFile) file = NULL;
880 [ + - ]: 13 : g_autoptr (GError) error = NULL;
881 : 13 : const char *path = NULL;
882 : :
883 [ + - ]: 13 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
884 : :
885 : : /* Generate certificate */
886 : 13 : file = valent_context_get_config_file (self->context, ".");
887 : 13 : path = g_file_peek_path (file);
888 : :
889 [ + - ]: 13 : if ((self->certificate = valent_certificate_new_sync (path, &error)) != NULL)
890 : 13 : self->id = valent_certificate_get_common_name (self->certificate);
891 : :
892 [ - + ]: 13 : if (self->id == NULL)
893 : : {
894 : 0 : self->id = valent_device_generate_id ();
895 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
896 : : }
897 : :
898 : : /* ... */
899 [ + - ]: 13 : if (default_manager == NULL)
900 : : {
901 : 13 : default_manager = self;
902 : 13 : g_object_add_weak_pointer (G_OBJECT (default_manager),
903 : : (gpointer)&default_manager);
904 : : }
905 : :
906 [ - + ]: 13 : G_OBJECT_CLASS (valent_device_manager_parent_class)->constructed (object);
907 : 13 : }
908 : :
909 : : static void
910 : 13 : valent_device_manager_finalize (GObject *object)
911 : : {
912 : 13 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (object);
913 : :
914 [ + - ]: 13 : g_clear_pointer (&self->exported, g_hash_table_unref);
915 [ + - ]: 13 : g_clear_pointer (&self->plugins, g_hash_table_unref);
916 [ + - ]: 13 : g_clear_pointer (&self->plugins_context, g_object_unref);
917 [ + - ]: 13 : g_clear_pointer (&self->devices, g_ptr_array_unref);
918 [ + + ]: 13 : g_clear_pointer (&self->state, json_node_unref);
919 : :
920 [ + - ]: 13 : g_clear_object (&self->certificate);
921 [ + - ]: 13 : g_clear_object (&self->context);
922 [ + - ]: 13 : g_clear_pointer (&self->name, g_free);
923 : :
924 : 13 : G_OBJECT_CLASS (valent_device_manager_parent_class)->finalize (object);
925 : 13 : }
926 : :
927 : : static void
928 : 1 : valent_device_manager_get_property (GObject *object,
929 : : guint prop_id,
930 : : GValue *value,
931 : : GParamSpec *pspec)
932 : : {
933 : 1 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (object);
934 : :
935 [ + - ]: 1 : switch (prop_id)
936 : : {
937 : 1 : case PROP_NAME:
938 : 1 : g_value_set_string (value, self->name);
939 : 1 : break;
940 : :
941 : 0 : default:
942 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
943 : : }
944 : 1 : }
945 : :
946 : : static void
947 : 24 : valent_device_manager_set_property (GObject *object,
948 : : guint prop_id,
949 : : const GValue *value,
950 : : GParamSpec *pspec)
951 : : {
952 : 24 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (object);
953 : :
954 [ + - ]: 24 : switch (prop_id)
955 : : {
956 : 24 : case PROP_NAME:
957 : 24 : valent_device_manager_set_name (self, g_value_get_string (value));
958 : 24 : break;
959 : :
960 : 0 : default:
961 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
962 : : }
963 : 24 : }
964 : :
965 : : static void
966 : 58 : valent_device_manager_class_init (ValentDeviceManagerClass *klass)
967 : : {
968 : 58 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
969 : 58 : ValentApplicationPluginClass *plugin_class = VALENT_APPLICATION_PLUGIN_CLASS (klass);
970 : :
971 : 58 : object_class->constructed = valent_device_manager_constructed;
972 : 58 : object_class->finalize = valent_device_manager_finalize;
973 : 58 : object_class->get_property = valent_device_manager_get_property;
974 : 58 : object_class->set_property = valent_device_manager_set_property;
975 : :
976 : 58 : plugin_class->dbus_register = valent_device_manager_dbus_register;
977 : 58 : plugin_class->dbus_unregister = valent_device_manager_dbus_unregister;
978 : 58 : plugin_class->shutdown = valent_device_manager_shutdown;
979 : 58 : plugin_class->startup = valent_device_manager_startup;
980 : :
981 : : /**
982 : : * ValentDeviceManager:name: (getter get_name) (setter set_name)
983 : : *
984 : : * The display name of the local device.
985 : : *
986 : : * Since: 1.0
987 : : */
988 : 116 : properties [PROP_NAME] =
989 : 58 : g_param_spec_string ("name", NULL, NULL,
990 : : "Valent",
991 : : (G_PARAM_READWRITE |
992 : : G_PARAM_CONSTRUCT |
993 : : G_PARAM_EXPLICIT_NOTIFY |
994 : : G_PARAM_STATIC_STRINGS));
995 : :
996 : 58 : g_object_class_install_properties (object_class, N_PROPERTIES, properties);
997 : 58 : }
998 : :
999 : : static void
1000 : 13 : valent_device_manager_init (ValentDeviceManager *self)
1001 : : {
1002 : 13 : self->context = valent_context_new (NULL, NULL, NULL);
1003 : 13 : self->devices = g_ptr_array_new_with_free_func (g_object_unref);
1004 : 13 : self->exported = g_hash_table_new (NULL, NULL);
1005 : 13 : self->plugins = g_hash_table_new_full (NULL, NULL, NULL, manager_plugin_free);
1006 : 13 : self->plugins_context = valent_context_new (self->context, "network", NULL);
1007 : 13 : }
1008 : :
1009 : : /**
1010 : : * valent_device_manager_get_default:
1011 : : *
1012 : : * Get the default [class@Valent.DeviceManager].
1013 : : *
1014 : : * Returns: (transfer none) (not nullable): a `ValentDeviceManager`
1015 : : *
1016 : : * Since: 1.0
1017 : : */
1018 : : ValentDeviceManager *
1019 : 11 : valent_device_manager_get_default (void)
1020 : : {
1021 [ + + ]: 11 : if (default_manager == NULL)
1022 : 10 : return g_object_new (VALENT_TYPE_DEVICE_MANAGER, NULL);
1023 : :
1024 : : return default_manager;
1025 : : }
1026 : :
1027 : : /**
1028 : : * valent_device_manager_get_name: (get-property name)
1029 : : * @manager: a `ValentDeviceManager`
1030 : : *
1031 : : * Get the display name of the local device.
1032 : : *
1033 : : * Returns: (transfer none): the local display name
1034 : : *
1035 : : * Since: 1.0
1036 : : */
1037 : : const char *
1038 : 0 : valent_device_manager_get_name (ValentDeviceManager *manager)
1039 : : {
1040 [ # # ]: 0 : g_return_val_if_fail (VALENT_IS_DEVICE_MANAGER (manager), NULL);
1041 : :
1042 : 0 : return manager->name;
1043 : : }
1044 : :
1045 : : /**
1046 : : * valent_device_manager_set_name: (set-property name)
1047 : : * @manager: a `ValentDeviceManager`
1048 : : * @name: (not nullable): a display name
1049 : : *
1050 : : * Set the display name of the local device to @name.
1051 : : *
1052 : : * Since: 1.0
1053 : : */
1054 : : void
1055 : 35 : valent_device_manager_set_name (ValentDeviceManager *manager,
1056 : : const char *name)
1057 : : {
1058 [ + - ]: 35 : g_return_if_fail (VALENT_IS_DEVICE_MANAGER (manager));
1059 [ + - - + ]: 35 : g_return_if_fail (name != NULL && *name != '\0');
1060 : :
1061 [ + + ]: 35 : if (g_set_str (&manager->name, name))
1062 : 13 : g_object_notify_by_pspec (G_OBJECT (manager), properties [PROP_NAME]);
1063 : : }
1064 : :
1065 : : /**
1066 : : * valent_device_manager_refresh:
1067 : : * @manager: a `ValentDeviceManager`
1068 : : *
1069 : : * Refresh the available devices.
1070 : : *
1071 : : * This method calls [method@Valent.ChannelService.identify] for each enabled
1072 : : * service, requesting it to announce itself on its respective network.
1073 : : *
1074 : : * Since: 1.0
1075 : : */
1076 : : void
1077 : 5 : valent_device_manager_refresh (ValentDeviceManager *manager)
1078 : : {
1079 : 5 : GHashTableIter iter;
1080 : 5 : ValentPlugin *plugin;
1081 : :
1082 : 5 : VALENT_ENTRY;
1083 : :
1084 [ + - ]: 5 : g_return_if_fail (VALENT_IS_DEVICE_MANAGER (manager));
1085 : :
1086 : 5 : g_hash_table_iter_init (&iter, manager->plugins);
1087 : :
1088 [ + + ]: 9 : while (g_hash_table_iter_next (&iter, NULL, (void **)&plugin))
1089 : : {
1090 [ - + ]: 4 : if (plugin->extension == NULL)
1091 : 0 : continue;
1092 : :
1093 : 4 : valent_channel_service_identify (VALENT_CHANNEL_SERVICE (plugin->extension),
1094 : : NULL);
1095 : : }
1096 : :
1097 : 5 : VALENT_EXIT;
1098 : : }
1099 : :
|