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