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 : : GTlsCertificate *certificate;
44 : :
45 : : GPtrArray *devices;
46 : : GHashTable *plugins;
47 : : ValentContext *plugins_context;
48 : : JsonNode *state;
49 : :
50 : : GDBusObjectManagerServer *dbus;
51 : : GHashTable *exports;
52 : : };
53 : :
54 : : static void valent_device_manager_add_device (ValentDeviceManager *manager,
55 : : ValentDevice *device);
56 : : static void valent_device_manager_remove_device (ValentDeviceManager *manager,
57 : : ValentDevice *device);
58 : : static ValentDevice * valent_device_manager_ensure_device (ValentDeviceManager *manager,
59 : : JsonNode *identity);
60 : :
61 : : static void g_list_model_iface_init (GListModelInterface *iface);
62 : :
63 [ + + + - ]: 452 : G_DEFINE_FINAL_TYPE_WITH_CODE (ValentDeviceManager, valent_device_manager, VALENT_TYPE_APPLICATION_PLUGIN,
64 : : G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, g_list_model_iface_init))
65 : :
66 : : static ValentDeviceManager *default_manager = NULL;
67 : :
68 : :
69 : : /*
70 : : * GListModel
71 : : */
72 : : static gpointer
73 : 6 : valent_device_manager_get_item (GListModel *list,
74 : : unsigned int position)
75 : : {
76 : 6 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (list);
77 : :
78 [ + - ]: 6 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
79 : :
80 [ + - ]: 6 : if G_UNLIKELY (position >= self->devices->len)
81 : : return NULL;
82 : :
83 : 6 : return g_object_ref (g_ptr_array_index (self->devices, position));
84 : : }
85 : :
86 : : static GType
87 : 0 : valent_device_manager_get_item_type (GListModel *list)
88 : : {
89 : 0 : return VALENT_TYPE_DEVICE;
90 : : }
91 : :
92 : : static unsigned int
93 : 6 : valent_device_manager_get_n_items (GListModel *list)
94 : : {
95 : 6 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (list);
96 : :
97 [ + - ]: 6 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
98 : :
99 : 6 : return self->devices->len;
100 : : }
101 : :
102 : : static void
103 : 58 : g_list_model_iface_init (GListModelInterface *iface)
104 : : {
105 : 58 : iface->get_item = valent_device_manager_get_item;
106 : 58 : iface->get_item_type = valent_device_manager_get_item_type;
107 : 58 : iface->get_n_items = valent_device_manager_get_n_items;
108 : 58 : }
109 : :
110 : : /*
111 : : * DBus
112 : : */
113 : : typedef struct
114 : : {
115 : : GDBusObjectManagerServer *manager;
116 : : GDBusConnection *connection;
117 : : char *object_path;
118 : : unsigned int actions_id;
119 : : unsigned int menu_id;
120 : : } DeviceExport;
121 : :
122 : : static void
123 : 1 : device_export_free (gpointer data)
124 : : {
125 : 1 : DeviceExport *info = data;
126 : :
127 : 1 : g_dbus_object_manager_server_unexport (info->manager, info->object_path);
128 : 1 : g_dbus_connection_unexport_action_group (info->connection, info->actions_id);
129 : 1 : g_dbus_connection_unexport_menu_model (info->connection, info->menu_id);
130 : :
131 [ + - ]: 1 : g_clear_pointer (&info->object_path, g_free);
132 [ + - ]: 1 : g_clear_object (&info->connection);
133 [ + - ]: 1 : g_clear_object (&info->manager);
134 : 1 : g_free (info);
135 : 1 : }
136 : :
137 : : static char *
138 : 1 : valent_device_manager_get_device_object_path (ValentDeviceManager *self,
139 : : ValentDevice *device)
140 : : {
141 : 1 : GDBusObjectManager *object_manager;
142 : 1 : GString *object_path = NULL;
143 : 1 : const char *base_path = NULL;
144 : 1 : const char *id = NULL;
145 : :
146 [ + - ]: 1 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
147 [ - + ]: 1 : g_assert (VALENT_IS_DEVICE (device));
148 : :
149 : 1 : object_manager = G_DBUS_OBJECT_MANAGER (self->dbus);
150 : 1 : base_path = g_dbus_object_manager_get_object_path (object_manager);
151 : :
152 : 1 : object_path = g_string_new (base_path);
153 [ - + ]: 1 : g_string_append (object_path, "/Device/");
154 : :
155 : 1 : id = valent_device_get_id (device);
156 : :
157 [ + + ]: 12 : while (*id)
158 : : {
159 [ + + ]: 11 : if G_LIKELY (g_ascii_isalnum (*id))
160 [ + - ]: 10 : g_string_append_c (object_path, *id);
161 : : else
162 [ + - ]: 1 : g_string_append_c (object_path, '_');
163 : :
164 : 11 : id++;
165 : : }
166 : :
167 : 1 : return g_string_free (object_path, FALSE);
168 : : }
169 : :
170 : : static void
171 : 1 : valent_device_manager_export_device (ValentDeviceManager *self,
172 : : ValentDevice *device)
173 : : {
174 : 2 : g_autoptr (GDBusObjectSkeleton) object = NULL;
175 [ + - ]: 1 : g_autoptr (GDBusInterfaceSkeleton) iface = NULL;
176 : 1 : DeviceExport *info;
177 : 1 : GActionGroup *action_group;
178 : 1 : GMenuModel *menu_model;
179 : :
180 : 1 : VALENT_ENTRY;
181 : :
182 [ + - ]: 1 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
183 [ - + ]: 1 : g_assert (VALENT_IS_DEVICE (device));
184 : :
185 [ + - ]: 1 : if (g_hash_table_contains (self->exports, device))
186 : 1 : VALENT_EXIT;
187 : :
188 : 1 : info = g_new0 (DeviceExport, 1);
189 : 1 : info->manager = g_object_ref (self->dbus);
190 : 1 : info->connection = g_dbus_object_manager_server_get_connection (self->dbus);
191 : 1 : info->object_path = valent_device_manager_get_device_object_path (self,
192 : : device);
193 : :
194 : 1 : object = g_dbus_object_skeleton_new (info->object_path);
195 : 1 : iface = valent_device_impl_new (device);
196 : 1 : g_dbus_object_skeleton_add_interface (object, iface);
197 : :
198 : 1 : action_group = G_ACTION_GROUP (device);
199 : 2 : info->actions_id = g_dbus_connection_export_action_group (info->connection,
200 : 1 : info->object_path,
201 : : action_group,
202 : : NULL);
203 : :
204 : 1 : menu_model = valent_device_get_menu (device);
205 : 2 : info->menu_id = g_dbus_connection_export_menu_model (info->connection,
206 : 1 : info->object_path,
207 : : menu_model,
208 : : NULL);
209 : :
210 : 1 : g_dbus_object_manager_server_export (self->dbus, object);
211 : 1 : g_hash_table_insert (self->exports, device, g_steal_pointer (&info));
212 : :
213 [ - + + - ]: 2 : VALENT_EXIT;
214 : : }
215 : :
216 : : /*
217 : : * Channel Services
218 : : */
219 : : static gboolean
220 : 3 : valent_device_manager_check_device (ValentDeviceManager *self,
221 : : ValentDevice *device)
222 : : {
223 : 3 : unsigned int n_unpaired = 0;
224 : :
225 [ - + ]: 3 : if ((valent_device_get_state (device) & VALENT_DEVICE_STATE_PAIRED) != 0)
226 : : return TRUE;
227 : :
228 [ + + ]: 6 : for (unsigned int i = 0, len = self->devices->len; i < len; i++)
229 : : {
230 : 3 : ValentDevice *check = g_ptr_array_index (self->devices, i);
231 : :
232 [ + - ]: 3 : if ((valent_device_get_state (check) & VALENT_DEVICE_STATE_PAIRED) == 0)
233 : 3 : n_unpaired++;
234 : : }
235 : :
236 [ + - ]: 3 : if (n_unpaired >= DEVICE_UNPAIRED_MAX)
237 : : {
238 : 0 : g_warning ("%s(): too many unpaired devices", G_STRFUNC);
239 : 0 : return FALSE;
240 : : }
241 : :
242 : : return TRUE;
243 : : }
244 : :
245 : : static void
246 : 3 : on_channel (ValentChannelService *service,
247 : : ValentChannel *channel,
248 : : ValentDeviceManager *self)
249 : : {
250 : 3 : JsonNode *identity;
251 : 3 : ValentDevice *device;
252 : :
253 : 3 : VALENT_ENTRY;
254 : :
255 [ + - ]: 3 : g_assert (VALENT_IS_CHANNEL_SERVICE (service));
256 [ - + ]: 3 : g_assert (VALENT_IS_CHANNEL (channel));
257 [ - + ]: 3 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
258 : :
259 [ - + ]: 3 : if ((identity = valent_channel_get_peer_identity (channel)) == NULL)
260 : : {
261 : 0 : g_warning ("%s(): %s missing peer identity",
262 : : G_STRFUNC,
263 : : G_OBJECT_TYPE_NAME (channel));
264 : 0 : VALENT_EXIT;
265 : : }
266 : :
267 [ + - ]: 3 : if ((device = valent_device_manager_ensure_device (self, identity)) == NULL)
268 : 3 : VALENT_EXIT;
269 : :
270 [ + - ]: 3 : if (!valent_device_manager_check_device (self, device))
271 : 3 : VALENT_EXIT;
272 : :
273 : 3 : valent_device_set_channel (device, channel);
274 : :
275 : 3 : VALENT_EXIT;
276 : : }
277 : :
278 : : static void
279 : 0 : g_async_initable_init_async_cb (GAsyncInitable *initable,
280 : : GAsyncResult *result,
281 : : gpointer user_data)
282 : : {
283 : 0 : g_autoptr (GError) error = NULL;
284 : :
285 : 0 : VALENT_ENTRY;
286 : :
287 [ # # ]: 0 : g_assert (VALENT_IS_CHANNEL_SERVICE (initable));
288 : :
289 [ # # # # ]: 0 : if (!g_async_initable_init_finish (initable, result, &error) &&
290 : 0 : !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
291 : 0 : g_warning ("%s: %s", G_OBJECT_TYPE_NAME (initable), error->message);
292 : :
293 [ # # ]: 0 : VALENT_EXIT;
294 : : }
295 : :
296 : : static inline void
297 : 14 : valent_device_manager_enable_plugin (ValentDeviceManager *self,
298 : : ValentPlugin *plugin)
299 : : {
300 [ + - ]: 14 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
301 [ - + ]: 14 : g_assert (plugin != NULL);
302 : :
303 : 14 : plugin->extension = peas_engine_create_extension (valent_get_plugin_engine (),
304 : : plugin->info,
305 : : VALENT_TYPE_CHANNEL_SERVICE,
306 : : "source", self,
307 : : "context", plugin->context,
308 : : "certificate", self->certificate,
309 : : NULL);
310 [ - + ]: 14 : g_return_if_fail (G_IS_OBJECT (plugin->extension));
311 : :
312 : 14 : g_signal_connect_object (plugin->extension,
313 : : "channel",
314 : : G_CALLBACK (on_channel),
315 : : self,
316 : : G_CONNECT_DEFAULT);
317 : :
318 [ + - + - : 14 : if (G_IS_ASYNC_INITABLE (plugin->extension))
+ - - + ]
319 : : {
320 : 14 : g_autoptr (GCancellable) destroy = NULL;
321 : :
322 : : /* Use a cancellable in case the plugin is unloaded before the operation
323 : : * completes. Chain to the manager in case it's destroyed. */
324 : 0 : plugin->cancellable = g_cancellable_new ();
325 : 0 : destroy = valent_object_chain_cancellable (VALENT_OBJECT (self),
326 : : plugin->cancellable);
327 : :
328 [ # # ]: 0 : g_async_initable_init_async (G_ASYNC_INITABLE (plugin->extension),
329 : : G_PRIORITY_DEFAULT,
330 : : destroy,
331 : : (GAsyncReadyCallback)g_async_initable_init_async_cb,
332 : : NULL);
333 : : }
334 : : }
335 : :
336 : : static inline void
337 : 1 : valent_device_manager_disable_plugin (ValentDeviceManager *self,
338 : : ValentPlugin *plugin)
339 : : {
340 [ + - ]: 1 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
341 [ - + ]: 1 : g_assert (plugin != NULL);
342 [ - + ]: 1 : g_return_if_fail (G_IS_OBJECT (plugin->extension));
343 : :
344 [ + - ]: 1 : if (plugin->extension != NULL)
345 : : {
346 : 1 : valent_object_destroy (VALENT_OBJECT (plugin->extension));
347 [ + - ]: 1 : g_clear_object (&plugin->extension);
348 : : }
349 : : }
350 : :
351 : : static void
352 : 2 : on_plugin_enabled_changed (ValentPlugin *plugin)
353 : : {
354 [ + - ]: 2 : g_assert (plugin != NULL);
355 [ - + ]: 2 : g_assert (VALENT_IS_DEVICE_MANAGER (plugin->parent));
356 : :
357 [ + + ]: 2 : if (valent_plugin_get_enabled (plugin))
358 : 1 : valent_device_manager_enable_plugin (plugin->parent, plugin);
359 : : else
360 : 1 : valent_device_manager_disable_plugin (plugin->parent, plugin);
361 : 2 : }
362 : :
363 : : static void
364 : 85 : on_load_service (PeasEngine *engine,
365 : : PeasPluginInfo *info,
366 : : ValentDeviceManager *self)
367 : : {
368 : 85 : ValentPlugin *plugin;
369 : :
370 [ + - ]: 85 : g_assert (PEAS_IS_ENGINE (engine));
371 [ - + ]: 85 : g_assert (info != NULL);
372 [ - + ]: 85 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
373 : :
374 : : /* We're only interested in one GType */
375 [ + + ]: 85 : if (!peas_engine_provides_extension (engine, info, VALENT_TYPE_CHANNEL_SERVICE))
376 : : return;
377 : :
378 : 13 : VALENT_NOTE ("%s: %s",
379 : : g_type_name (VALENT_TYPE_CHANNEL_SERVICE),
380 : : peas_plugin_info_get_module_name (info));
381 : :
382 : 13 : plugin = valent_plugin_new (self, self->plugins_context, info,
383 : : G_CALLBACK (on_plugin_enabled_changed));
384 : 13 : g_hash_table_insert (self->plugins, info, plugin);
385 : :
386 [ + - ]: 13 : if (valent_plugin_get_enabled (plugin))
387 : 13 : valent_device_manager_enable_plugin (self, plugin);
388 : : }
389 : :
390 : : static void
391 : 2 : on_unload_service (PeasEngine *engine,
392 : : PeasPluginInfo *info,
393 : : ValentDeviceManager *self)
394 : : {
395 [ + - ]: 2 : g_assert (PEAS_IS_ENGINE (engine));
396 [ - + ]: 2 : g_assert (info != NULL);
397 [ - + ]: 2 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
398 : :
399 : : /* We're only interested in one GType */
400 [ + - ]: 2 : if (!peas_engine_provides_extension (engine, info, VALENT_TYPE_CHANNEL_SERVICE))
401 : : return;
402 : :
403 : 2 : VALENT_NOTE ("%s: %s",
404 : : g_type_name (VALENT_TYPE_CHANNEL_SERVICE),
405 : : peas_plugin_info_get_module_name (info));
406 : :
407 : 2 : g_hash_table_remove (self->plugins, info);
408 : : }
409 : :
410 : : /*
411 : : * Device Management
412 : : */
413 : : static void
414 : 6 : on_device_state (ValentDevice *device,
415 : : GParamSpec *pspec,
416 : : ValentDeviceManager *self)
417 : : {
418 : 6 : ValentDeviceState state = valent_device_get_state (device);
419 : :
420 [ + + ]: 6 : if ((state & VALENT_DEVICE_STATE_CONNECTED) != 0 &&
421 : : (state & VALENT_DEVICE_STATE_PAIRED) != 0)
422 : : {
423 : 1 : g_autoptr (ValentChannel) channel = NULL;
424 : 1 : JsonNode *identity = NULL;
425 : :
426 [ + - ]: 1 : if ((channel = valent_device_ref_channel (device)) == NULL)
427 : : return;
428 : :
429 : 1 : identity = valent_channel_get_peer_identity (channel);
430 : 2 : json_object_set_object_member (json_node_get_object (self->state),
431 : 1 : valent_device_get_id (device),
432 : : json_node_dup_object (identity));
433 : : }
434 [ + - ]: 5 : else if ((state & VALENT_DEVICE_STATE_PAIRED) == 0)
435 : : {
436 : 5 : json_object_remove_member (json_node_get_object (self->state),
437 : 5 : valent_device_get_id (device));
438 : :
439 [ + + ]: 5 : if ((state & VALENT_DEVICE_STATE_CONNECTED) == 0)
440 : 1 : valent_device_manager_remove_device (self, device);
441 : : }
442 : : }
443 : :
444 : : static void
445 : 6 : valent_device_manager_add_device (ValentDeviceManager *self,
446 : : ValentDevice *device)
447 : : {
448 : 6 : unsigned int position = 0;
449 : :
450 : 6 : VALENT_ENTRY;
451 : :
452 [ + - ]: 6 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
453 [ - + ]: 6 : g_assert (VALENT_IS_DEVICE (device));
454 : :
455 [ - + ]: 6 : if (g_ptr_array_find (self->devices, device, NULL))
456 : : {
457 : 0 : g_warning ("Device \"%s\" not found in \"%s\"",
458 : : valent_device_get_name (device),
459 : : G_OBJECT_TYPE_NAME (self));
460 : 0 : VALENT_EXIT;
461 : : }
462 : :
463 : 6 : g_signal_connect_object (device,
464 : : "notify::state",
465 : : G_CALLBACK (on_device_state),
466 : : self,
467 : : G_CONNECT_DEFAULT);
468 : 6 : g_signal_connect_object (device,
469 : : "destroy",
470 : : G_CALLBACK (valent_device_manager_remove_device),
471 : : self,
472 : : G_CONNECT_SWAPPED);
473 : :
474 : 6 : position = self->devices->len;
475 : 6 : g_ptr_array_add (self->devices, g_object_ref (device));
476 : 6 : g_list_model_items_changed (G_LIST_MODEL (self), position, 0, 1);
477 : :
478 [ - + ]: 6 : if (self->dbus != NULL)
479 : 0 : valent_device_manager_export_device (self, device);
480 : :
481 : 6 : VALENT_EXIT;
482 : : }
483 : :
484 : : static inline gboolean
485 : 0 : find_device_by_id (gconstpointer a,
486 : : gconstpointer b)
487 : : {
488 : 0 : return g_str_equal (valent_device_get_id ((ValentDevice *)a), (const char *)b);
489 : : }
490 : :
491 : : static ValentDevice *
492 : 6 : valent_device_manager_ensure_device (ValentDeviceManager *self,
493 : : JsonNode *identity)
494 : : {
495 : 6 : const char *device_id;
496 : 6 : unsigned int position = 0;
497 : :
498 [ + - ]: 6 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
499 [ - + ]: 6 : g_assert (VALENT_IS_PACKET (identity));
500 : :
501 [ - + ]: 6 : if (!valent_packet_get_string (identity, "deviceId", &device_id))
502 : : {
503 : 0 : g_debug ("%s(): expected \"deviceId\" field holding a string",
504 : : G_STRFUNC);
505 : 0 : return NULL;
506 : : }
507 : :
508 [ + - ]: 6 : if (!g_ptr_array_find_with_equal_func (self->devices,
509 : : device_id,
510 : : find_device_by_id,
511 : : &position))
512 : : {
513 : 12 : g_autoptr (ValentContext) context = NULL;
514 [ + - ]: 6 : g_autoptr (ValentDevice) device = NULL;
515 : :
516 : 6 : context = valent_context_new (self->context, "device", device_id);
517 : 6 : device = valent_device_new_full (identity, context);
518 : :
519 : 6 : valent_device_manager_add_device (self, device);
520 [ + - ]: 6 : position = (self->devices->len - 1);
521 : : }
522 : :
523 : 6 : return g_ptr_array_index (self->devices, position);
524 : : }
525 : :
526 : : static gboolean
527 : 1 : valent_device_manager_destroy_device (gpointer data)
528 : : {
529 : 1 : valent_object_destroy (VALENT_OBJECT (data));
530 : :
531 : 1 : return G_SOURCE_REMOVE;
532 : : }
533 : :
534 : : static void
535 : 1 : valent_device_manager_remove_device (ValentDeviceManager *self,
536 : : ValentDevice *device)
537 : : {
538 : 1 : unsigned int position = 0;
539 : 2 : g_autoptr (ValentDevice) item = NULL;
540 : :
541 : 1 : VALENT_ENTRY;
542 : :
543 [ + - ]: 1 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
544 [ - + ]: 1 : g_assert (VALENT_IS_DEVICE (device));
545 : :
546 [ - + ]: 1 : if (!g_ptr_array_find (self->devices, device, &position))
547 : : {
548 : 0 : g_warning ("Device \"%s\" not found in \"%s\"",
549 : : valent_device_get_name (device),
550 : : G_OBJECT_TYPE_NAME (self));
551 : 1 : VALENT_EXIT;
552 : : }
553 : :
554 : 1 : g_signal_handlers_disconnect_by_data (device, self);
555 : :
556 : : /* NOTE: Dropping the last reference in a device's `notify::state` handler
557 : : * would result in a use-after-free for subsequent handlers
558 : : */
559 [ + - ]: 1 : if (!valent_object_in_destruction (VALENT_OBJECT (device)))
560 : : {
561 : 1 : g_idle_add_full (G_PRIORITY_DEFAULT,
562 : : valent_device_manager_destroy_device,
563 : : g_object_ref (device),
564 : : g_object_unref);
565 : : }
566 : :
567 : 1 : g_hash_table_remove (self->exports, device);
568 : 1 : item = g_ptr_array_steal_index (self->devices, position);
569 : 1 : g_list_model_items_changed (G_LIST_MODEL (self), position, 1, 0);
570 : :
571 [ + - ]: 1 : VALENT_EXIT;
572 : : }
573 : :
574 : : static void
575 : 11 : valent_device_manager_load_state (ValentDeviceManager *self)
576 : : {
577 : 11 : JsonObjectIter iter;
578 : 11 : const char *device_id;
579 : 11 : JsonNode *identity;
580 : :
581 [ + - ]: 11 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
582 : :
583 [ + - ]: 11 : if (self->state == NULL)
584 : : {
585 : 22 : g_autoptr (JsonParser) parser = NULL;
586 [ + - ]: 11 : g_autoptr (GFile) file = NULL;
587 : :
588 : 11 : parser = json_parser_new ();
589 : 11 : file = valent_context_get_cache_file (self->context, "devices.json");
590 [ + + ]: 11 : if (json_parser_load_from_file (parser, g_file_peek_path (file), NULL))
591 : 3 : self->state = json_parser_steal_root (parser);
592 : :
593 [ + + - + ]: 11 : if (self->state == NULL || !JSON_NODE_HOLDS_OBJECT (self->state))
594 : : {
595 [ - + ]: 8 : g_clear_pointer (&self->state, json_node_unref);
596 : 8 : self->state = json_node_new (JSON_NODE_OBJECT);
597 : 8 : json_node_take_object (self->state, json_object_new ());
598 : : }
599 : : }
600 : :
601 : 11 : json_object_iter_init (&iter, json_node_get_object (self->state));
602 [ + + ]: 14 : while (json_object_iter_next (&iter, &device_id, &identity))
603 : 3 : valent_device_manager_ensure_device (self, identity);
604 : 11 : }
605 : :
606 : : static void
607 : 11 : valent_device_manager_save_state (ValentDeviceManager *self)
608 : : {
609 : 22 : g_autoptr (JsonGenerator) generator = NULL;
610 [ + - ]: 11 : g_autoptr (GFile) file = NULL;
611 [ + - ]: 11 : g_autoptr (GError) error = NULL;
612 : :
613 [ + - ]: 11 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
614 : :
615 : 11 : generator = g_object_new (JSON_TYPE_GENERATOR,
616 : : "pretty", TRUE,
617 : : "root", self->state,
618 : : NULL);
619 : :
620 [ + + ]: 16 : for (unsigned int i = 0, len = self->devices->len; i < len; i++)
621 : : {
622 : 5 : ValentDevice *device = g_ptr_array_index (self->devices, i);
623 : 5 : ValentDeviceState state = valent_device_get_state (device);
624 : :
625 [ + + ]: 5 : if ((state & VALENT_DEVICE_STATE_PAIRED) == 0)
626 : : {
627 : 4 : json_object_remove_member (json_node_get_object (self->state),
628 : 4 : valent_device_get_id (device));
629 : : }
630 : : }
631 : :
632 : 11 : file = valent_context_get_cache_file (self->context, "devices.json");
633 [ - + ]: 11 : if (!json_generator_to_file (generator, g_file_peek_path (file), &error))
634 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
635 : 11 : }
636 : :
637 : : /*
638 : : * GActions
639 : : */
640 : : static void
641 : 0 : device_action (GSimpleAction *action,
642 : : GVariant *parameter,
643 : : gpointer user_data)
644 : : {
645 : 0 : ValentDeviceManager *manager = valent_device_manager_get_default ();
646 : 0 : const char *device_id;
647 : 0 : const char *name;
648 : 0 : g_autoptr (GVariantIter) targetv = NULL;
649 [ # # ]: 0 : g_autoptr (GVariant) target = NULL;
650 : :
651 [ # # ]: 0 : g_assert (VALENT_IS_DEVICE_MANAGER (manager));
652 : :
653 : : /* (<Valent.Device:id>, <Gio.Action:name>, [<GLib.Variant>]) */
654 : 0 : g_variant_get (parameter, "(&s&sav)", &device_id, &name, &targetv);
655 : 0 : g_variant_iter_next (targetv, "v", &target);
656 : :
657 [ # # ]: 0 : for (unsigned int i = 0, len = manager->devices->len; i < len; i++)
658 : : {
659 : 0 : ValentDevice *device = g_ptr_array_index (manager->devices, i);
660 : :
661 [ # # ]: 0 : if (g_strcmp0 (device_id, valent_device_get_id (device)) == 0)
662 : : {
663 : 0 : g_action_group_activate_action (G_ACTION_GROUP (device), name, target);
664 : 0 : break;
665 : : }
666 : : }
667 : 0 : }
668 : :
669 : : static const GActionEntry app_actions[] = {
670 : : { "device", device_action, "(ssav)", NULL, NULL },
671 : : };
672 : :
673 : : /*
674 : : * ValentApplicationPlugin
675 : : */
676 : : static gboolean
677 : 4 : valent_device_manager_dbus_register (ValentApplicationPlugin *plugin,
678 : : GDBusConnection *connection,
679 : : const char *object_path,
680 : : GError **error)
681 : : {
682 : 4 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (plugin);
683 : :
684 [ + - ]: 4 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
685 [ + - + - : 4 : g_assert (G_IS_DBUS_CONNECTION (connection));
- + - - ]
686 [ - + ]: 4 : g_assert (g_variant_is_object_path (object_path));
687 [ - + ]: 4 : g_return_val_if_fail (self->dbus == NULL, TRUE);
688 : :
689 : 4 : self->dbus = g_dbus_object_manager_server_new (object_path);
690 : 4 : g_dbus_object_manager_server_set_connection (self->dbus, connection);
691 : :
692 [ + + ]: 5 : for (unsigned int i = 0, len = self->devices->len; i < len; i++)
693 : : {
694 : 1 : ValentDevice *device = g_ptr_array_index (self->devices, i);
695 : :
696 : 1 : valent_device_manager_export_device (self, device);
697 : : }
698 : :
699 : : return TRUE;
700 : : }
701 : :
702 : : static void
703 : 4 : valent_device_manager_dbus_unregister (ValentApplicationPlugin *plugin,
704 : : GDBusConnection *connection,
705 : : const char *object_path)
706 : : {
707 : 4 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (plugin);
708 : :
709 [ + - ]: 4 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
710 [ + - + - : 4 : g_assert (G_IS_DBUS_CONNECTION (connection));
- + - - ]
711 [ - + ]: 4 : g_assert (g_variant_is_object_path (object_path));
712 [ + - + - : 4 : g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER (self->dbus));
+ - - + ]
713 : :
714 : 4 : g_hash_table_remove_all (self->exports);
715 : 4 : g_dbus_object_manager_server_set_connection (self->dbus, NULL);
716 [ + - ]: 4 : g_clear_object (&self->dbus);
717 : : }
718 : :
719 : : static void
720 : 11 : valent_device_manager_shutdown (ValentApplicationPlugin *plugin)
721 : : {
722 : 11 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (plugin);
723 : 11 : unsigned int n_devices = 0;
724 : :
725 [ + - ]: 11 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
726 [ + - + - : 11 : g_return_if_fail (G_IS_CANCELLABLE (self->cancellable));
- + - - ]
727 : :
728 : 11 : g_cancellable_cancel (self->cancellable);
729 [ + - ]: 11 : g_clear_object (&self->cancellable);
730 : :
731 : 11 : g_signal_handlers_disconnect_by_data (valent_get_plugin_engine (), self);
732 : 11 : g_hash_table_remove_all (self->plugins);
733 : 11 : valent_device_manager_save_state (self);
734 : :
735 : 11 : n_devices = self->devices->len;
736 [ + + ]: 16 : for (unsigned int i = 0; i < n_devices; i++)
737 : : {
738 : 5 : ValentDevice *device = g_ptr_array_index (self->devices, i);
739 : 5 : g_signal_handlers_disconnect_by_data (device, self);
740 : : }
741 : :
742 : 11 : g_ptr_array_remove_range (self->devices, 0, n_devices);
743 : 11 : g_list_model_items_changed (G_LIST_MODEL (self), 0, n_devices, 0);
744 : :
745 [ + - ]: 11 : if (self == default_manager)
746 : : {
747 : 11 : GApplication *application = g_application_get_default ();
748 : :
749 [ + + ]: 11 : if (application != NULL)
750 : : {
751 [ + + ]: 6 : for (size_t i = 0; i < G_N_ELEMENTS (app_actions); i++)
752 : : {
753 : 3 : g_action_map_remove_action (G_ACTION_MAP (application),
754 : : app_actions[i].name);
755 : : }
756 : : }
757 : : }
758 : : }
759 : :
760 : : static void
761 : 11 : valent_device_manager_startup (ValentApplicationPlugin *plugin)
762 : : {
763 : 11 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (plugin);
764 : 11 : PeasEngine *engine = NULL;
765 : 11 : unsigned int n_plugins = 0;
766 : :
767 [ + - ]: 11 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
768 [ - + ]: 11 : g_return_if_fail (self->cancellable == NULL);
769 : :
770 : 11 : self->cancellable = g_cancellable_new ();
771 : 11 : valent_device_manager_load_state (self);
772 : :
773 : 11 : engine = valent_get_plugin_engine ();
774 : 11 : g_signal_connect_object (engine,
775 : : "load-plugin",
776 : : G_CALLBACK (on_load_service),
777 : : self,
778 : : G_CONNECT_AFTER);
779 : 11 : g_signal_connect_object (engine,
780 : : "unload-plugin",
781 : : G_CALLBACK (on_unload_service),
782 : : self,
783 : : G_CONNECT_DEFAULT);
784 : :
785 : 11 : n_plugins = g_list_model_get_n_items (G_LIST_MODEL (engine));
786 [ + + ]: 94 : for (unsigned int i = 0; i < n_plugins; i++)
787 : : {
788 : 83 : g_autoptr (PeasPluginInfo) info = NULL;
789 : :
790 : 83 : info = g_list_model_get_item (G_LIST_MODEL (engine), i);
791 [ + - ]: 83 : if (peas_plugin_info_is_loaded (info))
792 : 83 : on_load_service (engine, info, self);
793 : : }
794 : :
795 [ + - ]: 11 : if (self == default_manager)
796 : : {
797 : 11 : GApplication *application = g_application_get_default ();
798 : :
799 [ + + ]: 11 : if (application != NULL)
800 : : {
801 : 3 : g_action_map_add_action_entries (G_ACTION_MAP (application),
802 : : app_actions,
803 : : G_N_ELEMENTS (app_actions),
804 : : application);
805 : : }
806 : : }
807 : : }
808 : :
809 : : /*
810 : : * GObject
811 : : */
812 : : static void
813 : 13 : valent_device_manager_constructed (GObject *object)
814 : : {
815 : 13 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (object);
816 : 26 : g_autoptr (GFile) file = NULL;
817 [ + - ]: 13 : g_autoptr (GError) error = NULL;
818 : :
819 : 13 : G_OBJECT_CLASS (valent_device_manager_parent_class)->constructed (object);
820 : :
821 : 13 : file = valent_context_get_config_file (self->context, ".");
822 : 13 : self->certificate = valent_certificate_new_sync (g_file_peek_path (file), &error);
823 [ - + ]: 13 : if (self->certificate == NULL)
824 : 0 : g_critical ("%s(): %s", G_STRFUNC, error->message);
825 : :
826 [ + - ]: 13 : if (default_manager == NULL)
827 : : {
828 : 13 : default_manager = self;
829 : 13 : g_object_add_weak_pointer (G_OBJECT (default_manager),
830 : : (gpointer)&default_manager);
831 : : }
832 : 13 : }
833 : :
834 : : static void
835 : 13 : valent_device_manager_finalize (GObject *object)
836 : : {
837 : 13 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (object);
838 : :
839 [ + - ]: 13 : g_clear_pointer (&self->exports, g_hash_table_unref);
840 [ + - ]: 13 : g_clear_pointer (&self->plugins, g_hash_table_unref);
841 [ + - ]: 13 : g_clear_pointer (&self->plugins_context, g_object_unref);
842 [ + - ]: 13 : g_clear_pointer (&self->devices, g_ptr_array_unref);
843 [ + + ]: 13 : g_clear_pointer (&self->state, json_node_unref);
844 : :
845 [ + - ]: 13 : g_clear_object (&self->certificate);
846 [ + - ]: 13 : g_clear_object (&self->context);
847 : :
848 : 13 : G_OBJECT_CLASS (valent_device_manager_parent_class)->finalize (object);
849 : 13 : }
850 : :
851 : : static void
852 : 58 : valent_device_manager_class_init (ValentDeviceManagerClass *klass)
853 : : {
854 : 58 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
855 : 58 : ValentApplicationPluginClass *plugin_class = VALENT_APPLICATION_PLUGIN_CLASS (klass);
856 : :
857 : 58 : object_class->constructed = valent_device_manager_constructed;
858 : 58 : object_class->finalize = valent_device_manager_finalize;
859 : :
860 : 58 : plugin_class->dbus_register = valent_device_manager_dbus_register;
861 : 58 : plugin_class->dbus_unregister = valent_device_manager_dbus_unregister;
862 : 58 : plugin_class->shutdown = valent_device_manager_shutdown;
863 : 58 : plugin_class->startup = valent_device_manager_startup;
864 : : }
865 : :
866 : : static void
867 : 13 : valent_device_manager_init (ValentDeviceManager *self)
868 : : {
869 : 13 : self->context = valent_context_new (NULL, NULL, NULL);
870 : 13 : self->devices = g_ptr_array_new_with_free_func (g_object_unref);
871 : 13 : self->exports = g_hash_table_new_full (NULL, NULL, NULL, device_export_free);
872 : 13 : self->plugins = g_hash_table_new_full (NULL, NULL, NULL, valent_plugin_free);
873 : 13 : self->plugins_context = valent_context_new (self->context, "network", NULL);
874 : 13 : }
875 : :
876 : : /**
877 : : * valent_device_manager_get_default:
878 : : *
879 : : * Get the default [class@Valent.DeviceManager].
880 : : *
881 : : * Returns: (transfer none) (not nullable): a `ValentDeviceManager`
882 : : *
883 : : * Since: 1.0
884 : : */
885 : : ValentDeviceManager *
886 : 11 : valent_device_manager_get_default (void)
887 : : {
888 [ + + ]: 11 : if (default_manager == NULL)
889 : 10 : return g_object_new (VALENT_TYPE_DEVICE_MANAGER, NULL);
890 : :
891 : : return default_manager;
892 : : }
893 : :
894 : : /**
895 : : * valent_device_manager_refresh:
896 : : * @manager: a `ValentDeviceManager`
897 : : *
898 : : * Refresh the available devices.
899 : : *
900 : : * This method calls [method@Valent.ChannelService.identify] for each enabled
901 : : * service, requesting it to announce itself on its respective network.
902 : : *
903 : : * Since: 1.0
904 : : */
905 : : void
906 : 4 : valent_device_manager_refresh (ValentDeviceManager *manager)
907 : : {
908 : 4 : GHashTableIter iter;
909 : 4 : ValentPlugin *plugin;
910 : :
911 : 4 : VALENT_ENTRY;
912 : :
913 [ + - ]: 4 : g_return_if_fail (VALENT_IS_DEVICE_MANAGER (manager));
914 : :
915 : 4 : g_hash_table_iter_init (&iter, manager->plugins);
916 [ + + ]: 7 : while (g_hash_table_iter_next (&iter, NULL, (void **)&plugin))
917 : : {
918 [ - + ]: 3 : if (plugin->extension == NULL)
919 : 0 : continue;
920 : :
921 : 3 : valent_channel_service_identify (VALENT_CHANNEL_SERVICE (plugin->extension),
922 : : NULL);
923 : : }
924 : :
925 : 4 : VALENT_EXIT;
926 : : }
927 : :
|