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 : : static inline void
70 : 6 : _valent_object_deref (gpointer data)
71 : : {
72 [ - + ]: 6 : g_assert (VALENT_IS_OBJECT (data));
73 : :
74 : 6 : valent_object_destroy (VALENT_OBJECT (data));
75 : 6 : g_object_unref (data);
76 : 6 : }
77 : :
78 : : /*
79 : : * GListModel
80 : : */
81 : : static gpointer
82 : 6 : valent_device_manager_get_item (GListModel *list,
83 : : unsigned int position)
84 : : {
85 : 6 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (list);
86 : :
87 [ - + ]: 6 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
88 : :
89 [ + - ]: 6 : if G_UNLIKELY (position >= self->devices->len)
90 : : return NULL;
91 : :
92 : 6 : return g_object_ref (g_ptr_array_index (self->devices, position));
93 : : }
94 : :
95 : : static GType
96 : 0 : valent_device_manager_get_item_type (GListModel *list)
97 : : {
98 : 0 : return VALENT_TYPE_DEVICE;
99 : : }
100 : :
101 : : static unsigned int
102 : 6 : valent_device_manager_get_n_items (GListModel *list)
103 : : {
104 : 6 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (list);
105 : :
106 [ - + ]: 6 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
107 : :
108 : 6 : return self->devices->len;
109 : : }
110 : :
111 : : static void
112 : 51 : g_list_model_iface_init (GListModelInterface *iface)
113 : : {
114 : 51 : iface->get_item = valent_device_manager_get_item;
115 : 51 : iface->get_item_type = valent_device_manager_get_item_type;
116 : 51 : iface->get_n_items = valent_device_manager_get_n_items;
117 : 51 : }
118 : :
119 : : /*
120 : : * DBus
121 : : */
122 : : typedef struct
123 : : {
124 : : GDBusObjectManagerServer *manager;
125 : : GDBusConnection *connection;
126 : : char *object_path;
127 : : unsigned int actions_id;
128 : : unsigned int menu_id;
129 : : } DeviceExport;
130 : :
131 : : static void
132 : 1 : device_export_free (gpointer data)
133 : : {
134 : 1 : DeviceExport *info = data;
135 : :
136 : 1 : g_dbus_object_manager_server_unexport (info->manager, info->object_path);
137 : 1 : g_dbus_connection_unexport_action_group (info->connection, info->actions_id);
138 : 1 : g_dbus_connection_unexport_menu_model (info->connection, info->menu_id);
139 : :
140 [ + - ]: 1 : g_clear_pointer (&info->object_path, g_free);
141 [ + - ]: 1 : g_clear_object (&info->connection);
142 [ + - ]: 1 : g_clear_object (&info->manager);
143 : 1 : g_free (info);
144 : 1 : }
145 : :
146 : : static void
147 : 1 : valent_device_manager_export_device (ValentDeviceManager *self,
148 : : ValentDevice *device)
149 : : {
150 : 1 : GDBusObjectManager *manager = G_DBUS_OBJECT_MANAGER (self->dbus);
151 : 1 : const char *base_path = NULL;
152 : 2 : g_autofree char *escaped_id = NULL;
153 : 1 : g_autofree char *object_path = NULL;
154 : 1 : g_autoptr (GDBusObjectSkeleton) object = NULL;
155 [ + - ]: 1 : g_autoptr (GDBusInterfaceSkeleton) iface = NULL;
156 : 1 : DeviceExport *info;
157 : 1 : GActionGroup *action_group;
158 : 1 : GMenuModel *menu_model;
159 : :
160 : 1 : VALENT_ENTRY;
161 : :
162 [ - + ]: 1 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
163 [ + - ]: 1 : g_assert (VALENT_IS_DEVICE (device));
164 : :
165 [ + - ]: 1 : if (g_hash_table_contains (self->exports, device))
166 : 1 : VALENT_EXIT;
167 : :
168 : 1 : base_path = g_dbus_object_manager_get_object_path (manager);
169 : 1 : escaped_id = g_dbus_escape_object_path (valent_device_get_id (device));
170 : 1 : object_path = g_strconcat (base_path, "/Device/", escaped_id, NULL);
171 [ + - ]: 1 : g_assert (g_variant_is_object_path (object_path));
172 : :
173 : 1 : info = g_new0 (DeviceExport, 1);
174 : 1 : info->manager = g_object_ref (self->dbus);
175 : 1 : info->connection = g_dbus_object_manager_server_get_connection (self->dbus);
176 : 1 : info->object_path = g_steal_pointer (&object_path);
177 : :
178 : 1 : object = g_dbus_object_skeleton_new (info->object_path);
179 : 1 : iface = valent_device_impl_new (device);
180 : 1 : g_dbus_object_skeleton_add_interface (object, iface);
181 : :
182 : 1 : action_group = G_ACTION_GROUP (device);
183 : 2 : info->actions_id = g_dbus_connection_export_action_group (info->connection,
184 : 1 : info->object_path,
185 : : action_group,
186 : : NULL);
187 : :
188 : 1 : menu_model = valent_device_get_menu (device);
189 : 2 : info->menu_id = g_dbus_connection_export_menu_model (info->connection,
190 : 1 : info->object_path,
191 : : menu_model,
192 : : NULL);
193 : :
194 : 1 : g_dbus_object_manager_server_export (self->dbus, object);
195 : 1 : g_hash_table_insert (self->exports, device, g_steal_pointer (&info));
196 : :
197 [ - + + - ]: 2 : VALENT_EXIT;
198 : : }
199 : :
200 : : /*
201 : : * Channel Services
202 : : */
203 : : static gboolean
204 : 3 : valent_device_manager_check_device (ValentDeviceManager *self,
205 : : ValentDevice *device)
206 : : {
207 : 3 : unsigned int n_unpaired = 0;
208 : :
209 [ - + ]: 3 : if ((valent_device_get_state (device) & VALENT_DEVICE_STATE_PAIRED) != 0)
210 : : return TRUE;
211 : :
212 [ + + ]: 6 : for (unsigned int i = 0, len = self->devices->len; i < len; i++)
213 : : {
214 : 3 : ValentDevice *check = g_ptr_array_index (self->devices, i);
215 : :
216 [ + - ]: 3 : if ((valent_device_get_state (check) & VALENT_DEVICE_STATE_PAIRED) == 0)
217 : 3 : n_unpaired++;
218 : : }
219 : :
220 [ + - ]: 3 : if (n_unpaired >= DEVICE_UNPAIRED_MAX)
221 : : {
222 : 0 : g_warning ("%s(): too many unpaired devices", G_STRFUNC);
223 : 0 : return FALSE;
224 : : }
225 : :
226 : : return TRUE;
227 : : }
228 : :
229 : : static void
230 : 3 : on_channel (ValentChannelService *service,
231 : : ValentChannel *channel,
232 : : ValentDeviceManager *self)
233 : : {
234 : 3 : JsonNode *identity;
235 : 3 : ValentDevice *device;
236 : :
237 : 3 : VALENT_ENTRY;
238 : :
239 [ - + ]: 3 : g_assert (VALENT_IS_CHANNEL_SERVICE (service));
240 [ + - ]: 3 : g_assert (VALENT_IS_CHANNEL (channel));
241 [ + - ]: 3 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
242 : :
243 [ - + ]: 3 : if ((identity = valent_channel_get_peer_identity (channel)) == NULL)
244 : : {
245 : 0 : g_warning ("%s(): %s missing peer identity",
246 : : G_STRFUNC,
247 : : G_OBJECT_TYPE_NAME (channel));
248 : 0 : VALENT_EXIT;
249 : : }
250 : :
251 [ + - ]: 3 : if ((device = valent_device_manager_ensure_device (self, identity)) == NULL)
252 : 3 : VALENT_EXIT;
253 : :
254 [ - + ]: 3 : if (!valent_device_manager_check_device (self, device))
255 : : {
256 : 0 : valent_object_destroy (VALENT_OBJECT (channel));
257 : 0 : VALENT_EXIT;
258 : : }
259 : :
260 : 3 : valent_device_add_channel (device, channel);
261 : :
262 : 3 : VALENT_EXIT;
263 : : }
264 : :
265 : : static void
266 : 0 : g_async_initable_init_async_cb (GAsyncInitable *initable,
267 : : GAsyncResult *result,
268 : : gpointer user_data)
269 : : {
270 : 0 : g_autoptr (GError) error = NULL;
271 : :
272 : 0 : VALENT_ENTRY;
273 : :
274 [ # # ]: 0 : g_assert (VALENT_IS_CHANNEL_SERVICE (initable));
275 : :
276 [ # # # # ]: 0 : if (!g_async_initable_init_finish (initable, result, &error) &&
277 : 0 : !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
278 : 0 : g_warning ("%s: %s", G_OBJECT_TYPE_NAME (initable), error->message);
279 : :
280 [ # # ]: 0 : VALENT_EXIT;
281 : : }
282 : :
283 : : static inline void
284 : 14 : valent_device_manager_enable_plugin (ValentDeviceManager *self,
285 : : ValentPlugin *plugin)
286 : : {
287 [ - + ]: 14 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
288 [ + - ]: 14 : g_assert (plugin != NULL);
289 : :
290 : 14 : plugin->extension = peas_engine_create_extension (valent_get_plugin_engine (),
291 : : plugin->info,
292 : : VALENT_TYPE_CHANNEL_SERVICE,
293 : : "source", self,
294 : : "context", plugin->context,
295 : : NULL);
296 [ + - ]: 14 : g_return_if_fail (G_IS_OBJECT (plugin->extension));
297 : :
298 : 14 : g_signal_connect_object (plugin->extension,
299 : : "channel",
300 : : G_CALLBACK (on_channel),
301 : : self,
302 : : G_CONNECT_DEFAULT);
303 : :
304 [ + - + - : 14 : if (G_IS_ASYNC_INITABLE (plugin->extension))
+ - - + ]
305 : : {
306 : 0 : plugin->cancellable = g_cancellable_new ();
307 : 0 : g_async_initable_init_async (G_ASYNC_INITABLE (plugin->extension),
308 : : G_PRIORITY_DEFAULT,
309 : : plugin->cancellable,
310 : : (GAsyncReadyCallback)g_async_initable_init_async_cb,
311 : : NULL);
312 : : }
313 [ + - + - : 14 : else if (G_IS_INITABLE (plugin->extension))
+ - - + ]
314 : : {
315 : 0 : GInitable *initable = G_INITABLE (plugin->extension);
316 : 0 : g_autoptr (GError) error = NULL;
317 : :
318 : 0 : plugin->cancellable = g_cancellable_new ();
319 [ # # # # ]: 0 : if (!g_initable_init (initable, plugin->cancellable, &error) &&
320 : 0 : !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
321 : : {
322 : 0 : g_warning ("%s: %s", G_OBJECT_TYPE_NAME (initable), error->message);
323 : : }
324 : : }
325 : : }
326 : :
327 : : static inline void
328 : 1 : valent_device_manager_disable_plugin (ValentDeviceManager *self,
329 : : ValentPlugin *plugin)
330 : : {
331 [ - + ]: 1 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
332 [ + - ]: 1 : g_assert (plugin != NULL);
333 [ + - ]: 1 : g_return_if_fail (G_IS_OBJECT (plugin->extension));
334 : :
335 : 1 : g_cancellable_cancel (plugin->cancellable);
336 [ - + ]: 1 : g_clear_object (&plugin->cancellable);
337 : :
338 [ + - ]: 1 : if (plugin->extension != NULL)
339 : : {
340 : 1 : valent_object_destroy (VALENT_OBJECT (plugin->extension));
341 [ + - ]: 1 : g_clear_object (&plugin->extension);
342 : : }
343 : : }
344 : :
345 : : static void
346 : 2 : on_plugin_enabled_changed (ValentPlugin *plugin)
347 : : {
348 [ - + ]: 2 : g_assert (plugin != NULL);
349 [ + - ]: 2 : g_assert (VALENT_IS_DEVICE_MANAGER (plugin->parent));
350 : :
351 [ + + ]: 2 : if (valent_plugin_get_enabled (plugin))
352 : 1 : valent_device_manager_enable_plugin (plugin->parent, plugin);
353 : : else
354 : 1 : valent_device_manager_disable_plugin (plugin->parent, plugin);
355 : 2 : }
356 : :
357 : : static void
358 : 85 : on_load_service (PeasEngine *engine,
359 : : PeasPluginInfo *info,
360 : : ValentDeviceManager *self)
361 : : {
362 : 85 : ValentPlugin *plugin;
363 : :
364 [ - + ]: 85 : g_assert (PEAS_IS_ENGINE (engine));
365 [ + - ]: 85 : g_assert (info != NULL);
366 [ + - ]: 85 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
367 : :
368 : : /* We're only interested in one GType */
369 [ + + ]: 85 : if (!peas_engine_provides_extension (engine, info, VALENT_TYPE_CHANNEL_SERVICE))
370 : : return;
371 : :
372 : 13 : VALENT_NOTE ("%s: %s",
373 : : g_type_name (VALENT_TYPE_CHANNEL_SERVICE),
374 : : peas_plugin_info_get_module_name (info));
375 : :
376 : 13 : plugin = valent_plugin_new (self, self->plugins_context, info,
377 : : G_CALLBACK (on_plugin_enabled_changed));
378 : 13 : g_hash_table_insert (self->plugins, info, plugin);
379 : :
380 [ + - ]: 13 : if (valent_plugin_get_enabled (plugin))
381 : 13 : valent_device_manager_enable_plugin (self, plugin);
382 : : }
383 : :
384 : : static void
385 : 2 : on_unload_service (PeasEngine *engine,
386 : : PeasPluginInfo *info,
387 : : ValentDeviceManager *self)
388 : : {
389 [ - + ]: 2 : g_assert (PEAS_IS_ENGINE (engine));
390 [ + - ]: 2 : g_assert (info != NULL);
391 [ + - ]: 2 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
392 : :
393 : : /* We're only interested in one GType */
394 [ + - ]: 2 : if (!peas_engine_provides_extension (engine, info, VALENT_TYPE_CHANNEL_SERVICE))
395 : : return;
396 : :
397 : 2 : VALENT_NOTE ("%s: %s",
398 : : g_type_name (VALENT_TYPE_CHANNEL_SERVICE),
399 : : peas_plugin_info_get_module_name (info));
400 : :
401 : 2 : g_hash_table_remove (self->plugins, info);
402 : : }
403 : :
404 : : /*
405 : : * Device Management
406 : : */
407 : : static void
408 : 6 : on_device_state (ValentDevice *device,
409 : : GParamSpec *pspec,
410 : : ValentDeviceManager *self)
411 : : {
412 : 6 : ValentDeviceState state = valent_device_get_state (device);
413 : :
414 [ + + ]: 6 : if ((state & VALENT_DEVICE_STATE_CONNECTED) != 0 &&
415 [ - + ]: 4 : (state & VALENT_DEVICE_STATE_PAIRED) != 0)
416 : : {
417 : 6 : g_autoptr (ValentChannel) channel = NULL;
418 : 0 : JsonNode *identity = NULL;
419 : :
420 : 0 : channel = g_list_model_get_item (valent_device_get_channels (device), 0);
421 : 0 : identity = valent_channel_get_peer_identity (channel);
422 [ # # ]: 0 : json_object_set_object_member (json_node_get_object (self->state),
423 : 0 : valent_device_get_id (device),
424 : : json_node_dup_object (identity));
425 : : }
426 [ + - ]: 2 : else if ((state & VALENT_DEVICE_STATE_PAIRED) == 0)
427 : : {
428 : 6 : json_object_remove_member (json_node_get_object (self->state),
429 : 6 : valent_device_get_id (device));
430 : :
431 [ + + ]: 6 : if ((state & VALENT_DEVICE_STATE_CONNECTED) == 0)
432 : 2 : valent_device_manager_remove_device (self, device);
433 : : }
434 : 6 : }
435 : :
436 : : static void
437 : 6 : valent_device_manager_add_device (ValentDeviceManager *self,
438 : : ValentDevice *device)
439 : : {
440 : 6 : unsigned int position = 0;
441 : :
442 : 6 : VALENT_ENTRY;
443 : :
444 [ - + ]: 6 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
445 [ + - ]: 6 : g_assert (VALENT_IS_DEVICE (device));
446 : :
447 [ - + ]: 6 : if (g_ptr_array_find (self->devices, device, NULL))
448 : : {
449 : 0 : g_warning ("Device \"%s\" already managed by \"%s\"",
450 : : valent_device_get_name (device),
451 : : G_OBJECT_TYPE_NAME (self));
452 : 0 : VALENT_EXIT;
453 : : }
454 : :
455 : 6 : g_signal_connect_object (device,
456 : : "notify::state",
457 : : G_CALLBACK (on_device_state),
458 : : self,
459 : : G_CONNECT_DEFAULT);
460 : :
461 : 6 : position = self->devices->len;
462 : 6 : g_ptr_array_add (self->devices, g_object_ref (device));
463 : 6 : g_list_model_items_changed (G_LIST_MODEL (self), position, 0, 1);
464 : :
465 [ - + ]: 6 : if (self->dbus != NULL)
466 : 0 : valent_device_manager_export_device (self, device);
467 : :
468 : 6 : VALENT_EXIT;
469 : : }
470 : :
471 : : static inline gboolean
472 : 0 : find_device_by_id (gconstpointer a,
473 : : gconstpointer b)
474 : : {
475 : 0 : return g_str_equal (valent_device_get_id ((ValentDevice *)a), (const char *)b);
476 : : }
477 : :
478 : : static ValentDevice *
479 : 6 : valent_device_manager_ensure_device (ValentDeviceManager *self,
480 : : JsonNode *identity)
481 : : {
482 : 6 : const char *device_id;
483 : 6 : unsigned int position = 0;
484 : :
485 [ - + ]: 6 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
486 [ + - ]: 6 : g_assert (VALENT_IS_PACKET (identity));
487 : :
488 [ - + ]: 6 : if (!valent_packet_get_string (identity, "deviceId", &device_id))
489 : : {
490 : 0 : g_critical ("%s(): expected \"deviceId\" field holding a string",
491 : : G_STRFUNC);
492 : 0 : return NULL;
493 : : }
494 : :
495 [ - + ]: 6 : if (!valent_device_validate_id (device_id))
496 : : {
497 : 0 : g_critical ("%s(): invalid device ID \"%s\"", G_STRFUNC, device_id);
498 : 0 : return NULL;
499 : : }
500 : :
501 [ + - ]: 6 : if (!g_ptr_array_find_with_equal_func (self->devices,
502 : : device_id,
503 : : find_device_by_id,
504 : : &position))
505 : : {
506 : 12 : g_autoptr (ValentContext) context = NULL;
507 [ + - ]: 6 : g_autoptr (ValentDevice) device = NULL;
508 : :
509 : 6 : context = valent_context_new (self->context, "device", device_id);
510 : 6 : device = valent_device_new_full (identity, context);
511 : :
512 : 6 : valent_device_manager_add_device (self, device);
513 [ + - ]: 6 : position = (self->devices->len - 1);
514 : : }
515 : :
516 : 6 : return g_ptr_array_index (self->devices, position);
517 : : }
518 : :
519 : : static void
520 : 2 : valent_device_manager_remove_device (ValentDeviceManager *self,
521 : : ValentDevice *device)
522 : : {
523 : 2 : unsigned int position = 0;
524 : :
525 : 2 : VALENT_ENTRY;
526 : :
527 [ - + ]: 2 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
528 [ + - ]: 2 : g_assert (VALENT_IS_DEVICE (device));
529 : :
530 [ - + ]: 2 : if (!g_ptr_array_find (self->devices, device, &position))
531 : : {
532 : 0 : g_warning ("Device \"%s\" not managed by \"%s\"",
533 : : valent_device_get_name (device),
534 : : G_OBJECT_TYPE_NAME (self));
535 : 0 : VALENT_EXIT;
536 : : }
537 : :
538 : 2 : g_signal_handlers_disconnect_by_data (device, self);
539 : :
540 : 2 : g_hash_table_remove (self->exports, device);
541 : 2 : g_ptr_array_remove_index (self->devices, position);
542 : 2 : g_list_model_items_changed (G_LIST_MODEL (self), position, 1, 0);
543 : :
544 : 2 : VALENT_EXIT;
545 : : }
546 : :
547 : : static void
548 : 11 : valent_device_manager_load_state (ValentDeviceManager *self)
549 : : {
550 : 11 : JsonObjectIter iter;
551 : 11 : const char *device_id;
552 : 11 : JsonNode *identity;
553 : 22 : g_autoptr (GFile) path = NULL;
554 [ + - ]: 11 : g_autoptr (GTlsCertificate) certificate = NULL;
555 [ + - ]: 11 : g_autoptr (GError) error = NULL;
556 : :
557 [ - + ]: 11 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
558 : :
559 : : /* Ensure we're wiping old certificates with invalid device IDs. In the
560 : : * unlikely event of an error, the channel service will re-generate it.
561 : : *
562 : : * TODO: remove this after a period of time
563 : : */
564 : 11 : path = valent_context_get_config_file (self->context, ".");
565 : 11 : certificate = valent_certificate_new_sync (g_file_peek_path (path), NULL);
566 [ + - ]: 11 : if (certificate != NULL)
567 : : {
568 : 11 : device_id = valent_certificate_get_common_name (certificate);
569 [ - + ]: 11 : if (!valent_device_validate_id (device_id))
570 : : {
571 : 11 : g_autoptr (GFile) cert_file = NULL;
572 [ # # ]: 0 : g_autoptr (GFile) pkey_file = NULL;
573 : :
574 : 0 : cert_file = valent_context_get_config_file (self->context,
575 : : "certificate.pem");
576 : 0 : g_file_delete (cert_file, NULL, NULL);
577 : 0 : pkey_file = valent_context_get_config_file (self->context,
578 : : "private.pem");
579 [ # # ]: 0 : g_file_delete (pkey_file, NULL, NULL);
580 : : }
581 : : }
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 [ + + ]: 15 : for (unsigned int i = 0, len = self->devices->len; i < len; i++)
616 : : {
617 : 4 : ValentDevice *device = g_ptr_array_index (self->devices, i);
618 : 4 : ValentDeviceState state = valent_device_get_state (device);
619 : :
620 [ + - ]: 4 : if ((state & VALENT_DEVICE_STATE_PAIRED) == 0)
621 : : {
622 : 4 : json_object_remove_member (json_node_get_object (self->state),
623 : 4 : valent_device_get_id (device));
624 : : }
625 : : }
626 : :
627 : 11 : generator = g_object_new (JSON_TYPE_GENERATOR,
628 : : "pretty", TRUE,
629 : : "root", self->state,
630 : : NULL);
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 (self->settings, self);
732 [ + - ]: 11 : g_clear_object (&self->settings);
733 : :
734 : 11 : g_signal_handlers_disconnect_by_data (valent_get_plugin_engine (), self);
735 : 11 : g_hash_table_remove_all (self->plugins);
736 : 11 : valent_device_manager_save_state (self);
737 : :
738 : 11 : n_devices = self->devices->len;
739 [ + + ]: 15 : for (unsigned int i = 0; i < n_devices; i++)
740 : : {
741 : 4 : ValentDevice *device = g_ptr_array_index (self->devices, i);
742 : 4 : g_signal_handlers_disconnect_by_data (device, self);
743 : : }
744 : :
745 : 11 : g_ptr_array_remove_range (self->devices, 0, n_devices);
746 : 11 : g_list_model_items_changed (G_LIST_MODEL (self), 0, n_devices, 0);
747 : :
748 [ + - ]: 11 : if (self == default_manager)
749 : : {
750 : 11 : GApplication *application = g_application_get_default ();
751 : :
752 [ + + ]: 11 : if (application != NULL)
753 : : {
754 [ + + ]: 6 : for (size_t i = 0; i < G_N_ELEMENTS (app_actions); i++)
755 : : {
756 : 3 : g_action_map_remove_action (G_ACTION_MAP (application),
757 : : app_actions[i].name);
758 : : }
759 : : }
760 : : }
761 : : }
762 : :
763 : : static void
764 : 11 : valent_device_manager_startup (ValentApplicationPlugin *plugin)
765 : : {
766 : 11 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (plugin);
767 : 11 : PeasEngine *engine = NULL;
768 : 11 : unsigned int n_plugins = 0;
769 : :
770 [ - + ]: 11 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
771 [ + - ]: 11 : g_return_if_fail (self->cancellable == NULL);
772 : :
773 : 11 : self->cancellable = g_cancellable_new ();
774 : 11 : valent_device_manager_load_state (self);
775 : :
776 : 11 : self->settings = g_settings_new ("ca.andyholmes.Valent");
777 : 11 : g_signal_connect_object (self->settings,
778 : : "changed::device-addresses",
779 : : G_CALLBACK (valent_device_manager_refresh),
780 : : self,
781 : : G_CONNECT_SWAPPED);
782 : :
783 : 11 : engine = valent_get_plugin_engine ();
784 : 11 : g_signal_connect_object (engine,
785 : : "load-plugin",
786 : : G_CALLBACK (on_load_service),
787 : : self,
788 : : G_CONNECT_AFTER);
789 : 11 : g_signal_connect_object (engine,
790 : : "unload-plugin",
791 : : G_CALLBACK (on_unload_service),
792 : : self,
793 : : G_CONNECT_DEFAULT);
794 : :
795 : 11 : n_plugins = g_list_model_get_n_items (G_LIST_MODEL (engine));
796 [ + + ]: 94 : for (unsigned int i = 0; i < n_plugins; i++)
797 : : {
798 : 83 : g_autoptr (PeasPluginInfo) info = NULL;
799 : :
800 : 83 : info = g_list_model_get_item (G_LIST_MODEL (engine), i);
801 [ + - ]: 83 : if (peas_plugin_info_is_loaded (info))
802 : 83 : on_load_service (engine, info, self);
803 : : }
804 : :
805 [ + - ]: 11 : if (self == default_manager)
806 : : {
807 : 11 : GApplication *application = g_application_get_default ();
808 : :
809 [ + + ]: 11 : if (application != NULL)
810 : : {
811 : 3 : g_action_map_add_action_entries (G_ACTION_MAP (application),
812 : : app_actions,
813 : : G_N_ELEMENTS (app_actions),
814 : : application);
815 : : }
816 : : }
817 : : }
818 : :
819 : : /*
820 : : * GObject
821 : : */
822 : : static void
823 : 13 : valent_device_manager_constructed (GObject *object)
824 : : {
825 : 13 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (object);
826 : :
827 : 13 : G_OBJECT_CLASS (valent_device_manager_parent_class)->constructed (object);
828 : :
829 [ + - ]: 13 : if (default_manager == NULL)
830 : : {
831 : 13 : default_manager = self;
832 : 13 : g_object_add_weak_pointer (G_OBJECT (default_manager),
833 : : (gpointer)&default_manager);
834 : : }
835 : 13 : }
836 : :
837 : : static void
838 : 13 : valent_device_manager_finalize (GObject *object)
839 : : {
840 : 13 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (object);
841 : :
842 [ + - ]: 13 : g_clear_pointer (&self->exports, g_hash_table_unref);
843 [ + - ]: 13 : g_clear_pointer (&self->plugins, g_hash_table_unref);
844 [ + - ]: 13 : g_clear_pointer (&self->plugins_context, g_object_unref);
845 [ + - ]: 13 : g_clear_pointer (&self->devices, g_ptr_array_unref);
846 [ + + ]: 13 : g_clear_pointer (&self->state, json_node_unref);
847 [ + - ]: 13 : g_clear_object (&self->context);
848 : :
849 : 13 : G_OBJECT_CLASS (valent_device_manager_parent_class)->finalize (object);
850 : 13 : }
851 : :
852 : : static void
853 : 51 : valent_device_manager_class_init (ValentDeviceManagerClass *klass)
854 : : {
855 : 51 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
856 : 51 : ValentApplicationPluginClass *plugin_class = VALENT_APPLICATION_PLUGIN_CLASS (klass);
857 : :
858 : 51 : object_class->constructed = valent_device_manager_constructed;
859 : 51 : object_class->finalize = valent_device_manager_finalize;
860 : :
861 : 51 : plugin_class->dbus_register = valent_device_manager_dbus_register;
862 : 51 : plugin_class->dbus_unregister = valent_device_manager_dbus_unregister;
863 : 51 : plugin_class->shutdown = valent_device_manager_shutdown;
864 : 51 : plugin_class->startup = valent_device_manager_startup;
865 : : }
866 : :
867 : : static void
868 : 13 : valent_device_manager_init (ValentDeviceManager *self)
869 : : {
870 : 13 : self->context = valent_context_new (NULL, NULL, NULL);
871 : 13 : self->devices = g_ptr_array_new_with_free_func (_valent_object_deref);
872 : 13 : self->exports = g_hash_table_new_full (NULL, NULL, NULL, device_export_free);
873 : 13 : self->plugins = g_hash_table_new_full (NULL, NULL, NULL, valent_plugin_free);
874 : 13 : self->plugins_context = valent_context_new (self->context, "network", NULL);
875 : 13 : }
876 : :
877 : : /**
878 : : * valent_device_manager_get_default:
879 : : *
880 : : * Get the default [class@Valent.DeviceManager].
881 : : *
882 : : * Returns: (transfer none) (not nullable): a `ValentDeviceManager`
883 : : *
884 : : * Since: 1.0
885 : : */
886 : : ValentDeviceManager *
887 : 15 : valent_device_manager_get_default (void)
888 : : {
889 [ + + ]: 15 : if (default_manager == NULL)
890 : 10 : return g_object_new (VALENT_TYPE_DEVICE_MANAGER, NULL);
891 : :
892 : : return default_manager;
893 : : }
894 : :
895 : : /**
896 : : * valent_device_manager_refresh:
897 : : * @manager: a `ValentDeviceManager`
898 : : *
899 : : * Refresh the available devices.
900 : : *
901 : : * This method calls [method@Valent.ChannelService.identify] for each enabled
902 : : * service, requesting it to announce itself on its respective network.
903 : : *
904 : : * Since: 1.0
905 : : */
906 : : void
907 : 4 : valent_device_manager_refresh (ValentDeviceManager *manager)
908 : : {
909 : 4 : GHashTableIter iter;
910 : 4 : ValentPlugin *plugin;
911 : 8 : g_auto (GStrv) addresses = NULL;
912 : :
913 : 4 : VALENT_ENTRY;
914 : :
915 [ - + ]: 4 : g_return_if_fail (VALENT_IS_DEVICE_MANAGER (manager));
916 : :
917 [ + + ]: 4 : if (manager->cancellable == NULL)
918 : 3 : VALENT_EXIT;
919 : :
920 : 3 : addresses = g_settings_get_strv (manager->settings, "device-addresses");
921 : :
922 : 3 : g_hash_table_iter_init (&iter, manager->plugins);
923 [ + + ]: 6 : while (g_hash_table_iter_next (&iter, NULL, (void **)&plugin))
924 : : {
925 [ - + ]: 3 : if (plugin->extension == NULL)
926 : 0 : continue;
927 : :
928 : 3 : valent_channel_service_identify (VALENT_CHANNEL_SERVICE (plugin->extension),
929 : : NULL);
930 : :
931 [ - + ]: 3 : for (size_t i = 0; addresses[i] != NULL; i++)
932 : : {
933 : 0 : valent_channel_service_identify (VALENT_CHANNEL_SERVICE (plugin->extension),
934 : : addresses[i]);
935 : : }
936 : : }
937 : :
938 [ + - ]: 3 : VALENT_EXIT;
939 : : }
940 : :
|