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