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 [ + + + - ]: 444 : 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 : 52 : g_list_model_iface_init (GListModelInterface *iface)
113 : : {
114 : 52 : iface->get_item = valent_device_manager_get_item;
115 : 52 : iface->get_item_type = valent_device_manager_get_item_type;
116 : 52 : iface->get_n_items = valent_device_manager_get_n_items;
117 : 52 : }
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 : : "source", 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 : 85 : on_load_service (PeasEngine *engine,
389 : : PeasPluginInfo *info,
390 : : ValentDeviceManager *self)
391 : : {
392 : 85 : ValentPlugin *plugin;
393 : :
394 [ - + ]: 85 : g_assert (PEAS_IS_ENGINE (engine));
395 [ + - ]: 85 : g_assert (info != NULL);
396 [ + - ]: 85 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
397 : :
398 : : /* We're only interested in one GType */
399 [ + + ]: 85 : 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 (ValentContext) context = NULL;
537 [ + - ]: 6 : g_autoptr (ValentDevice) device = NULL;
538 : :
539 : 6 : context = valent_context_new (self->context, "device", device_id);
540 : 6 : device = valent_device_new_full (identity, context);
541 : :
542 : 6 : valent_device_manager_add_device (self, device);
543 [ + - ]: 6 : position = (self->devices->len - 1);
544 : : }
545 : :
546 : 6 : return g_ptr_array_index (self->devices, position);
547 : : }
548 : :
549 : : static void
550 : 4 : valent_device_manager_remove_device (ValentDeviceManager *self,
551 : : ValentDevice *device)
552 : : {
553 : 4 : unsigned int position = 0;
554 : :
555 : 4 : VALENT_ENTRY;
556 : :
557 [ - + ]: 4 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
558 [ + - ]: 4 : g_assert (VALENT_IS_DEVICE (device));
559 : :
560 [ - + ]: 4 : if (!g_ptr_array_find (self->devices, device, &position))
561 : : {
562 : 0 : g_warning ("Device \"%s\" not managed by \"%s\"",
563 : : valent_device_get_name (device),
564 : : G_OBJECT_TYPE_NAME (self));
565 : 0 : VALENT_EXIT;
566 : : }
567 : :
568 : 4 : g_signal_handlers_disconnect_by_data (device, self);
569 : :
570 : 4 : g_hash_table_remove (self->exports, device);
571 : 4 : g_ptr_array_remove_index (self->devices, position);
572 : 4 : g_list_model_items_changed (G_LIST_MODEL (self), position, 1, 0);
573 : :
574 : 4 : VALENT_EXIT;
575 : : }
576 : :
577 : : static void
578 : 11 : valent_device_manager_load_state (ValentDeviceManager *self)
579 : : {
580 : 11 : JsonObjectIter iter;
581 : 11 : const char *device_id;
582 : 11 : JsonNode *identity;
583 : 22 : g_autoptr (GFile) path = NULL;
584 [ + - ]: 11 : g_autoptr (GTlsCertificate) certificate = NULL;
585 [ + - ]: 11 : g_autoptr (GError) error = NULL;
586 : :
587 [ - + ]: 11 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
588 : :
589 : : /* Ensure we're wiping old certificates with invalid device IDs. In the
590 : : * unlikely event of an error, the channel service will re-generate it.
591 : : *
592 : : * TODO: remove this after a period of time
593 : : */
594 : 11 : path = valent_context_get_config_file (self->context, ".");
595 : 11 : certificate = valent_certificate_new_sync (g_file_peek_path (path), NULL);
596 [ + - ]: 11 : if (certificate != NULL)
597 : : {
598 : 11 : device_id = valent_certificate_get_common_name (certificate);
599 [ - + ]: 11 : if (!valent_device_validate_id (device_id))
600 : : {
601 : 11 : g_autoptr (GFile) cert_file = NULL;
602 [ # # ]: 0 : g_autoptr (GFile) pkey_file = NULL;
603 : :
604 : 0 : cert_file = valent_context_get_config_file (self->context,
605 : : "certificate.pem");
606 : 0 : g_file_delete (cert_file, NULL, NULL);
607 : 0 : pkey_file = valent_context_get_config_file (self->context,
608 : : "private.pem");
609 [ # # ]: 0 : g_file_delete (pkey_file, NULL, NULL);
610 : : }
611 : : }
612 : :
613 [ + - ]: 11 : if (self->state == NULL)
614 : : {
615 : 22 : g_autoptr (JsonParser) parser = NULL;
616 [ + - ]: 11 : g_autoptr (GFile) file = NULL;
617 : :
618 : 11 : parser = json_parser_new ();
619 : 11 : file = valent_context_get_cache_file (self->context, "devices.json");
620 [ + + ]: 11 : if (json_parser_load_from_file (parser, g_file_peek_path (file), NULL))
621 : 3 : self->state = json_parser_steal_root (parser);
622 : :
623 [ + + - + ]: 11 : if (self->state == NULL || !JSON_NODE_HOLDS_OBJECT (self->state))
624 : : {
625 [ - + ]: 8 : g_clear_pointer (&self->state, json_node_unref);
626 : 8 : self->state = json_node_new (JSON_NODE_OBJECT);
627 : 8 : json_node_take_object (self->state, json_object_new ());
628 : : }
629 : : }
630 : :
631 : 11 : json_object_iter_init (&iter, json_node_get_object (self->state));
632 [ + + ]: 14 : while (json_object_iter_next (&iter, &device_id, &identity))
633 : 3 : valent_device_manager_ensure_device (self, identity);
634 : 11 : }
635 : :
636 : : static void
637 : 11 : valent_device_manager_save_state (ValentDeviceManager *self)
638 : : {
639 : 22 : g_autoptr (JsonGenerator) generator = NULL;
640 [ + - ]: 11 : g_autoptr (GFile) file = NULL;
641 [ + - ]: 11 : g_autoptr (GError) error = NULL;
642 : :
643 [ - + ]: 11 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
644 : :
645 [ + + ]: 13 : for (unsigned int i = 0, len = self->devices->len; i < len; i++)
646 : : {
647 : 2 : ValentDevice *device = g_ptr_array_index (self->devices, i);
648 : 2 : ValentDeviceState state = valent_device_get_state (device);
649 : :
650 [ + - ]: 2 : if ((state & VALENT_DEVICE_STATE_PAIRED) == 0)
651 : : {
652 : 2 : json_object_remove_member (json_node_get_object (self->state),
653 : 2 : valent_device_get_id (device));
654 : : }
655 : : }
656 : :
657 : 11 : generator = g_object_new (JSON_TYPE_GENERATOR,
658 : : "pretty", TRUE,
659 : : "root", self->state,
660 : : NULL);
661 : :
662 : 11 : file = valent_context_get_cache_file (self->context, "devices.json");
663 [ - + ]: 11 : if (!json_generator_to_file (generator, g_file_peek_path (file), &error))
664 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
665 : 11 : }
666 : :
667 : : /*
668 : : * GActions
669 : : */
670 : : static void
671 : 0 : device_action (GSimpleAction *action,
672 : : GVariant *parameter,
673 : : gpointer user_data)
674 : : {
675 : 0 : ValentDeviceManager *manager = valent_device_manager_get_default ();
676 : 0 : const char *device_id;
677 : 0 : const char *name;
678 : 0 : g_autoptr (GVariantIter) targetv = NULL;
679 [ # # ]: 0 : g_autoptr (GVariant) target = NULL;
680 : :
681 [ # # ]: 0 : g_assert (VALENT_IS_DEVICE_MANAGER (manager));
682 : :
683 : : /* (<Valent.Device:id>, <Gio.Action:name>, [<GLib.Variant>]) */
684 : 0 : g_variant_get (parameter, "(&s&sav)", &device_id, &name, &targetv);
685 : 0 : g_variant_iter_next (targetv, "v", &target);
686 : :
687 [ # # ]: 0 : for (unsigned int i = 0, len = manager->devices->len; i < len; i++)
688 : : {
689 : 0 : ValentDevice *device = g_ptr_array_index (manager->devices, i);
690 : :
691 [ # # ]: 0 : if (g_strcmp0 (device_id, valent_device_get_id (device)) == 0)
692 : : {
693 : 0 : g_action_group_activate_action (G_ACTION_GROUP (device), name, target);
694 : 0 : break;
695 : : }
696 : : }
697 : 0 : }
698 : :
699 : : static const GActionEntry app_actions[] = {
700 : : { "device", device_action, "(ssav)", NULL, NULL },
701 : : };
702 : :
703 : : /*
704 : : * ValentApplicationPlugin
705 : : */
706 : : static gboolean
707 : 4 : valent_device_manager_dbus_register (ValentApplicationPlugin *plugin,
708 : : GDBusConnection *connection,
709 : : const char *object_path,
710 : : GError **error)
711 : : {
712 : 4 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (plugin);
713 : :
714 [ - + ]: 4 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
715 [ + - + - : 4 : g_assert (G_IS_DBUS_CONNECTION (connection));
- + - - ]
716 [ + - ]: 4 : g_assert (g_variant_is_object_path (object_path));
717 [ + - ]: 4 : g_return_val_if_fail (self->dbus == NULL, TRUE);
718 : :
719 : 4 : self->dbus = g_dbus_object_manager_server_new (object_path);
720 : 4 : g_dbus_object_manager_server_set_connection (self->dbus, connection);
721 : :
722 [ + + ]: 5 : for (unsigned int i = 0, len = self->devices->len; i < len; i++)
723 : : {
724 : 1 : ValentDevice *device = g_ptr_array_index (self->devices, i);
725 : :
726 : 1 : valent_device_manager_export_device (self, device);
727 : : }
728 : :
729 : : return TRUE;
730 : : }
731 : :
732 : : static void
733 : 4 : valent_device_manager_dbus_unregister (ValentApplicationPlugin *plugin,
734 : : GDBusConnection *connection,
735 : : const char *object_path)
736 : : {
737 : 4 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (plugin);
738 : :
739 [ - + ]: 4 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
740 [ + - + - : 4 : g_assert (G_IS_DBUS_CONNECTION (connection));
- + - - ]
741 [ + - ]: 4 : g_assert (g_variant_is_object_path (object_path));
742 [ + - + - : 4 : g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER (self->dbus));
+ - + - ]
743 : :
744 : 4 : g_hash_table_remove_all (self->exports);
745 : 4 : g_dbus_object_manager_server_set_connection (self->dbus, NULL);
746 [ + - ]: 4 : g_clear_object (&self->dbus);
747 : : }
748 : :
749 : : static void
750 : 11 : valent_device_manager_shutdown (ValentApplicationPlugin *plugin)
751 : : {
752 : 11 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (plugin);
753 : 11 : unsigned int n_devices = 0;
754 : :
755 [ - + ]: 11 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
756 [ + - + - : 11 : g_return_if_fail (G_IS_CANCELLABLE (self->cancellable));
- + - - ]
757 : :
758 : 11 : g_cancellable_cancel (self->cancellable);
759 [ + - ]: 11 : g_clear_object (&self->cancellable);
760 : :
761 : 11 : g_signal_handlers_disconnect_by_data (self->settings, self);
762 [ + - ]: 11 : g_clear_object (&self->settings);
763 : :
764 : 11 : g_signal_handlers_disconnect_by_data (valent_get_plugin_engine (), self);
765 : 11 : g_hash_table_remove_all (self->plugins);
766 : 11 : valent_device_manager_save_state (self);
767 : :
768 : 11 : n_devices = self->devices->len;
769 [ + + ]: 13 : for (unsigned int i = 0; i < n_devices; i++)
770 : : {
771 : 2 : ValentDevice *device = g_ptr_array_index (self->devices, i);
772 : 2 : g_signal_handlers_disconnect_by_data (device, self);
773 : : }
774 : :
775 : 11 : g_ptr_array_remove_range (self->devices, 0, n_devices);
776 : 11 : g_list_model_items_changed (G_LIST_MODEL (self), 0, n_devices, 0);
777 : :
778 [ + - ]: 11 : if (self == default_manager)
779 : : {
780 : 11 : GApplication *application = g_application_get_default ();
781 : :
782 [ + + ]: 11 : if (application != NULL)
783 : : {
784 [ + + ]: 6 : for (size_t i = 0; i < G_N_ELEMENTS (app_actions); i++)
785 : : {
786 : 3 : g_action_map_remove_action (G_ACTION_MAP (application),
787 : : app_actions[i].name);
788 : : }
789 : : }
790 : : }
791 : : }
792 : :
793 : : static void
794 : 11 : valent_device_manager_startup (ValentApplicationPlugin *plugin)
795 : : {
796 : 11 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (plugin);
797 : 11 : PeasEngine *engine = NULL;
798 : 11 : unsigned int n_plugins = 0;
799 : :
800 [ - + ]: 11 : g_assert (VALENT_IS_DEVICE_MANAGER (self));
801 [ + - ]: 11 : g_return_if_fail (self->cancellable == NULL);
802 : :
803 : 11 : self->cancellable = g_cancellable_new ();
804 : 11 : valent_device_manager_load_state (self);
805 : :
806 : 11 : self->settings = g_settings_new ("ca.andyholmes.Valent");
807 : 11 : g_signal_connect_object (self->settings,
808 : : "changed::device-addresses",
809 : : G_CALLBACK (valent_device_manager_refresh),
810 : : self,
811 : : G_CONNECT_SWAPPED);
812 : :
813 : 11 : engine = valent_get_plugin_engine ();
814 : 11 : g_signal_connect_object (engine,
815 : : "load-plugin",
816 : : G_CALLBACK (on_load_service),
817 : : self,
818 : : G_CONNECT_AFTER);
819 : 11 : g_signal_connect_object (engine,
820 : : "unload-plugin",
821 : : G_CALLBACK (on_unload_service),
822 : : self,
823 : : G_CONNECT_DEFAULT);
824 : :
825 : 11 : n_plugins = g_list_model_get_n_items (G_LIST_MODEL (engine));
826 [ + + ]: 94 : for (unsigned int i = 0; i < n_plugins; i++)
827 : : {
828 : 83 : g_autoptr (PeasPluginInfo) info = NULL;
829 : :
830 : 83 : info = g_list_model_get_item (G_LIST_MODEL (engine), i);
831 [ + - ]: 83 : if (peas_plugin_info_is_loaded (info))
832 : 83 : on_load_service (engine, info, self);
833 : : }
834 : :
835 [ + - ]: 11 : if (self == default_manager)
836 : : {
837 : 11 : GApplication *application = g_application_get_default ();
838 : :
839 [ + + ]: 11 : if (application != NULL)
840 : : {
841 : 3 : g_action_map_add_action_entries (G_ACTION_MAP (application),
842 : : app_actions,
843 : : G_N_ELEMENTS (app_actions),
844 : : application);
845 : : }
846 : : }
847 : : }
848 : :
849 : : /*
850 : : * GObject
851 : : */
852 : : static void
853 : 13 : valent_device_manager_constructed (GObject *object)
854 : : {
855 : 13 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (object);
856 : :
857 : 13 : G_OBJECT_CLASS (valent_device_manager_parent_class)->constructed (object);
858 : :
859 [ + - ]: 13 : if (default_manager == NULL)
860 : : {
861 : 13 : default_manager = self;
862 : 13 : g_object_add_weak_pointer (G_OBJECT (default_manager),
863 : : (gpointer)&default_manager);
864 : : }
865 : 13 : }
866 : :
867 : : static void
868 : 13 : valent_device_manager_finalize (GObject *object)
869 : : {
870 : 13 : ValentDeviceManager *self = VALENT_DEVICE_MANAGER (object);
871 : :
872 [ + - ]: 13 : g_clear_pointer (&self->exports, g_hash_table_unref);
873 [ + - ]: 13 : g_clear_pointer (&self->plugins, g_hash_table_unref);
874 [ + - ]: 13 : g_clear_pointer (&self->plugins_context, g_object_unref);
875 [ + - ]: 13 : g_clear_pointer (&self->devices, g_ptr_array_unref);
876 [ + + ]: 13 : g_clear_pointer (&self->state, json_node_unref);
877 [ + - ]: 13 : g_clear_object (&self->context);
878 : :
879 : 13 : G_OBJECT_CLASS (valent_device_manager_parent_class)->finalize (object);
880 : 13 : }
881 : :
882 : : static void
883 : 52 : valent_device_manager_class_init (ValentDeviceManagerClass *klass)
884 : : {
885 : 52 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
886 : 52 : ValentApplicationPluginClass *plugin_class = VALENT_APPLICATION_PLUGIN_CLASS (klass);
887 : :
888 : 52 : object_class->constructed = valent_device_manager_constructed;
889 : 52 : object_class->finalize = valent_device_manager_finalize;
890 : :
891 : 52 : plugin_class->dbus_register = valent_device_manager_dbus_register;
892 : 52 : plugin_class->dbus_unregister = valent_device_manager_dbus_unregister;
893 : 52 : plugin_class->shutdown = valent_device_manager_shutdown;
894 : 52 : plugin_class->startup = valent_device_manager_startup;
895 : : }
896 : :
897 : : static void
898 : 13 : valent_device_manager_init (ValentDeviceManager *self)
899 : : {
900 : 13 : self->context = valent_context_new (NULL, NULL, NULL);
901 : 13 : self->devices = g_ptr_array_new_with_free_func (_valent_object_deref);
902 : 13 : self->exports = g_hash_table_new_full (NULL, NULL, NULL, device_export_free);
903 : 13 : self->plugins = g_hash_table_new_full (NULL, NULL, NULL, valent_plugin_free);
904 : 13 : self->plugins_context = valent_context_new (self->context, "network", NULL);
905 : 13 : }
906 : :
907 : : /**
908 : : * valent_device_manager_get_default:
909 : : *
910 : : * Get the default [class@Valent.DeviceManager].
911 : : *
912 : : * Returns: (transfer none) (not nullable): a `ValentDeviceManager`
913 : : *
914 : : * Since: 1.0
915 : : */
916 : : ValentDeviceManager *
917 : 15 : valent_device_manager_get_default (void)
918 : : {
919 [ + + ]: 15 : if (default_manager == NULL)
920 : 10 : return g_object_new (VALENT_TYPE_DEVICE_MANAGER, NULL);
921 : :
922 : : return default_manager;
923 : : }
924 : :
925 : : /**
926 : : * valent_device_manager_refresh:
927 : : * @manager: a `ValentDeviceManager`
928 : : *
929 : : * Refresh the available devices.
930 : : *
931 : : * This method calls [method@Valent.ChannelService.identify] for each enabled
932 : : * service, requesting it to announce itself on its respective network.
933 : : *
934 : : * Since: 1.0
935 : : */
936 : : void
937 : 4 : valent_device_manager_refresh (ValentDeviceManager *manager)
938 : : {
939 : 4 : GHashTableIter iter;
940 : 4 : ValentPlugin *plugin;
941 : 8 : g_auto (GStrv) addresses = NULL;
942 : :
943 : 4 : VALENT_ENTRY;
944 : :
945 [ - + ]: 4 : g_return_if_fail (VALENT_IS_DEVICE_MANAGER (manager));
946 : :
947 [ + + ]: 4 : if (manager->cancellable == NULL)
948 : 3 : VALENT_EXIT;
949 : :
950 : 3 : addresses = g_settings_get_strv (manager->settings, "device-addresses");
951 : :
952 : 3 : g_hash_table_iter_init (&iter, manager->plugins);
953 [ + + ]: 6 : while (g_hash_table_iter_next (&iter, NULL, (void **)&plugin))
954 : : {
955 [ - + ]: 3 : if (plugin->extension == NULL)
956 : 0 : continue;
957 : :
958 : 3 : valent_channel_service_identify (VALENT_CHANNEL_SERVICE (plugin->extension),
959 : : NULL);
960 : :
961 [ - + ]: 3 : for (size_t i = 0; addresses[i] != NULL; i++)
962 : : {
963 : 0 : valent_channel_service_identify (VALENT_CHANNEL_SERVICE (plugin->extension),
964 : : addresses[i]);
965 : : }
966 : : }
967 : :
968 [ + - ]: 3 : VALENT_EXIT;
969 : : }
970 : :
|