2 * Copyright (C) 2012 Intel Corporation. All rights reserved.
3 * Copyright (C) 2009 Nokia Corporation.
5 * Author: Ludovic Ferrandis <ludovic.ferrandis@intel.com>
6 * Author: Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
7 * <zeeshan.ali@nokia.com>
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the
21 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
26 * SECTION:gupnp-connman-manager
27 * @short_description: Connman-based implementation of
28 * #GUPnPContextManager.
32 #include "gupnp-connman-manager.h"
33 #include "gupnp-context.h"
34 #include "gupnp-marshal.h"
38 CM_SERVICE_STATE_ACTIVE = 1,
39 CM_SERVICE_STATE_INACTIVE = 2
44 GUPnPConnmanManager *manager;
45 GUPnPContext *context;
47 CMServiceState current;
55 struct _GUPnPConnmanManagerPrivate {
56 GDBusProxy *manager_proxy;
57 GSource *idle_context_creation_src;
58 GHashTable *cm_services;
60 GDBusConnection *system_bus;
63 #define CM_DBUS_CONNMAN_NAME "net.connman"
64 #define CM_DBUS_MANAGER_PATH "/"
65 #define CM_DBUS_MANAGER_INTERFACE "net.connman.Manager"
66 #define CM_DBUS_SERVICE_INTERFACE "net.connman.Service"
68 #define DBUS_SERVICE_DBUS "org.freedesktop.DBus"
69 #define DBUS_PATH_DBUS "/org/freedesktop/DBus"
70 #define DBUS_INTERFACE_DBUS "org.freedesktop.DBus"
72 #define LOOPBACK_IFACE "lo"
74 G_DEFINE_TYPE (GUPnPConnmanManager,
75 gupnp_connman_manager,
76 GUPNP_TYPE_CONTEXT_MANAGER);
79 loopback_context_create (gpointer data)
81 GUPnPConnmanManager *manager;
82 GUPnPContext *context;
86 manager = GUPNP_CONNMAN_MANAGER (data);
87 manager->priv->idle_context_creation_src = NULL;
89 g_object_get (manager, "port", &port, NULL);
91 context = g_initable_new (GUPNP_TYPE_CONTEXT,
94 "interface", LOOPBACK_IFACE,
99 g_warning ("Error creating GUPnP context: %s", error->message);
100 g_error_free (error);
105 g_signal_emit_by_name (manager, "context-available", context);
106 g_object_unref (context);
112 service_context_create (CMService *cm_service)
114 GError *error = NULL;
116 cm_service->context = g_initable_new (GUPNP_TYPE_CONTEXT,
119 "interface", cm_service->iface,
120 "network", cm_service->name,
121 "port", cm_service->port,
125 g_warning ("Error creating GUPnP context: %s", error->message);
126 g_error_free (error);
131 g_signal_emit_by_name (cm_service->manager,
133 cm_service->context);
139 service_context_delete (CMService *cm_service)
141 // For other states we just destroy the context
142 g_signal_emit_by_name (cm_service->manager,
143 "context-unavailable",
144 cm_service->context);
146 g_object_unref (cm_service->context);
147 cm_service->context = NULL;
151 service_context_update (CMService *cm_service, CMServiceState new_state)
153 if (cm_service->current != new_state) {
154 if (new_state == CM_SERVICE_STATE_ACTIVE) {
155 if (service_context_create (cm_service) == FALSE)
156 new_state = CM_SERVICE_STATE_INACTIVE;
158 } else if ((new_state == CM_SERVICE_STATE_INACTIVE) &&
159 (cm_service->context != NULL)) {
160 service_context_delete (cm_service);
163 cm_service->current = new_state;
168 on_service_property_signal (GDBusConnection *connection,
169 const gchar *sender_name,
170 const gchar *object_path,
171 const gchar *interface_name,
172 const gchar *signal_name,
173 GVariant *parameters,
176 CMService *cm_service;
179 const gchar *state_str;
180 CMServiceState new_state;
182 cm_service = (CMService *) user_data;
183 g_variant_get (parameters, "(&sv)", &name, &value);
185 if (g_strcmp0 (name, "Name") == 0) {
186 g_free (cm_service->name);
187 g_variant_get (value, "s", &cm_service->name);
189 if (cm_service->context != NULL)
190 g_object_set (G_OBJECT (cm_service->context),
195 } else if (g_strcmp0 (name, "Ethernet") == 0) {
196 g_free (cm_service->iface);
197 g_variant_lookup (value, "Interface", "s", &cm_service->iface);
199 if (cm_service->context != NULL)
200 g_object_set (G_OBJECT (cm_service->context),
205 } else if (g_strcmp0 (name, "State") == 0) {
206 state_str = g_variant_get_string (value, 0);
208 if ((g_strcmp0 (state_str, "online") == 0) ||
209 (g_strcmp0 (state_str, "ready") == 0))
210 new_state = CM_SERVICE_STATE_ACTIVE;
212 new_state = CM_SERVICE_STATE_INACTIVE;
214 service_context_update (cm_service, new_state);
217 g_variant_unref (value);
221 cm_service_new (GUPnPConnmanManager *manager,
222 GDBusProxy *service_proxy)
224 CMService *cm_service;
226 cm_service = g_slice_new0 (CMService);
228 cm_service->manager = manager;
229 cm_service->proxy = service_proxy;
230 cm_service->current = CM_SERVICE_STATE_INACTIVE;
236 cm_service_free (CMService *cm_service)
238 GDBusConnection *cnx;
240 cnx = g_dbus_proxy_get_connection (cm_service->proxy);
242 if (cm_service->sig_prop_id) {
243 g_dbus_connection_signal_unsubscribe (cnx,
244 cm_service->sig_prop_id);
245 cm_service->sig_prop_id = 0;
248 g_object_unref (cm_service->proxy);
250 if (cm_service->context != NULL) {
251 g_signal_emit_by_name (cm_service->manager,
252 "context-unavailable",
253 cm_service->context);
255 g_object_unref (cm_service->context);
258 g_free (cm_service->iface);
259 g_free (cm_service->name);
260 g_slice_free (CMService, cm_service);
264 cm_service_use (GUPnPConnmanManager *manager,
265 CMService *cm_service)
267 GDBusConnection *connection;
269 connection = g_dbus_proxy_get_connection (cm_service->proxy);
271 cm_service->sig_prop_id = g_dbus_connection_signal_subscribe (
273 CM_DBUS_CONNMAN_NAME,
274 CM_DBUS_SERVICE_INTERFACE,
276 g_dbus_proxy_get_object_path (cm_service->proxy),
278 G_DBUS_SIGNAL_FLAGS_NONE,
279 on_service_property_signal,
283 if (cm_service->current == CM_SERVICE_STATE_ACTIVE)
284 if (service_context_create (cm_service) == FALSE)
285 cm_service->current = CM_SERVICE_STATE_INACTIVE;
288 cm_service_update (CMService *cm_service, GVariant *dict, guint port)
290 CMServiceState new_state;
302 is_name = g_variant_lookup (dict, "Name", "s", &name);
304 eth = g_variant_lookup_value (dict, "Ethernet", G_VARIANT_TYPE_VARDICT);
307 is_iface = g_variant_lookup (eth, "Interface", "s", &iface);
308 g_variant_unref (eth);
311 new_state = CM_SERVICE_STATE_INACTIVE;
313 if (g_variant_lookup (dict, "State", "&s", &state) != FALSE)
314 if ((g_strcmp0 (state, "online") == 0) ||
315 (g_strcmp0 (state, "ready") == 0))
316 new_state = CM_SERVICE_STATE_ACTIVE;
318 if (is_name && (g_strcmp0 (cm_service->name, name) == 0)) {
319 g_free (cm_service->name);
320 cm_service->name = name;
322 if (cm_service->context != NULL)
323 g_object_set (G_OBJECT (cm_service->context),
329 if (is_iface && (g_strcmp0 (cm_service->iface, iface) == 0)) {
330 g_free (cm_service->iface);
331 cm_service->iface = iface;
333 if (cm_service->context != NULL)
334 g_object_set (G_OBJECT (cm_service->context),
340 cm_service->port = port;
341 service_context_update (cm_service, new_state);
345 service_proxy_new_cb (GObject *source_object,
349 GUPnPConnmanManager *manager;
350 GDBusProxy *service_proxy;
351 GError *error = NULL;
352 CMService *cm_service;
355 service_proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
358 g_warning ("Failed to create D-Bus proxy: %s", error->message);
359 g_error_free (error);
364 manager = GUPNP_CONNMAN_MANAGER (user_data);
365 path = g_dbus_proxy_get_object_path (service_proxy);
366 cm_service = g_hash_table_lookup (manager->priv->cm_services, path);
368 if (cm_service == NULL) {
369 g_object_unref (service_proxy);
374 cm_service->proxy = service_proxy;
375 cm_service_use (manager, cm_service);
379 cm_service_add (GUPnPConnmanManager *manager,
384 CMServiceState new_state;
385 CMService *cm_service;
394 g_variant_lookup (dict, "Name", "s", &name);
396 eth = g_variant_lookup_value (dict, "Ethernet", G_VARIANT_TYPE_VARDICT);
399 g_variant_lookup (eth, "Interface", "s", &iface);
400 g_variant_unref (eth);
403 new_state = CM_SERVICE_STATE_INACTIVE;
405 if (g_variant_lookup (dict, "State", "&s", &state) != FALSE)
406 if ((g_strcmp0 (state, "online") == 0) ||
407 (g_strcmp0 (state, "ready") == 0))
408 new_state = CM_SERVICE_STATE_ACTIVE;
410 cm_service = cm_service_new (manager, NULL);
412 cm_service->name = name;
413 cm_service->iface = iface;
414 cm_service->port = port;
415 cm_service->current = new_state;
417 g_hash_table_insert (manager->priv->cm_services,
421 g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
422 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
423 G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
425 CM_DBUS_CONNMAN_NAME,
427 CM_DBUS_SERVICE_INTERFACE,
429 service_proxy_new_cb,
434 services_array_add (GUPnPConnmanManager *manager, GVariant *data)
437 CMService *cm_service;
440 GVariantIter dict_iter;
443 g_object_get (manager, "port", &port, NULL);
444 g_variant_iter_init (&iter, data);
446 while (g_variant_iter_loop (&iter, "(&o@a{sv})", &path, &dict)) {
454 if (g_variant_iter_init (&dict_iter, dict) == 0)
457 cm_service = g_hash_table_lookup (manager->priv->cm_services,
460 if (cm_service == NULL)
461 cm_service_add (manager, dict, path, port);
463 cm_service_update (cm_service, dict, port);
468 services_array_remove (GUPnPConnmanManager *manager, GVariant *data)
470 GUPnPConnmanManagerPrivate *priv;
471 CMService *cm_service;
475 priv = manager->priv;
476 g_variant_iter_init (&iter, data);
478 while (g_variant_iter_next (&iter, "&o", &path)) {
482 cm_service = g_hash_table_lookup (priv->cm_services, path);
484 if (cm_service == NULL)
487 g_hash_table_remove (priv->cm_services, path);
492 on_manager_svc_changed_signal (GDBusConnection *connection,
493 const gchar *sender_name,
494 const gchar *object_path,
495 const gchar *interface_name,
496 const gchar *signal_name,
497 GVariant *parameters,
500 GUPnPConnmanManager *manager;
502 GVariant *remove_array;
504 manager = GUPNP_CONNMAN_MANAGER (user_data);
506 add_array = g_variant_get_child_value (parameters, 0);
507 remove_array = g_variant_get_child_value (parameters, 1);
509 if ((add_array != NULL) && (g_variant_n_children (add_array) > 0))
510 services_array_add (manager, add_array);
512 if ((remove_array != NULL) && (g_variant_n_children (remove_array) > 0))
513 services_array_remove (manager, remove_array);
515 g_variant_unref (add_array);
516 g_variant_unref (remove_array);
520 get_services_cb (GObject *source_object,
524 GUPnPConnmanManager *manager;
526 GVariant *services_array;
527 GError *error = NULL;
529 ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object),
534 g_warning ("Error fetching service list: %s", error->message);
535 g_error_free (error);
541 g_warning ("Failed fetching list of services but no error");
546 if (g_variant_is_container (ret) != TRUE)
548 g_warning ("Wrong format result");
549 g_variant_unref (ret);
554 manager = GUPNP_CONNMAN_MANAGER (user_data);
555 services_array = g_variant_get_child_value (ret, 0);
556 services_array_add (manager, services_array);
558 g_variant_unref (services_array);
559 g_variant_unref (ret);
563 schedule_loopback_context_creation (GUPnPConnmanManager *manager)
565 /* Create contexts in mainloop so that it happens after user has hooked
566 * to the "context-available" signal.
568 manager->priv->idle_context_creation_src = g_idle_source_new ();
570 g_source_attach (manager->priv->idle_context_creation_src,
571 g_main_context_get_thread_default ());
573 g_source_set_callback (manager->priv->idle_context_creation_src,
574 loopback_context_create,
578 g_source_unref (manager->priv->idle_context_creation_src);
582 init_connman_manager (GUPnPConnmanManager *manager)
584 GUPnPConnmanManagerPrivate *priv;
585 GError *error = NULL;
586 GDBusConnection *connection;
588 priv = manager->priv;
590 priv->manager_proxy = g_dbus_proxy_new_for_bus_sync (
592 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
593 G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
595 CM_DBUS_CONNMAN_NAME,
596 CM_DBUS_MANAGER_PATH,
597 CM_DBUS_MANAGER_INTERFACE,
602 g_warning ("Failed to connect to Connman: %s", error->message);
603 g_error_free (error);
608 connection = g_dbus_proxy_get_connection (priv->manager_proxy);
610 if (connection == NULL) {
611 g_warning ("Failed to get DBus Connection");
612 g_object_unref (priv->manager_proxy);
613 priv->manager_proxy = NULL;
618 g_dbus_proxy_call (priv->manager_proxy,
621 G_DBUS_CALL_FLAGS_NONE,
627 priv->sig_change_id = g_dbus_connection_signal_subscribe (
629 CM_DBUS_CONNMAN_NAME,
630 CM_DBUS_MANAGER_INTERFACE,
632 CM_DBUS_MANAGER_PATH,
634 G_DBUS_SIGNAL_FLAGS_NONE,
635 on_manager_svc_changed_signal,
641 gupnp_connman_manager_dispose (GObject *object)
643 GUPnPConnmanManager *manager;
644 GUPnPConnmanManagerPrivate *priv;
645 GObjectClass *object_class;
646 GDBusConnection *cnx;
648 manager = GUPNP_CONNMAN_MANAGER (object);
649 priv = manager->priv;
650 cnx = g_dbus_proxy_get_connection (priv->manager_proxy);
652 if (priv->sig_change_id) {
653 g_dbus_connection_signal_unsubscribe (cnx, priv->sig_change_id);
654 priv->sig_change_id = 0;
657 if (priv->idle_context_creation_src) {
658 g_source_destroy (priv->idle_context_creation_src);
659 priv->idle_context_creation_src = NULL;
662 if (priv->manager_proxy != NULL) {
663 g_object_unref (priv->manager_proxy);
664 priv->manager_proxy = NULL;
667 if (priv->cm_services) {
668 g_hash_table_destroy (priv->cm_services);
669 priv->cm_services = NULL;
672 g_clear_object (&(priv->system_bus));
675 object_class = G_OBJECT_CLASS (gupnp_connman_manager_parent_class);
676 object_class->dispose (object);
680 gupnp_connman_manager_constructed (GObject *object)
682 GUPnPConnmanManager *manager;
683 GObjectClass *object_class;
685 manager = GUPNP_CONNMAN_MANAGER (object);
687 manager->priv->system_bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM,
691 init_connman_manager (manager);
693 schedule_loopback_context_creation (manager);
696 object_class = G_OBJECT_CLASS (gupnp_connman_manager_parent_class);
698 if (object_class->constructed != NULL) {
699 object_class->constructed (object);
704 gupnp_connman_manager_init (GUPnPConnmanManager *manager)
706 manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (
708 GUPNP_TYPE_CONNMAN_MANAGER,
709 GUPnPConnmanManagerPrivate);
711 manager->priv->cm_services = g_hash_table_new_full (
714 (GDestroyNotify) g_free,
715 (GDestroyNotify) cm_service_free);
719 gupnp_connman_manager_class_init (GUPnPConnmanManagerClass *klass)
721 GObjectClass *object_class;
723 object_class = G_OBJECT_CLASS (klass);
725 object_class->constructed = gupnp_connman_manager_constructed;
726 object_class->dispose = gupnp_connman_manager_dispose;
728 g_type_class_add_private (klass, sizeof (GUPnPConnmanManagerPrivate));
732 gupnp_connman_manager_is_available (void)
734 GDBusProxy *dbus_proxy;
735 GVariant *ret_values;
736 GError *error = NULL;
737 gboolean ret = FALSE;
739 dbus_proxy = g_dbus_proxy_new_for_bus_sync (
741 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
751 g_warning ("Failed to connect to Connman: %s", error->message);
752 g_error_free (error);
757 ret_values = g_dbus_proxy_call_sync (
760 g_variant_new ("(s)", CM_DBUS_CONNMAN_NAME),
761 G_DBUS_CALL_FLAGS_NONE,
767 g_warning ("%s.NameHasOwner() failed: %s",
771 g_error_free (error);
774 g_variant_get_child (ret_values, 0, "b", &ret);
775 g_variant_unref (ret_values);
778 g_object_unref (dbus_proxy);