From 1ae5d53354178666c7754457e1ef5e4b69bc29ec Mon Sep 17 00:00:00 2001 From: Emilio Pozuelo Monfort Date: Wed, 29 May 2013 21:39:13 +0200 Subject: bluez: add support for BlueZ 5 At this moment we only support one of BlueZ 4 and 5, which has to be defined at build time. Patch rewritten by Thomas Haller Signed-off-by: Thomas Haller --- src/Makefile.am | 25 +- src/bluez-manager/nm-bluez-common.h | 14 + src/bluez-manager/nm-bluez-device.c | 1 + src/bluez-manager/nm-bluez-device.h | 7 +- src/bluez-manager/nm-bluez-manager.h | 9 +- src/bluez-manager/nm-bluez5-device.c | 658 ++++++++++++++++++++++++++++++++++ src/bluez-manager/nm-bluez5-manager.c | 424 ++++++++++++++++++++++ src/nm-manager.c | 6 +- 8 files changed, 1133 insertions(+), 11 deletions(-) create mode 100644 src/bluez-manager/nm-bluez5-device.c create mode 100644 src/bluez-manager/nm-bluez5-manager.c diff --git a/src/Makefile.am b/src/Makefile.am index b9ca94bb20..9791102cdb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -49,14 +49,6 @@ NetworkManager_LDADD = libNetworkManager.la $(top_builddir)/libgsystem.la $(LIBN noinst_LTLIBRARIES = libNetworkManager.la nm_sources = \ - bluez-manager/nm-bluez-adapter.c \ - bluez-manager/nm-bluez-adapter.h \ - bluez-manager/nm-bluez-common.h \ - bluez-manager/nm-bluez-device.c \ - bluez-manager/nm-bluez-device.h \ - bluez-manager/nm-bluez-manager.c \ - bluez-manager/nm-bluez-manager.h \ - \ config/nm-config.c \ config/nm-config.h \ config/nm-config-device.c \ @@ -266,6 +258,23 @@ nm_sources = \ NetworkManagerUtils.c \ NetworkManagerUtils.h +nm_sources += \ + bluez-manager/nm-bluez-common.h \ + bluez-manager/nm-bluez-device.h \ + bluez-manager/nm-bluez-manager.h + +if WITH_BLUEZ5 +nm_sources += \ + bluez-manager/nm-bluez5-device.c \ + bluez-manager/nm-bluez5-manager.c +else +nm_sources += \ + bluez-manager/nm-bluez-adapter.h \ + bluez-manager/nm-bluez-adapter.c \ + bluez-manager/nm-bluez-device.c \ + bluez-manager/nm-bluez-manager.c +endif + if WITH_MODEM_MANAGER_1 nm_sources += \ modem-manager/nm-modem-broadband.c \ diff --git a/src/bluez-manager/nm-bluez-common.h b/src/bluez-manager/nm-bluez-common.h index 26ef1e31a7..4bf029d9a4 100644 --- a/src/bluez-manager/nm-bluez-common.h +++ b/src/bluez-manager/nm-bluez-common.h @@ -21,17 +21,31 @@ #ifndef NM_BLUEZ_COMMON_H #define NM_BLUEZ_COMMON_H +#include + #define BLUETOOTH_CONNECT_DUN "dun" #define BLUETOOTH_CONNECT_NAP "nap" #define BLUEZ_SERVICE "org.bluez" #define BLUEZ_MANAGER_PATH "/" +#define OBJECT_MANAGER_INTERFACE "org.freedesktop.DBus.ObjectManager" + +#if WITH_BLUEZ5 + +#define BLUEZ_ADAPTER_INTERFACE "org.bluez.Adapter1" +#define BLUEZ_DEVICE_INTERFACE "org.bluez.Device1" +#define BLUEZ_NETWORK_INTERFACE "org.bluez.Network1" + +#else + #define BLUEZ_MANAGER_INTERFACE "org.bluez.Manager" #define BLUEZ_ADAPTER_INTERFACE "org.bluez.Adapter" #define BLUEZ_DEVICE_INTERFACE "org.bluez.Device" #define BLUEZ_SERIAL_INTERFACE "org.bluez.Serial" #define BLUEZ_NETWORK_INTERFACE "org.bluez.Network" +#endif /* WITH_BLUEZ */ + #endif /* NM_BLUEZ_COMMON_H */ diff --git a/src/bluez-manager/nm-bluez-device.c b/src/bluez-manager/nm-bluez-device.c index 18c166ad85..a14075fbbc 100644 --- a/src/bluez-manager/nm-bluez-device.c +++ b/src/bluez-manager/nm-bluez-device.c @@ -297,6 +297,7 @@ bluez_connect_cb (DBusGProxy *proxy, else if (!device || !strlen (device)) { g_simple_async_result_set_error (result, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid argument received"); + g_free (device); } else { g_simple_async_result_set_op_res_gpointer (result, g_strdup (device), diff --git a/src/bluez-manager/nm-bluez-device.h b/src/bluez-manager/nm-bluez-device.h index 54a7e1cd59..d03d4bafdf 100644 --- a/src/bluez-manager/nm-bluez-device.h +++ b/src/bluez-manager/nm-bluez-device.h @@ -25,6 +25,7 @@ #include #include +#include #include "nm-connection.h" #include "nm-connection-provider.h" @@ -58,7 +59,11 @@ typedef struct { GType nm_bluez_device_get_type (void); -NMBluezDevice *nm_bluez_device_new (const char *path, NMConnectionProvider *provider); +NMBluezDevice *nm_bluez_device_new (const char *path +#if ! WITH_BLUEZ5 + , NMConnectionProvider *provider +#endif + ); const char *nm_bluez_device_get_path (NMBluezDevice *self); diff --git a/src/bluez-manager/nm-bluez-manager.h b/src/bluez-manager/nm-bluez-manager.h index 2bfc971eb8..3391f7f5c1 100644 --- a/src/bluez-manager/nm-bluez-manager.h +++ b/src/bluez-manager/nm-bluez-manager.h @@ -25,6 +25,7 @@ #include #include +#include #include "nm-connection-provider.h" G_BEGIN_DECLS @@ -60,7 +61,13 @@ typedef struct { GType nm_bluez_manager_get_type (void); -NMBluezManager *nm_bluez_manager_get (NMConnectionProvider *provider); +NMBluezManager *nm_bluez_manager_get ( +#if WITH_BLUEZ5 + void +#else + NMConnectionProvider *provider +#endif + ); void nm_bluez_manager_query_devices (NMBluezManager *manager); diff --git a/src/bluez-manager/nm-bluez5-device.c b/src/bluez-manager/nm-bluez5-device.c new file mode 100644 index 0000000000..3ccbea719e --- /dev/null +++ b/src/bluez-manager/nm-bluez5-device.c @@ -0,0 +1,658 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2009 - 2012 Red Hat, Inc. + * Copyright (C) 2013 Intel Corporation. + */ + +#include +#include +#include +#include +#include + +#include "NetworkManager.h" +#include "nm-setting-bluetooth.h" + +#include "nm-bluez-device.h" +#include "nm-bluez-common.h" +#include "nm-logging.h" + + +G_DEFINE_TYPE (NMBluezDevice, nm_bluez_device, G_TYPE_OBJECT) + +#define NM_BLUEZ_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_BLUEZ_DEVICE, NMBluezDevicePrivate)) + +typedef struct { + char *path; + GDBusProxy *proxy; + GDBusProxy *adapter; + GDBusConnection *connection; + + gboolean initialized; + gboolean usable; + NMBluetoothCapabilities connection_bt_type; + + char *address; + guint8 bin_address[ETH_ALEN]; + char *name; + guint32 capabilities; + gint rssi; + gboolean connected; + + char *bt_iface; +} NMBluezDevicePrivate; + + +enum { + PROP_0, + PROP_PATH, + PROP_ADDRESS, + PROP_NAME, + PROP_CAPABILITIES, + PROP_RSSI, + PROP_USABLE, + PROP_CONNECTED, + + LAST_PROP +}; + +/* Signals */ +enum { + INITIALIZED, + LAST_SIGNAL +}; +static guint signals[LAST_SIGNAL] = { 0 }; + +/***********************************************************/ + +const char * +nm_bluez_device_get_path (NMBluezDevice *self) +{ + g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), NULL); + + return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->path; +} + +const char * +nm_bluez_device_get_address (NMBluezDevice *self) +{ + g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), NULL); + + return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->address; +} + +gboolean +nm_bluez_device_get_initialized (NMBluezDevice *self) +{ + g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), FALSE); + + return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->initialized; +} + +gboolean +nm_bluez_device_get_usable (NMBluezDevice *self) +{ + g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), FALSE); + + return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->usable; +} + +const char * +nm_bluez_device_get_name (NMBluezDevice *self) +{ + g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), NULL); + + return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->name; +} + +guint32 +nm_bluez_device_get_capabilities (NMBluezDevice *self) +{ + g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), 0); + + return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->capabilities; +} + +gint +nm_bluez_device_get_rssi (NMBluezDevice *self) +{ + g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), 0); + + return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->rssi; +} + +gboolean +nm_bluez_device_get_connected (NMBluezDevice *self) +{ + g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), FALSE); + + return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->connected; +} + +static void +check_emit_usable (NMBluezDevice *self) +{ + NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); + gboolean new_usable; + + new_usable = (priv->initialized && priv->capabilities && priv->name && + priv->address && priv->adapter && priv->connection); + if (new_usable != priv->usable) { + priv->usable = new_usable; + g_object_notify (G_OBJECT (self), NM_BLUEZ_DEVICE_USABLE); + } +} + +/********************************************************************/ + +void +nm_bluez_device_call_disconnect (NMBluezDevice *self) +{ + NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); + + g_return_if_fail (priv->connection); + g_return_if_fail (priv->connection_bt_type == NM_BT_CAPABILITY_NAP); + + g_dbus_connection_call (priv->connection, + BLUEZ_SERVICE, + priv->path, + BLUEZ_NETWORK_INTERFACE, + "Disconnect", + g_variant_new ("()"), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, NULL, NULL); + + priv->connection_bt_type = NM_BT_CAPABILITY_NONE; +} + +static void +bluez_connect_pan_cb (GDBusConnection *connection, + GAsyncResult *res, + gpointer user_data) +{ + GSimpleAsyncResult *result = user_data; + NMBluezDevice *self = NM_BLUEZ_DEVICE (g_async_result_get_source_object (G_ASYNC_RESULT (result))); + NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); + GVariant *variant; + GError *error = NULL; + char *device; + + variant = g_dbus_connection_call_finish (connection, res, &error); + + if (!variant) { + g_simple_async_result_take_error (result, error); + } else { + g_variant_get (variant, "(s)", &device); + + g_simple_async_result_set_op_res_gpointer (result, + g_strdup (device), + g_free); + priv->bt_iface = device; + g_variant_unref (variant); + } + + g_simple_async_result_complete (result); + g_object_unref (result); +} + +void +nm_bluez_device_connect_async (NMBluezDevice *self, + NMBluetoothCapabilities connection_bt_type, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *simple; + NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); + + g_return_if_fail (connection_bt_type == NM_BT_CAPABILITY_NAP); + + simple = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + nm_bluez_device_connect_async); + + /* For PAN we call Connect() on org.bluez.Network1 */ + g_dbus_connection_call (priv->connection, + BLUEZ_SERVICE, + priv->path, + BLUEZ_NETWORK_INTERFACE, + "Connect", + g_variant_new ("(s)", BLUETOOTH_CONNECT_NAP), + NULL, + G_DBUS_CALL_FLAGS_NONE, + 20000, + NULL, + (GAsyncReadyCallback) bluez_connect_pan_cb, + simple); + + priv->connection_bt_type = connection_bt_type; +} + +const char * +nm_bluez_device_connect_finish (NMBluezDevice *self, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + const char *device; + + g_return_val_if_fail (g_simple_async_result_is_valid (result, + G_OBJECT (self), + nm_bluez_device_connect_async), + NULL); + + simple = (GSimpleAsyncResult *) result; + + if (g_simple_async_result_propagate_error (simple, error)) + return NULL; + + device = (const char *) g_simple_async_result_get_op_res_gpointer (simple); + return device; +} + +/***********************************************************/ + +static guint32 +convert_uuids_to_capabilities (const char **strings) +{ + const char **iter; + guint32 capabilities = 0; + + for (iter = strings; iter && *iter; iter++) { + char **parts; + + parts = g_strsplit (*iter, "-", -1); + if (parts && parts[0]) { + switch (g_ascii_strtoull (parts[0], NULL, 16)) { + case 0x1116: + capabilities |= NM_BT_CAPABILITY_NAP; + break; + default: + break; + } + } + g_strfreev (parts); + } + + return capabilities; +} + +static void +on_adapter_acquired (GObject *object, GAsyncResult *res, NMBluezDevice *self) +{ + NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); + GError *error; + + priv->adapter = g_dbus_proxy_new_for_bus_finish (res, &error); + + if (!priv->adapter) { + nm_log_warn (LOGD_BT, "failed to acquire adapter proxy: %s.", + error && error->message ? error->message : "(unknown)"); + g_clear_error (&error); + return; + } + + check_emit_usable (self); +} + +static void +properties_changed (GDBusProxy *proxy, + GVariant *changed_properties, + GStrv invalidated_properties, + gpointer user_data) +{ + NMBluezDevice *self = NM_BLUEZ_DEVICE (user_data); + NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); + GVariantIter i; + const char *property; + const char *str; + GVariant *v; + guint32 uint_val; + gint int_val; + const char **strv; + + g_variant_iter_init (&i, changed_properties); + while (g_variant_iter_next (&i, "{&sv}", &property, &v)) { + if (!strcmp (property, "Name")) { + str = g_variant_get_string (v, NULL); + if (g_strcmp0 (priv->name, str)) { + g_free (priv->name); + priv->name = g_strdup (str); + g_object_notify (G_OBJECT (self), NM_BLUEZ_DEVICE_NAME); + } + } else if (!strcmp (property, "RSSI")) { + int_val = g_variant_get_int16 (v); + if (priv->rssi != int_val) { + priv->rssi = int_val; + g_object_notify (G_OBJECT (self), NM_BLUEZ_DEVICE_RSSI); + } + } else if (!strcmp (property, "UUIDs")) { + strv = g_variant_get_strv (v, NULL); + uint_val = convert_uuids_to_capabilities (strv); + g_free (strv); + if (priv->capabilities != uint_val) { + priv->capabilities = uint_val; + g_object_notify (G_OBJECT (self), NM_BLUEZ_DEVICE_CAPABILITIES); + } + } else if (!strcmp (property, "Connected")) { + gboolean connected = g_variant_get_boolean (v); + if (priv->connected != connected) { + priv->connected = connected; + g_object_notify (G_OBJECT (self), NM_BLUEZ_DEVICE_CONNECTED); + } + } + g_variant_unref (v); + } + + check_emit_usable (self); +} + +static void +query_properties (NMBluezDevice *self) +{ + NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); + GVariant *v; + const char **uuids; + struct ether_addr *tmp; + + v = g_dbus_proxy_get_cached_property (priv->proxy, "Address"); + priv->address = v ? g_variant_dup_string (v, NULL) : NULL; + if (v) + g_variant_unref (v); + if (priv->address) { + tmp = ether_aton (priv->address); + g_assert (tmp); + memcpy (priv->bin_address, tmp->ether_addr_octet, ETH_ALEN); + } + + v = g_dbus_proxy_get_cached_property (priv->proxy, "Name"); + priv->name = v ? g_variant_dup_string (v, NULL) : NULL; + if (v) + g_variant_unref (v); + + v = g_dbus_proxy_get_cached_property (priv->proxy, "RSSI"); + priv->rssi = v ? g_variant_get_int16 (v) : 0; + if (v) + g_variant_unref (v); + + v = g_dbus_proxy_get_cached_property (priv->proxy, "UUIDs"); + if (v) { + uuids = g_variant_get_strv (v, NULL); + priv->capabilities = convert_uuids_to_capabilities (uuids); + g_variant_unref (v); + } else + priv->capabilities = NM_BT_CAPABILITY_NONE; + + v = g_dbus_proxy_get_cached_property (priv->proxy, "Adapter"); + if (v) { + g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + BLUEZ_SERVICE, + g_variant_get_string (v, NULL), + BLUEZ_ADAPTER_INTERFACE, + NULL, + (GAsyncReadyCallback) on_adapter_acquired, + self); + g_variant_unref (v); + } + + priv->initialized = TRUE; + g_signal_emit (self, signals[INITIALIZED], 0, TRUE); + + check_emit_usable (self); +} + +static void +on_proxy_acquired (GObject *object, GAsyncResult *res, NMBluezDevice *self) +{ + NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); + GError *error; + + priv->proxy = g_dbus_proxy_new_for_bus_finish (res, &error); + + if (!priv->proxy) { + nm_log_warn (LOGD_BT, "failed to acquire device proxy: %s.", + error && error->message ? error->message : "(unknown)"); + g_clear_error (&error); + g_signal_emit (self, signals[INITIALIZED], 0, FALSE); + return; + } + + g_signal_connect (priv->proxy, "g-properties-changed", + G_CALLBACK (properties_changed), self); + + query_properties (self); +} + +static void +on_bus_acquired (GObject *object, GAsyncResult *res, NMBluezDevice *self) +{ + NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); + GError *error = NULL; + + priv->connection = g_bus_get_finish (res, &error); + + if (!priv->connection) { + nm_log_warn (LOGD_BT, "failed to acquire bus connection: %s.", + error && error->message ? error->message : "(unknown)"); + g_clear_error (&error); + g_signal_emit (self, signals[INITIALIZED], 0, FALSE); + return; + } + + check_emit_usable (self); +} + +/********************************************************************/ + +NMBluezDevice * +nm_bluez_device_new (const char *path) +{ + NMBluezDevice *self; + NMBluezDevicePrivate *priv; + + g_return_val_if_fail (path != NULL, NULL); + + self = (NMBluezDevice *) g_object_new (NM_TYPE_BLUEZ_DEVICE, + NM_BLUEZ_DEVICE_PATH, path, + NULL); + if (!self) + return NULL; + + priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); + + g_bus_get (G_BUS_TYPE_SYSTEM, + NULL, + (GAsyncReadyCallback) on_bus_acquired, + self); + + g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + BLUEZ_SERVICE, + priv->path, + BLUEZ_DEVICE_INTERFACE, + NULL, + (GAsyncReadyCallback) on_proxy_acquired, + self); + + return self; +} + +static void +nm_bluez_device_init (NMBluezDevice *self) +{ +} + +static void +dispose (GObject *object) +{ + NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (object); + + g_clear_object (&priv->adapter); + g_clear_object (&priv->connection); + + G_OBJECT_CLASS (nm_bluez_device_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (object); + + g_free (priv->path); + g_free (priv->address); + g_free (priv->name); + g_free (priv->bt_iface); + g_object_unref (priv->proxy); + + G_OBJECT_CLASS (nm_bluez_device_parent_class)->finalize (object); +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_PATH: + g_value_set_string (value, priv->path); + break; + case PROP_ADDRESS: + g_value_set_string (value, priv->address); + break; + case PROP_NAME: + g_value_set_string (value, priv->name); + break; + case PROP_CAPABILITIES: + g_value_set_uint (value, priv->capabilities); + break; + case PROP_RSSI: + g_value_set_int (value, priv->rssi); + break; + case PROP_USABLE: + g_value_set_boolean (value, priv->usable); + break; + case PROP_CONNECTED: + g_value_set_boolean (value, priv->connected); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_PATH: + /* construct only */ + priv->path = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_bluez_device_class_init (NMBluezDeviceClass *config_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (config_class); + + g_type_class_add_private (config_class, sizeof (NMBluezDevicePrivate)); + + /* virtual methods */ + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->finalize = finalize; + object_class->dispose = dispose; + + /* Properties */ + g_object_class_install_property + (object_class, PROP_PATH, + g_param_spec_string (NM_BLUEZ_DEVICE_PATH, + "DBus Path", + "DBus Path", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_ADDRESS, + g_param_spec_string (NM_BLUEZ_DEVICE_ADDRESS, + "Address", + "Address", + NULL, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_NAME, + g_param_spec_string (NM_BLUEZ_DEVICE_NAME, + "Name", + "Name", + NULL, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_CAPABILITIES, + g_param_spec_uint (NM_BLUEZ_DEVICE_CAPABILITIES, + "Capabilities", + "Capabilities", + 0, G_MAXUINT, 0, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_RSSI, + g_param_spec_int (NM_BLUEZ_DEVICE_RSSI, + "RSSI", + "RSSI", + G_MININT, G_MAXINT, 0, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_USABLE, + g_param_spec_boolean (NM_BLUEZ_DEVICE_USABLE, + "Usable", + "Usable", + FALSE, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_CONNECTED, + g_param_spec_boolean (NM_BLUEZ_DEVICE_CONNECTED, + "Connected", + "Connected", + FALSE, + G_PARAM_READABLE)); + + /* Signals */ + signals[INITIALIZED] = g_signal_new ("initialized", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NMBluezDeviceClass, initialized), + NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_BOOLEAN); +} diff --git a/src/bluez-manager/nm-bluez5-manager.c b/src/bluez-manager/nm-bluez5-manager.c new file mode 100644 index 0000000000..b138eec066 --- /dev/null +++ b/src/bluez-manager/nm-bluez5-manager.c @@ -0,0 +1,424 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2012 Red Hat, Inc. + * Copyright (C) 2013 Intel Corporation. + */ + +#include +#include +#include +#include + +#include "nm-logging.h" +#include "nm-bluez-manager.h" +#include "nm-bluez-device.h" +#include "nm-bluez-common.h" + +#include "nm-dbus-manager.h" + +typedef struct { + NMDBusManager *dbus_mgr; + gulong name_owner_changed_id; + + GDBusProxy *proxy; + + GHashTable *devices; +} NMBluezManagerPrivate; + +#define NM_BLUEZ_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_BLUEZ_MANAGER, NMBluezManagerPrivate)) + +G_DEFINE_TYPE (NMBluezManager, nm_bluez_manager, G_TYPE_OBJECT) + +enum { + BDADDR_ADDED, + BDADDR_REMOVED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +static void +emit_bdaddr_added (NMBluezManager *self, NMBluezDevice *device) +{ + g_signal_emit (self, signals[BDADDR_ADDED], 0, + device, + nm_bluez_device_get_address (device), + nm_bluez_device_get_name (device), + nm_bluez_device_get_path (device), + nm_bluez_device_get_capabilities (device)); +} + +void +nm_bluez_manager_query_devices (NMBluezManager *self) +{ + NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); + NMBluezDevice *device; + GHashTableIter iter; + + g_hash_table_iter_init (&iter, priv->devices); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &device)) { + if (nm_bluez_device_get_usable (device)) + emit_bdaddr_added (self, device); + } +} + +static void +device_usable (NMBluezDevice *device, GParamSpec *pspec, NMBluezManager *self) +{ + gboolean usable = nm_bluez_device_get_usable (device); + + nm_log_dbg (LOGD_BT, "(%s): bluez device now %s", + nm_bluez_device_get_path (device), + usable ? "usable" : "unusable"); + + if (usable) { + nm_log_dbg (LOGD_BT, "(%s): bluez device address %s", + nm_bluez_device_get_path (device), + nm_bluez_device_get_address (device)); + emit_bdaddr_added (self, device); + } else + g_signal_emit (self, signals[BDADDR_REMOVED], 0, + nm_bluez_device_get_address (device), + nm_bluez_device_get_path (device)); +} + +static void +device_initialized (NMBluezDevice *device, gboolean success, NMBluezManager *self) +{ + NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); + + nm_log_dbg (LOGD_BT, "(%s): bluez device %s", + nm_bluez_device_get_path (device), + success ? "initialized" : "failed to initialize"); + if (!success) + g_hash_table_remove (priv->devices, nm_bluez_device_get_path (device)); +} + +static void +device_added (GDBusProxy *proxy, const gchar *path, NMBluezManager *self) +{ + NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); + NMBluezDevice *device; + + device = nm_bluez_device_new (path); + g_signal_connect (device, "initialized", G_CALLBACK (device_initialized), self); + g_signal_connect (device, "notify::usable", G_CALLBACK (device_usable), self); + g_hash_table_insert (priv->devices, (gpointer) nm_bluez_device_get_path (device), device); + + nm_log_dbg (LOGD_BT, "(%s): new bluez device found", path); +} + +static void +device_removed (GDBusProxy *proxy, const gchar *path, NMBluezManager *self) +{ + NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); + NMBluezDevice *device; + + nm_log_dbg (LOGD_BT, "(%s): bluez device removed", path); + + device = g_hash_table_lookup (priv->devices, path); + if (device) { + g_object_ref (device); + g_hash_table_remove (priv->devices, nm_bluez_device_get_path (device)); + + g_signal_emit (self, signals[BDADDR_REMOVED], 0, + nm_bluez_device_get_address (device), + nm_bluez_device_get_path (device)); + + g_object_unref (device); + } +} + +static void +object_manager_g_signal (GDBusProxy *proxy, + gchar *sender_name, + gchar *signal_name, + GVariant *parameters, + NMBluezManager *self) +{ + GVariant *variant; + const gchar *path; + + if (!strcmp (signal_name, "InterfacesRemoved")) { + const gchar **ifaces; + gsize i, length; + + g_variant_get (parameters, "(&o*)", &path, &variant); + + ifaces = g_variant_get_strv (variant, &length); + + for (i = 0; i < length; i++) { + if (!strcmp (ifaces[i], BLUEZ_DEVICE_INTERFACE)) { + device_removed (proxy, path, self); + break; + } + } + + g_free (ifaces); + + } else if (!strcmp (signal_name, "InterfacesAdded")) { + g_variant_get (parameters, "(&o*)", &path, &variant); + + if (g_variant_lookup_value (variant, BLUEZ_DEVICE_INTERFACE, + G_VARIANT_TYPE_DICTIONARY)) + device_added (proxy, path, self); + } +} + +static void +get_managed_objects_cb (GDBusProxy *proxy, + GAsyncResult *res, + NMBluezManager *self) +{ + GVariant *variant, *ifaces; + GVariantIter i; + GError *error = NULL; + const char *path; + + variant = g_dbus_proxy_call_finish (proxy, res, &error); + + if (!variant) { + nm_log_warn (LOGD_BT, "Couldn't get managed objects: %s", + error && error->message ? error->message : "(unknown)"); + g_clear_error (&error); + return; + } + g_variant_iter_init (&i, g_variant_get_child_value (variant, 0)); + while ((g_variant_iter_next (&i, "{&o*}", &path, &ifaces))) { + if (g_variant_lookup_value (ifaces, BLUEZ_DEVICE_INTERFACE, + G_VARIANT_TYPE_DICTIONARY)) { + device_added (proxy, path, self); + } + } + + g_variant_unref (variant); +} + +static void +on_proxy_acquired (GObject *object, + GAsyncResult *res, + NMBluezManager *self) +{ + NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); + GError *error = NULL; + + priv->proxy = g_dbus_proxy_new_for_bus_finish (res, &error); + + if (!priv->proxy) { + nm_log_warn (LOGD_BT, "Couldn't acquire object manager proxy: %s", + error && error->message ? error->message : "(unknown)"); + g_clear_error (&error); + return; + } + + /* Get already managed devices. */ + g_dbus_proxy_call (priv->proxy, "GetManagedObjects", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) get_managed_objects_cb, + self); + + g_signal_connect (priv->proxy, "g-signal", + G_CALLBACK (object_manager_g_signal), self); +} + +static void +bluez_connect (NMBluezManager *self) +{ + NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); + + g_return_if_fail (priv->proxy == NULL); + + g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + BLUEZ_SERVICE, + BLUEZ_MANAGER_PATH, + OBJECT_MANAGER_INTERFACE, + NULL, + (GAsyncReadyCallback) on_proxy_acquired, + self); +} + +static void +name_owner_changed_cb (NMDBusManager *dbus_mgr, + const char *name, + const char *old_owner, + const char *new_owner, + gpointer user_data) +{ + NMBluezManager *self = NM_BLUEZ_MANAGER (user_data); + NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); + gboolean old_owner_good = (old_owner && strlen (old_owner)); + gboolean new_owner_good = (new_owner && strlen (new_owner)); + + /* Can't handle the signal if its not from the Bluez */ + if (strcmp (BLUEZ_SERVICE, name)) + return; + + if (old_owner_good && !new_owner_good) { + if (priv->devices) { + GHashTableIter iter; + NMBluezDevice *device; + + g_hash_table_iter_init (&iter, priv->devices); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &device)) + g_signal_emit (self, signals[BDADDR_REMOVED], 0, + nm_bluez_device_get_address (device), + nm_bluez_device_get_path (device)); + g_hash_table_remove_all (priv->devices); + } + } +} + +static void +bluez_cleanup (NMBluezManager *self, gboolean do_signal) +{ + NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); + NMBluezDevice *device; + GHashTableIter iter; + + if (priv->proxy) { + g_object_unref (priv->proxy); + priv->proxy = NULL; + } + + if (do_signal) { + g_hash_table_iter_init (&iter, priv->devices); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &device)) + g_signal_emit (self, signals[BDADDR_REMOVED], 0, + nm_bluez_device_get_address (device), + nm_bluez_device_get_path (device)); + } + + g_hash_table_remove_all (priv->devices); +} + +static void +dbus_connection_changed_cb (NMDBusManager *dbus_mgr, + DBusGConnection *connection, + gpointer user_data) +{ + NMBluezManager *self = NM_BLUEZ_MANAGER (user_data); + + if (!connection) + bluez_cleanup (self, TRUE); + else + bluez_connect (self); +} + +/****************************************************************/ + +NMBluezManager * +nm_bluez_manager_get (void) +{ + static NMBluezManager *singleton = NULL; + + if (singleton) + return g_object_ref (singleton); + + singleton = (NMBluezManager *) g_object_new (NM_TYPE_BLUEZ_MANAGER, NULL); + g_assert (singleton); + + return singleton; +} + +static void +nm_bluez_manager_init (NMBluezManager *self) +{ + NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); + + priv->dbus_mgr = nm_dbus_manager_get (); + g_assert (priv->dbus_mgr); + + g_signal_connect (priv->dbus_mgr, + NM_DBUS_MANAGER_NAME_OWNER_CHANGED, + G_CALLBACK (name_owner_changed_cb), + self); + + g_signal_connect (priv->dbus_mgr, + NM_DBUS_MANAGER_DBUS_CONNECTION_CHANGED, + G_CALLBACK (dbus_connection_changed_cb), + self); + + bluez_connect (self); + + priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal, + NULL, g_object_unref); +} + +static void +dispose (GObject *object) +{ + NMBluezManager *self = NM_BLUEZ_MANAGER (object); + NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); + + bluez_cleanup (self, FALSE); + + if (priv->dbus_mgr) { + g_signal_handlers_disconnect_by_func (priv->dbus_mgr, name_owner_changed_cb, self); + g_signal_handlers_disconnect_by_func (priv->dbus_mgr, dbus_connection_changed_cb, self); + priv->dbus_mgr = NULL; + } + + G_OBJECT_CLASS (nm_bluez_manager_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (object); + + g_hash_table_destroy (priv->devices); + + G_OBJECT_CLASS (nm_bluez_manager_parent_class)->finalize (object); +} + +static void +nm_bluez_manager_class_init (NMBluezManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (NMBluezManagerPrivate)); + + /* virtual methods */ + object_class->dispose = dispose; + object_class->finalize = finalize; + + /* Signals */ + signals[BDADDR_ADDED] = + g_signal_new (NM_BLUEZ_MANAGER_BDADDR_ADDED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMBluezManagerClass, bdaddr_added), + NULL, NULL, NULL, + G_TYPE_NONE, 5, G_TYPE_OBJECT, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT); + + signals[BDADDR_REMOVED] = + g_signal_new (NM_BLUEZ_MANAGER_BDADDR_REMOVED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMBluezManagerClass, bdaddr_removed), + NULL, NULL, NULL, + G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING); +} diff --git a/src/nm-manager.c b/src/nm-manager.c index f395f03302..416fefd84b 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -4452,7 +4452,11 @@ nm_manager_new (NMSettings *settings, G_CALLBACK (rfkill_manager_rfkill_changed_cb), singleton); - priv->bluez_mgr = nm_bluez_manager_get (NM_CONNECTION_PROVIDER (priv->settings)); + priv->bluez_mgr = nm_bluez_manager_get ( +#if ! WITH_BLUEZ5 + NM_CONNECTION_PROVIDER (priv->settings) +#endif + ); g_signal_connect (priv->bluez_mgr, NM_BLUEZ_MANAGER_BDADDR_ADDED, -- cgit v1.2.1