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 : 3 : VALENT_EXIT;
256 : :
257 : 3 : valent_device_set_channel (device, channel);
258 : :
259 : 3 : VALENT_EXIT;
260 : : }
261 : :
262 : : static void
263 : 0 : g_async_initable_init_async_cb (GAsyncInitable *initable,
264 : : GAsyncResult *result,
265 : : gpointer user_data)
266 : : {
267 : 0 : g_autoptr (GError) error = NULL;
268 : :
269 : 0 : VALENT_ENTRY;
270 : :
271 [ # # ]: 0 : g_assert (VALENT_IS_CHANNEL_SERVICE (initable));
272 : :
273 [ # # # # ]: 0 : if (!g_async_initable_init_finish (initable, result, &error) &&
274 : 0 : !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
275 : 0 : g_warning ("%s: %s", G_OBJECT_TYPE_NAME (initable), error->message);
276 : :
277 [ # # ]: 0 : VALENT_EXIT;
278 : : }
279 : :
280 : : static inline void
281 : 14 : valent_device_manager_enable_plugin (ValentDeviceManager *self,
282 : : ValentPlugin *plugin)
283 : : {
284 [ - + ]: 14 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
285 [ + - ]: 14 : g_assert (plugin != NULL);
286 : :
287 : 14 : plugin->extension = peas_engine_create_extension (valent_get_plugin_engine (),
288 : : plugin->info,
289 : : VALENT_TYPE_CHANNEL_SERVICE,
290 : : "source", self,
291 : : "context", plugin->context,
292 : : NULL);
293 [ + - ]: 14 : g_return_if_fail (G_IS_OBJECT (plugin->extension));
294 : :
295 : 14 : g_signal_connect_object (plugin->extension,
296 : : "channel",
297 : : G_CALLBACK (on_channel),
298 : : self,
299 : : G_CONNECT_DEFAULT);
300 : :
301 [ + - + - : 14 : if (G_IS_ASYNC_INITABLE (plugin->extension))
+ - - + ]
302 : : {
303 : 0 : plugin->cancellable = g_cancellable_new ();
304 : 0 : g_async_initable_init_async (G_ASYNC_INITABLE (plugin->extension),
305 : : G_PRIORITY_DEFAULT,
306 : : plugin->cancellable,
307 : : (GAsyncReadyCallback)g_async_initable_init_async_cb,
308 : : NULL);
309 : : }
310 [ + - + - : 14 : else if (G_IS_INITABLE (plugin->extension))
+ - - + ]
311 : : {
312 : 0 : GInitable *initable = G_INITABLE (plugin->extension);
313 : 0 : g_autoptr (GError) error = NULL;
314 : :
315 : 0 : plugin->cancellable = g_cancellable_new ();
316 [ # # # # ]: 0 : if (!g_initable_init (initable, plugin->cancellable, &error) &&
317 : 0 : !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
318 : : {
319 : 0 : g_warning ("%s: %s", G_OBJECT_TYPE_NAME (initable), error->message);
320 : : }
321 : : }
322 : : }
323 : :
324 : : static inline void
325 : 1 : valent_device_manager_disable_plugin (ValentDeviceManager *self,
326 : : ValentPlugin *plugin)
327 : : {
328 [ - + ]: 1 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
329 [ + - ]: 1 : g_assert (plugin != NULL);
330 [ + - ]: 1 : g_return_if_fail (G_IS_OBJECT (plugin->extension));
331 : :
332 : 1 : g_cancellable_cancel (plugin->cancellable);
333 [ - + ]: 1 : g_clear_object (&plugin->cancellable);
334 : :
335 [ + - ]: 1 : if (plugin->extension != NULL)
336 : : {
337 : 1 : valent_object_destroy (VALENT_OBJECT (plugin->extension));
338 [ + - ]: 1 : g_clear_object (&plugin->extension);
339 : : }
340 : : }
341 : :
342 : : static void
343 : 2 : on_plugin_enabled_changed (ValentPlugin *plugin)
344 : : {
345 [ - + ]: 2 : g_assert (plugin != NULL);
346 [ + - ]: 2 : g_assert (VALENT_IS_DEVICE_MANAGER (plugin->parent));
347 : :
348 [ + + ]: 2 : if (valent_plugin_get_enabled (plugin))
349 : 1 : valent_device_manager_enable_plugin (plugin->parent, plugin);
350 : : else
351 : 1 : valent_device_manager_disable_plugin (plugin->parent, plugin);
352 : 2 : }
353 : :
354 : : static void
355 : 85 : on_load_service (PeasEngine *engine,
356 : : PeasPluginInfo *info,
357 : : ValentDeviceManager *self)
358 : : {
359 : 85 : ValentPlugin *plugin;
360 : :
361 [ - + ]: 85 : g_assert (PEAS_IS_ENGINE (engine));
362 [ + - ]: 85 : g_assert (info != NULL);
363 [ + - ]: 85 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
364 : :
365 : : /* We're only interested in one GType */
366 [ + + ]: 85 : if (!peas_engine_provides_extension (engine, info, VALENT_TYPE_CHANNEL_SERVICE))
367 : : return;
368 : :
369 : 13 : VALENT_NOTE ("%s: %s",
370 : : g_type_name (VALENT_TYPE_CHANNEL_SERVICE),
371 : : peas_plugin_info_get_module_name (info));
372 : :
373 : 13 : plugin = valent_plugin_new (self, self->plugins_context, info,
374 : : G_CALLBACK (on_plugin_enabled_changed));
375 : 13 : g_hash_table_insert (self->plugins, info, plugin);
376 : :
377 [ + - ]: 13 : if (valent_plugin_get_enabled (plugin))
378 : 13 : valent_device_manager_enable_plugin (self, plugin);
379 : : }
380 : :
381 : : static void
382 : 2 : on_unload_service (PeasEngine *engine,
383 : : PeasPluginInfo *info,
384 : : ValentDeviceManager *self)
385 : : {
386 [ - + ]: 2 : g_assert (PEAS_IS_ENGINE (engine));
387 [ + - ]: 2 : g_assert (info != NULL);
388 [ + - ]: 2 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
389 : :
390 : : /* We're only interested in one GType */
391 [ + - ]: 2 : if (!peas_engine_provides_extension (engine, info, VALENT_TYPE_CHANNEL_SERVICE))
392 : : return;
393 : :
394 : 2 : VALENT_NOTE ("%s: %s",
395 : : g_type_name (VALENT_TYPE_CHANNEL_SERVICE),
396 : : peas_plugin_info_get_module_name (info));
397 : :
398 : 2 : g_hash_table_remove (self->plugins, info);
399 : : }
400 : :
401 : : /*
402 : : * Device Management
403 : : */
404 : : static void
405 : 6 : on_device_state (ValentDevice *device,
406 : : GParamSpec *pspec,
407 : : ValentDeviceManager *self)
408 : : {
409 : 6 : ValentDeviceState state = valent_device_get_state (device);
410 : :
411 [ + + ]: 6 : if ((state & VALENT_DEVICE_STATE_CONNECTED) != 0 &&
412 [ - + ]: 4 : (state & VALENT_DEVICE_STATE_PAIRED) != 0)
413 : : {
414 : 0 : g_autoptr (ValentChannel) channel = NULL;
415 : 0 : JsonNode *identity = NULL;
416 : :
417 [ # # ]: 0 : if ((channel = valent_device_ref_channel (device)) == NULL)
418 : : return;
419 : :
420 : 0 : identity = valent_channel_get_peer_identity (channel);
421 : 0 : json_object_set_object_member (json_node_get_object (self->state),
422 : 0 : valent_device_get_id (device),
423 : : json_node_dup_object (identity));
424 : : }
425 [ + - ]: 2 : else if ((state & VALENT_DEVICE_STATE_PAIRED) == 0)
426 : : {
427 : 6 : json_object_remove_member (json_node_get_object (self->state),
428 : 6 : valent_device_get_id (device));
429 : :
430 [ + + ]: 6 : if ((state & VALENT_DEVICE_STATE_CONNECTED) == 0)
431 : 2 : valent_device_manager_remove_device (self, device);
432 : : }
433 : : }
434 : :
435 : : static void
436 : 6 : valent_device_manager_add_device (ValentDeviceManager *self,
437 : : ValentDevice *device)
438 : : {
439 : 6 : unsigned int position = 0;
440 : :
441 : 6 : VALENT_ENTRY;
442 : :
443 [ - + ]: 6 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
444 [ + - ]: 6 : g_assert (VALENT_IS_DEVICE (device));
445 : :
446 [ - + ]: 6 : if (g_ptr_array_find (self->devices, device, NULL))
447 : : {
448 : 0 : g_warning ("Device \"%s\" already managed by \"%s\"",
449 : : valent_device_get_name (device),
450 : : G_OBJECT_TYPE_NAME (self));
451 : 0 : VALENT_EXIT;
452 : : }
453 : :
454 : 6 : g_signal_connect_object (device,
455 : : "notify::state",
456 : : G_CALLBACK (on_device_state),
457 : : self,
458 : : G_CONNECT_DEFAULT);
459 : :
460 : 6 : position = self->devices->len;
461 : 6 : g_ptr_array_add (self->devices, g_object_ref (device));
462 : 6 : g_list_model_items_changed (G_LIST_MODEL (self), position, 0, 1);
463 : :
464 [ - + ]: 6 : if (self->dbus != NULL)
465 : 0 : valent_device_manager_export_device (self, device);
466 : :
467 : 6 : VALENT_EXIT;
468 : : }
469 : :
470 : : static inline gboolean
471 : 0 : find_device_by_id (gconstpointer a,
472 : : gconstpointer b)
473 : : {
474 : 0 : return g_str_equal (valent_device_get_id ((ValentDevice *)a), (const char *)b);
475 : : }
476 : :
477 : : static ValentDevice *
478 : 6 : valent_device_manager_ensure_device (ValentDeviceManager *self,
479 : : JsonNode *identity)
480 : : {
481 : 6 : const char *device_id;
482 : 6 : unsigned int position = 0;
483 : :
484 [ - + ]: 6 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
485 [ + - ]: 6 : g_assert (VALENT_IS_PACKET (identity));
486 : :
487 [ - + ]: 6 : if (!valent_packet_get_string (identity, "deviceId", &device_id))
488 : : {
489 : 0 : g_critical ("%s(): expected \"deviceId\" field holding a string",
490 : : G_STRFUNC);
491 : 0 : return NULL;
492 : : }
493 : :
494 [ - + ]: 6 : if (!valent_device_validate_id (device_id))
495 : : {
496 : 0 : g_critical ("%s(): invalid device ID \"%s\"", G_STRFUNC, device_id);
497 : 0 : return NULL;
498 : : }
499 : :
500 [ + - ]: 6 : if (!g_ptr_array_find_with_equal_func (self->devices,
501 : : device_id,
502 : : find_device_by_id,
503 : : &position))
504 : : {
505 : 12 : g_autoptr (ValentContext) context = NULL;
506 [ + - ]: 6 : g_autoptr (ValentDevice) device = NULL;
507 : :
508 : 6 : context = valent_context_new (self->context, "device", device_id);
509 : 6 : device = valent_device_new_full (identity, context);
510 : :
511 : 6 : valent_device_manager_add_device (self, device);
512 [ + - ]: 6 : position = (self->devices->len - 1);
513 : : }
514 : :
515 : 6 : return g_ptr_array_index (self->devices, position);
516 : : }
517 : :
518 : : static void
519 : 2 : valent_device_manager_remove_device (ValentDeviceManager *self,
520 : : ValentDevice *device)
521 : : {
522 : 2 : unsigned int position = 0;
523 : :
524 : 2 : VALENT_ENTRY;
525 : :
526 [ - + ]: 2 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
527 [ + - ]: 2 : g_assert (VALENT_IS_DEVICE (device));
528 : :
529 [ - + ]: 2 : if (!g_ptr_array_find (self->devices, device, &position))
530 : : {
531 : 0 : g_warning ("Device \"%s\" not managed by \"%s\"",
532 : : valent_device_get_name (device),
533 : : G_OBJECT_TYPE_NAME (self));
534 : 0 : VALENT_EXIT;
535 : : }
536 : :
537 : 2 : g_signal_handlers_disconnect_by_data (device, self);
538 : :
539 : 2 : g_hash_table_remove (self->exports, device);
540 : 2 : g_ptr_array_remove_index (self->devices, position);
541 : 2 : g_list_model_items_changed (G_LIST_MODEL (self), position, 1, 0);
542 : :
543 : 2 : VALENT_EXIT;
544 : : }
545 : :
546 : : static void
547 : 11 : valent_device_manager_load_state (ValentDeviceManager *self)
548 : : {
549 : 11 : JsonObjectIter iter;
550 : 11 : const char *device_id;
551 : 11 : JsonNode *identity;
552 : 22 : g_autoptr (GFile) path = NULL;
553 [ + - ]: 11 : g_autoptr (GTlsCertificate) certificate = NULL;
554 [ + - ]: 11 : g_autoptr (GError) error = NULL;
555 : :
556 [ - + ]: 11 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
557 : :
558 : : /* Ensure we're wiping old certificates with invalid device IDs. In the
559 : : * unlikely event of an error, the channel service will re-generate it.
560 : : *
561 : : * TODO: remove this after a period of time
562 : : */
563 : 11 : path = valent_context_get_config_file (self->context, ".");
564 : 11 : certificate = valent_certificate_new_sync (g_file_peek_path (path), NULL);
565 [ + - ]: 11 : if (certificate != NULL)
566 : : {
567 : 11 : device_id = valent_certificate_get_common_name (certificate);
568 [ - + ]: 11 : if (!valent_device_validate_id (device_id))
569 : : {
570 : 11 : g_autoptr (GFile) cert_file = NULL;
571 [ # # ]: 0 : g_autoptr (GFile) pkey_file = NULL;
572 : :
573 : 0 : cert_file = valent_context_get_config_file (self->context,
574 : : "certificate.pem");
575 : 0 : g_file_delete (cert_file, NULL, NULL);
576 : 0 : pkey_file = valent_context_get_config_file (self->context,
577 : : "private.pem");
578 [ # # ]: 0 : g_file_delete (pkey_file, NULL, NULL);
579 : : }
580 : : }
581 : :
582 [ + - ]: 11 : if (self->state == NULL)
583 : : {
584 : 22 : g_autoptr (JsonParser) parser = NULL;
585 [ + - ]: 11 : g_autoptr (GFile) file = NULL;
586 : :
587 : 11 : parser = json_parser_new ();
588 : 11 : file = valent_context_get_cache_file (self->context, "devices.json");
589 [ + + ]: 11 : if (json_parser_load_from_file (parser, g_file_peek_path (file), NULL))
590 : 3 : self->state = json_parser_steal_root (parser);
591 : :
592 [ + + - + ]: 11 : if (self->state == NULL || !JSON_NODE_HOLDS_OBJECT (self->state))
593 : : {
594 [ - + ]: 8 : g_clear_pointer (&self->state, json_node_unref);
595 : 8 : self->state = json_node_new (JSON_NODE_OBJECT);
596 : 8 : json_node_take_object (self->state, json_object_new ());
597 : : }
598 : : }
599 : :
600 : 11 : json_object_iter_init (&iter, json_node_get_object (self->state));
601 [ + + ]: 14 : while (json_object_iter_next (&iter, &device_id, &identity))
602 : 3 : valent_device_manager_ensure_device (self, identity);
603 : 11 : }
604 : :
605 : : static void
606 : 11 : valent_device_manager_save_state (ValentDeviceManager *self)
607 : : {
608 : 22 : g_autoptr (JsonGenerator) generator = NULL;
609 [ + - ]: 11 : g_autoptr (GFile) file = NULL;
610 [ + - ]: 11 : g_autoptr (GError) error = NULL;
611 : :
612 [ - + ]: 11 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
613 : :
614 [ + + ]: 15 : for (unsigned int i = 0, len = self->devices->len; i < len; i++)
615 : : {
616 : 4 : ValentDevice *device = g_ptr_array_index (self->devices, i);
617 : 4 : ValentDeviceState state = valent_device_get_state (device);
618 : :
619 [ + - ]: 4 : if ((state & VALENT_DEVICE_STATE_PAIRED) == 0)
620 : : {
621 : 4 : json_object_remove_member (json_node_get_object (self->state),
622 : 4 : valent_device_get_id (device));
623 : : }
624 : : }
625 : :
626 : 11 : generator = g_object_new (JSON_TYPE_GENERATOR,
627 : : "pretty", TRUE,
628 : : "root", self->state,
629 : : NULL);
630 : :
631 : 11 : file = valent_context_get_cache_file (self->context, "devices.json");
632 [ - + ]: 11 : if (!json_generator_to_file (generator, g_file_peek_path (file), &error))
633 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
634 : 11 : }
635 : :
636 : : /*
637 : : * GActions
638 : : */
639 : : static void
640 : 0 : device_action (GSimpleAction *action,
641 : : GVariant *parameter,
642 : : gpointer user_data)
643 : : {
644 : 0 : ValentDeviceManager *manager = valent_device_manager_get_default ();
645 : 0 : const char *device_id;
646 : 0 : const char *name;
647 : 0 : g_autoptr (GVariantIter) targetv = NULL;
648 [ # # ]: 0 : g_autoptr (GVariant) target = NULL;
649 : :
650 [ # # ]: 0 : g_assert (VALENT_IS_DEVICE_MANAGER (manager));
651 : :
652 : : /* (<Valent.Device:id>, <Gio.Action:name>, [<GLib.Variant>]) */
653 : 0 : g_variant_get (parameter, "(&s&sav)", &device_id, &name, &targetv);
654 : 0 : g_variant_iter_next (targetv, "v", &target);
655 : :
656 [ # # ]: 0 : for (unsigned int i = 0, len = manager->devices->len; i < len; i++)
657 : : {
658 : 0 : ValentDevice *device = g_ptr_array_index (manager->devices, i);
659 : :
660 [ # # ]: 0 : if (g_strcmp0 (device_id, valent_device_get_id (device)) == 0)
661 : : {
662 : 0 : g_action_group_activate_action (G_ACTION_GROUP (device), name, target);
663 : 0 : break;
664 : : }
665 : : }
666 : 0 : }
667 : :
668 : : static const GActionEntry app_actions[] = {
669 : : { "device", device_action, "(ssav)", NULL, NULL },
670 : : };
671 : :
672 : : /*
673 : : * ValentApplicationPlugin
674 : : */
675 : : static gboolean
676 : 4 : valent_device_manager_dbus_register (ValentApplicationPlugin *plugin,
677 : : GDBusConnection *connection,
678 : : const char *object_path,
679 : : GError **error)
680 : : {
681 : 4 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (plugin);
682 : :
683 [ - + ]: 4 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
684 [ + - + - : 4 : g_assert (G_IS_DBUS_CONNECTION (connection));
- + - - ]
685 [ + - ]: 4 : g_assert (g_variant_is_object_path (object_path));
686 [ + - ]: 4 : g_return_val_if_fail (self->dbus == NULL, TRUE);
687 : :
688 : 4 : self->dbus = g_dbus_object_manager_server_new (object_path);
689 : 4 : g_dbus_object_manager_server_set_connection (self->dbus, connection);
690 : :
691 [ + + ]: 5 : for (unsigned int i = 0, len = self->devices->len; i < len; i++)
692 : : {
693 : 1 : ValentDevice *device = g_ptr_array_index (self->devices, i);
694 : :
695 : 1 : valent_device_manager_export_device (self, device);
696 : : }
697 : :
698 : : return TRUE;
699 : : }
700 : :
701 : : static void
702 : 4 : valent_device_manager_dbus_unregister (ValentApplicationPlugin *plugin,
703 : : GDBusConnection *connection,
704 : : const char *object_path)
705 : : {
706 : 4 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (plugin);
707 : :
708 [ - + ]: 4 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
709 [ + - + - : 4 : g_assert (G_IS_DBUS_CONNECTION (connection));
- + - - ]
710 [ + - ]: 4 : g_assert (g_variant_is_object_path (object_path));
711 [ + - + - : 4 : g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER (self->dbus));
+ - + - ]
712 : :
713 : 4 : g_hash_table_remove_all (self->exports);
714 : 4 : g_dbus_object_manager_server_set_connection (self->dbus, NULL);
715 [ + - ]: 4 : g_clear_object (&self->dbus);
716 : : }
717 : :
718 : : static void
719 : 11 : valent_device_manager_shutdown (ValentApplicationPlugin *plugin)
720 : : {
721 : 11 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (plugin);
722 : 11 : unsigned int n_devices = 0;
723 : :
724 [ - + ]: 11 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
725 [ + - + - : 11 : g_return_if_fail (G_IS_CANCELLABLE (self->cancellable));
- + - - ]
726 : :
727 : 11 : g_cancellable_cancel (self->cancellable);
728 [ + - ]: 11 : g_clear_object (&self->cancellable);
729 : :
730 : 11 : g_signal_handlers_disconnect_by_data (self->settings, self);
731 [ + - ]: 11 : g_clear_object (&self->settings);
732 : :
733 : 11 : g_signal_handlers_disconnect_by_data (valent_get_plugin_engine (), self);
734 : 11 : g_hash_table_remove_all (self->plugins);
735 : 11 : valent_device_manager_save_state (self);
736 : :
737 : 11 : n_devices = self->devices->len;
738 [ + + ]: 15 : for (unsigned int i = 0; i < n_devices; i++)
739 : : {
740 : 4 : ValentDevice *device = g_ptr_array_index (self->devices, i);
741 : 4 : g_signal_handlers_disconnect_by_data (device, self);
742 : : }
743 : :
744 : 11 : g_ptr_array_remove_range (self->devices, 0, n_devices);
745 : 11 : g_list_model_items_changed (G_LIST_MODEL (self), 0, n_devices, 0);
746 : :
747 [ + - ]: 11 : if (self == default_manager)
748 : : {
749 : 11 : GApplication *application = g_application_get_default ();
750 : :
751 [ + + ]: 11 : if (application != NULL)
752 : : {
753 [ + + ]: 6 : for (size_t i = 0; i < G_N_ELEMENTS (app_actions); i++)
754 : : {
755 : 3 : g_action_map_remove_action (G_ACTION_MAP (application),
756 : : app_actions[i].name);
757 : : }
758 : : }
759 : : }
760 : : }
761 : :
762 : : static void
763 : 11 : valent_device_manager_startup (ValentApplicationPlugin *plugin)
764 : : {
765 : 11 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (plugin);
766 : 11 : PeasEngine *engine = NULL;
767 : 11 : unsigned int n_plugins = 0;
768 : :
769 [ - + ]: 11 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
770 [ + - ]: 11 : g_return_if_fail (self->cancellable == NULL);
771 : :
772 : 11 : self->cancellable = g_cancellable_new ();
773 : 11 : valent_device_manager_load_state (self);
774 : :
775 : 11 : self->settings = g_settings_new ("ca.andyholmes.Valent");
776 : 11 : g_signal_connect_object (self->settings,
777 : : "changed::device-addresses",
778 : : G_CALLBACK (valent_device_manager_refresh),
779 : : self,
780 : : G_CONNECT_SWAPPED);
781 : :
782 : 11 : engine = valent_get_plugin_engine ();
783 : 11 : g_signal_connect_object (engine,
784 : : "load-plugin",
785 : : G_CALLBACK (on_load_service),
786 : : self,
787 : : G_CONNECT_AFTER);
788 : 11 : g_signal_connect_object (engine,
789 : : "unload-plugin",
790 : : G_CALLBACK (on_unload_service),
791 : : self,
792 : : G_CONNECT_DEFAULT);
793 : :
794 : 11 : n_plugins = g_list_model_get_n_items (G_LIST_MODEL (engine));
795 [ + + ]: 94 : for (unsigned int i = 0; i < n_plugins; i++)
796 : : {
797 : 83 : g_autoptr (PeasPluginInfo) info = NULL;
798 : :
799 : 83 : info = g_list_model_get_item (G_LIST_MODEL (engine), i);
800 [ + - ]: 83 : if (peas_plugin_info_is_loaded (info))
801 : 83 : on_load_service (engine, info, self);
802 : : }
803 : :
804 [ + - ]: 11 : if (self == default_manager)
805 : : {
806 : 11 : GApplication *application = g_application_get_default ();
807 : :
808 [ + + ]: 11 : if (application != NULL)
809 : : {
810 : 3 : g_action_map_add_action_entries (G_ACTION_MAP (application),
811 : : app_actions,
812 : : G_N_ELEMENTS (app_actions),
813 : : application);
814 : : }
815 : : }
816 : : }
817 : :
818 : : /*
819 : : * GObject
820 : : */
821 : : static void
822 : 13 : valent_device_manager_constructed (GObject *object)
823 : : {
824 : 13 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (object);
825 : :
826 : 13 : G_OBJECT_CLASS (valent_device_manager_parent_class)->constructed (object);
827 : :
828 [ + - ]: 13 : if (default_manager == NULL)
829 : : {
830 : 13 : default_manager = self;
831 : 13 : g_object_add_weak_pointer (G_OBJECT (default_manager),
832 : : (gpointer)&default_manager);
833 : : }
834 : 13 : }
835 : :
836 : : static void
837 : 13 : valent_device_manager_finalize (GObject *object)
838 : : {
839 : 13 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (object);
840 : :
841 [ + - ]: 13 : g_clear_pointer (&self->exports, g_hash_table_unref);
842 [ + - ]: 13 : g_clear_pointer (&self->plugins, g_hash_table_unref);
843 [ + - ]: 13 : g_clear_pointer (&self->plugins_context, g_object_unref);
844 [ + - ]: 13 : g_clear_pointer (&self->devices, g_ptr_array_unref);
845 [ + + ]: 13 : g_clear_pointer (&self->state, json_node_unref);
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 : 51 : valent_device_manager_class_init (ValentDeviceManagerClass *klass)
853 : : {
854 : 51 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
855 : 51 : ValentApplicationPluginClass *plugin_class = VALENT_APPLICATION_PLUGIN_CLASS (klass);
856 : :
857 : 51 : object_class->constructed = valent_device_manager_constructed;
858 : 51 : object_class->finalize = valent_device_manager_finalize;
859 : :
860 : 51 : plugin_class->dbus_register = valent_device_manager_dbus_register;
861 : 51 : plugin_class->dbus_unregister = valent_device_manager_dbus_unregister;
862 : 51 : plugin_class->shutdown = valent_device_manager_shutdown;
863 : 51 : 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 (_valent_object_deref);
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 : 15 : valent_device_manager_get_default (void)
887 : : {
888 [ + + ]: 15 : 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 : 8 : g_auto (GStrv) addresses = NULL;
911 : :
912 : 4 : VALENT_ENTRY;
913 : :
914 [ - + ]: 4 : g_return_if_fail (VALENT_IS_DEVICE_MANAGER (manager));
915 : :
916 [ + + ]: 4 : if (manager->cancellable == NULL)
917 : 3 : VALENT_EXIT;
918 : :
919 : 3 : addresses = g_settings_get_strv (manager->settings, "device-addresses");
920 : :
921 : 3 : g_hash_table_iter_init (&iter, manager->plugins);
922 [ + + ]: 6 : while (g_hash_table_iter_next (&iter, NULL, (void **)&plugin))
923 : : {
924 [ - + ]: 3 : if (plugin->extension == NULL)
925 : 0 : continue;
926 : :
927 : 3 : valent_channel_service_identify (VALENT_CHANNEL_SERVICE (plugin->extension),
928 : : NULL);
929 : :
930 [ - + ]: 3 : for (size_t i = 0; addresses[i] != NULL; i++)
931 : : {
932 : 0 : valent_channel_service_identify (VALENT_CHANNEL_SERVICE (plugin->extension),
933 : : addresses[i]);
934 : : }
935 : : }
936 : :
937 [ + - ]: 3 : VALENT_EXIT;
938 : : }
939 : :
|