diff options
Diffstat (limited to 'src/bluez-manager/nm-bluez-device.c')
| -rw-r--r-- | src/bluez-manager/nm-bluez-device.c | 728 |
1 files changed, 678 insertions, 50 deletions
diff --git a/src/bluez-manager/nm-bluez-device.c b/src/bluez-manager/nm-bluez-device.c index b12a8619..ed5e345d 100644 --- a/src/bluez-manager/nm-bluez-device.c +++ b/src/bluez-manager/nm-bluez-device.c @@ -16,9 +16,12 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Copyright (C) 2009 - 2012 Red Hat, Inc. + * Copyright (C) 2013 Intel Corporation. */ #include <glib.h> +#include <glib/gi18n.h> +#include <gio/gio.h> #include <string.h> #include <net/ethernet.h> #include <netinet/ether.h> @@ -26,12 +29,15 @@ #include "NetworkManager.h" #include "nm-setting-bluetooth.h" -#include "nm-dbus-manager.h" -#include "nm-bluez-device.h" #include "nm-bluez-common.h" +#if WITH_BLUEZ4 +#include "nm-dbus-manager.h" #include "nm-dbus-glib-types.h" +#endif +#include "nm-bluez-device.h" #include "nm-logging.h" #include "nm-marshal.h" +#include "nm-utils.h" G_DEFINE_TYPE (NMBluezDevice, nm_bluez_device, G_TYPE_OBJECT) @@ -40,18 +46,33 @@ G_DEFINE_TYPE (NMBluezDevice, nm_bluez_device, G_TYPE_OBJECT) typedef struct { char *path; - DBusGProxy *proxy; + GDBusConnection *dbus_connection; +#if ! WITH_BLUEZ4 + GDBusProxy *proxy5; + GDBusProxy *adapter; + gboolean adapter_powered; +#else + DBusGProxy *proxy4; +#endif + 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; NMConnectionProvider *provider; GSList *connections; + + NMConnection *pan_connection; + gboolean pan_connection_no_autocreate; } NMBluezDevicePrivate; @@ -63,6 +84,7 @@ enum { PROP_CAPABILITIES, PROP_RSSI, PROP_USABLE, + PROP_CONNECTED, LAST_PROP }; @@ -74,6 +96,12 @@ enum { }; static guint signals[LAST_SIGNAL] = { 0 }; + +static void cp_connection_added (NMConnectionProvider *provider, + NMConnection *connection, NMBluezDevice *self); +static gboolean connection_compatible (NMBluezDevice *self, NMConnection *connection); + + /***********************************************************/ const char * @@ -132,13 +160,147 @@ nm_bluez_device_get_rssi (NMBluezDevice *self) 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 +pan_connection_check_create (NMBluezDevice *self) +{ + NMConnection *connection; + NMConnection *added; + NMSetting *setting; + char *uuid, *id; + GByteArray *bdaddr_array; + GError *error = NULL; + NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); + + g_return_if_fail (priv->capabilities & NM_BT_CAPABILITY_NAP); + g_return_if_fail (priv->connections == NULL); + g_return_if_fail (priv->name); + + if (priv->pan_connection || priv->pan_connection_no_autocreate) { + /* already have a connection or we don't want to create one, nothing to do. */ + return; + } + + if (!nm_connection_provider_has_connections_loaded (priv->provider)) { + /* do not try to create any connections until the connection provider is ready. */ + return; + } + + /* Only try once to create a connection. If it does not succeed, we do not try again. Also, + * if the connection gets deleted later, do not create another one for this device. */ + priv->pan_connection_no_autocreate = TRUE; + + /* create a new connection */ + + connection = nm_connection_new (); + + /* Setting: Connection */ + uuid = nm_utils_uuid_generate (); + id = g_strdup_printf (_("%s Network"), priv->name); + setting = nm_setting_connection_new (); + g_object_set (setting, + NM_SETTING_CONNECTION_ID, id, + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_AUTOCONNECT, FALSE, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_BLUETOOTH_SETTING_NAME, + NULL); + nm_connection_add_setting (connection, setting); + + /* Setting: Bluetooth */ + bdaddr_array = g_byte_array_sized_new (sizeof (priv->bin_address)); + g_byte_array_append (bdaddr_array, priv->bin_address, sizeof (priv->bin_address)); + setting = nm_setting_bluetooth_new (); + g_object_set (G_OBJECT (setting), + NM_SETTING_BLUETOOTH_BDADDR, bdaddr_array, + NM_SETTING_BLUETOOTH_TYPE, NM_SETTING_BLUETOOTH_TYPE_PANU, + NULL); + nm_connection_add_setting (connection, setting); + g_byte_array_free (bdaddr_array, TRUE); + + /* Setting: IPv4 */ + setting = nm_setting_ip4_config_new (); + g_object_set (G_OBJECT (setting), + NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NM_SETTING_IP4_CONFIG_MAY_FAIL, FALSE, + NULL); + nm_connection_add_setting (connection, setting); + + /* Setting: IPv6 */ + setting = nm_setting_ip6_config_new (); + g_object_set (G_OBJECT (setting), + NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE, + NULL); + nm_connection_add_setting (connection, setting); + + /* Adding a new connection raises a signal which eventually calls check_emit_usable (again) + * which then already finds the suitable connection in priv->connections. This is confusing, + * so block the signal. check_emit_usable will succeed after this function call returns. */ + g_signal_handlers_block_by_func (priv->provider, cp_connection_added, self); + added = nm_connection_provider_add_connection (priv->provider, connection, &error); + g_signal_handlers_unblock_by_func (priv->provider, cp_connection_added, self); + + if (added) { + g_assert (!g_slist_find (priv->connections, added)); + g_assert (connection_compatible (self, added)); + + priv->connections = g_slist_prepend (priv->connections, g_object_ref (added)); + priv->pan_connection = added; + nm_log_dbg (LOGD_SETTINGS, "added new Bluetooth connection for NAP device '%s': '%s' (%s)", priv->path, id, uuid); + } else { + nm_log_warn (LOGD_SETTINGS, "couldn't add new Bluetooth connection for NAP device '%s': '%s' (%s): %d / %s", + priv->path, id, uuid, error ? error->code : -1, + (error && error->message) ? error->message : "(unknown)"); + g_clear_error (&error); + } + + g_object_unref (connection); + g_free (id); + g_free (uuid); +} + 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->connections); + /* only expect the supported capabilities set. */ + g_assert ((priv->capabilities & ~( NM_BT_CAPABILITY_NAP +#if WITH_BLUEZ4 + | NM_BT_CAPABILITY_DUN +#endif + )) == NM_BT_CAPABILITY_NONE); + + new_usable = (priv->initialized && priv->capabilities && priv->name && +#if ! WITH_BLUEZ4 + priv->adapter && priv->adapter_powered && +#endif + priv->dbus_connection && priv->address); + + if (!new_usable) + goto END; + + if (priv->connections) + goto END; + + if (!(priv->capabilities & NM_BT_CAPABILITY_NAP)) { + /* non NAP devices are only usable, if they already have a connection. */ + new_usable = FALSE; + goto END; + } + + pan_connection_check_create (self); + new_usable = !!priv->pan_connection; + +END: if (new_usable != priv->usable) { priv->usable = new_usable; g_object_notify (G_OBJECT (self), NM_BLUEZ_DEVICE_USABLE); @@ -162,6 +324,10 @@ connection_compatible (NMBluezDevice *self, NMConnection *connection) if (!s_bt) return FALSE; + if (!priv->address) { + /* unless address is set, bin_address is not initialized. */ + return FALSE; + } bdaddr = nm_setting_bluetooth_get_bdaddr (s_bt); if (!bdaddr || bdaddr->len != ETH_ALEN) return FALSE; @@ -171,11 +337,11 @@ connection_compatible (NMBluezDevice *self, NMConnection *connection) bt_type = nm_setting_bluetooth_get_connection_type (s_bt); if ( g_str_equal (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN) && !(priv->capabilities & NM_BT_CAPABILITY_DUN)) - return FALSE; + return FALSE; if ( g_str_equal (bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU) && !(priv->capabilities & NM_BT_CAPABILITY_NAP)) - return FALSE; + return FALSE; return TRUE; } @@ -209,6 +375,9 @@ cp_connection_removed (NMConnectionProvider *provider, if (g_slist_find (priv->connections, connection)) { priv->connections = g_slist_remove (priv->connections, connection); + if (priv->pan_connection == connection) { + priv->pan_connection = NULL; + } g_object_unref (connection); check_emit_usable (self); } @@ -237,6 +406,165 @@ cp_connections_loaded (NMConnectionProvider *provider, NMBluezDevice *self) /***********************************************************/ +static void +bluez_disconnect_cb (GDBusConnection *dbus_connection, + GAsyncResult *res, + gpointer user_data) +{ + NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (user_data); + GError *error = NULL; + GVariant *variant; + + variant = g_dbus_connection_call_finish (dbus_connection, res, &error); + if (!variant) { + nm_log_warn (LOGD_BT, "%s: failed to disconnect: %s", priv->address, error->message); + g_error_free (error); + } else + g_variant_unref (variant); +} + +void +nm_bluez_device_disconnect (NMBluezDevice *self) +{ + NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); + GVariant *args = NULL; + const char *dbus_iface = BLUEZ_NETWORK_INTERFACE; + + g_return_if_fail (priv->dbus_connection); + +#if ! WITH_BLUEZ4 + g_return_if_fail (priv->connection_bt_type == NM_BT_CAPABILITY_NAP); +#else + g_return_if_fail (priv->connection_bt_type == NM_BT_CAPABILITY_NAP || priv->connection_bt_type == NM_BT_CAPABILITY_DUN); + + if (priv->connection_bt_type == NM_BT_CAPABILITY_DUN) { + /* Can't pass a NULL interface name through dbus to bluez, so just + * ignore the disconnect if the interface isn't known. + */ + if (!priv->bt_iface) + return; + + args = g_variant_new ("(s)", priv->bt_iface), + dbus_iface = BLUEZ_SERIAL_INTERFACE; + } +#endif + + g_dbus_connection_call (priv->dbus_connection, + BLUEZ_SERVICE, + priv->path, + dbus_iface, + "Disconnect", + args ? args : g_variant_new ("()"), + NULL, + G_DBUS_CALL_FLAGS_NONE, + 10000, + NULL, + (GAsyncReadyCallback) bluez_disconnect_cb, + self); + + priv->connection_bt_type = NM_BT_CAPABILITY_NONE; +} + +static void +bluez_connect_cb (GDBusConnection *dbus_connection, + GAsyncResult *res, + gpointer user_data) +{ + GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data); + GObject *result_object = g_async_result_get_source_object (G_ASYNC_RESULT (result)); + NMBluezDevice *self = NM_BLUEZ_DEVICE (result_object); + NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); + GError *error = NULL; + char *device; + GVariant *variant; + + variant = g_dbus_connection_call_finish (dbus_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); + g_object_unref (result_object); +} + +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); + const char *dbus_iface = BLUEZ_NETWORK_INTERFACE; + const char *connect_type = BLUETOOTH_CONNECT_NAP; + + g_return_if_fail (priv->capabilities & connection_bt_type & (NM_BT_CAPABILITY_DUN | NM_BT_CAPABILITY_NAP)); +#if ! WITH_BLUEZ4 + g_return_if_fail (connection_bt_type == NM_BT_CAPABILITY_NAP); +#else + g_return_if_fail (connection_bt_type == NM_BT_CAPABILITY_NAP || connection_bt_type == NM_BT_CAPABILITY_DUN); + + if (connection_bt_type == NM_BT_CAPABILITY_DUN) { + dbus_iface = BLUEZ_SERIAL_INTERFACE; + connect_type = BLUETOOTH_CONNECT_DUN; + } +#endif + + simple = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + nm_bluez_device_connect_async); + + g_dbus_connection_call (priv->dbus_connection, + BLUEZ_SERVICE, + priv->path, + dbus_iface, + "Connect", + g_variant_new ("(s)", connect_type), + NULL, + G_DBUS_CALL_FLAGS_NONE, + 20000, + NULL, + (GAsyncReadyCallback) bluez_connect_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) { @@ -249,9 +577,11 @@ convert_uuids_to_capabilities (const char **strings) parts = g_strsplit (*iter, "-", -1); if (parts && parts[0]) { switch (g_ascii_strtoull (parts[0], NULL, 16)) { +#if WITH_BLUEZ4 case 0x1103: capabilities |= NM_BT_CAPABILITY_DUN; break; +#endif case 0x1116: capabilities |= NM_BT_CAPABILITY_NAP; break; @@ -266,7 +596,175 @@ convert_uuids_to_capabilities (const char **strings) } static void -property_changed (DBusGProxy *proxy, +_set_property_capabilities (NMBluezDevice *self, const char **uuids, gboolean notify) +{ + guint32 uint_val; + NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); + + uint_val = convert_uuids_to_capabilities (uuids); + if (priv->capabilities != uint_val) { + if (priv->capabilities) { + /* changing (relevant) capabilities is not supported and ignored -- except setting initially */ + nm_log_warn (LOGD_BT, "ignore change of capabilities for Bluetooth device %s from %u to %u", + priv->path, priv->capabilities, uint_val); + return; + } + nm_log_dbg (LOGD_BT, "set capabilities for Bluetooth device %s: %s%s%s", priv->path, + uint_val & NM_BT_CAPABILITY_NAP ? "NAP" : "", + ((uint_val & NM_BT_CAPABILITY_DUN) && (uint_val &NM_BT_CAPABILITY_NAP)) ? " | " : "", + uint_val & NM_BT_CAPABILITY_DUN ? "DUN" : ""); + priv->capabilities = uint_val; + if (notify) + g_object_notify (G_OBJECT (self), NM_BLUEZ_DEVICE_CAPABILITIES); + } +} + +/** + * priv->address can only be set one to a certain (non NULL) value. Every later attempt + * to reset it to another value will be ignored and a warning will be logged. + * + * When setting the address for the first time, we also set bin_address. + **/ +static void +_set_property_address (NMBluezDevice *self, const char *addr) +{ + struct ether_addr *tmp; + NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); + + if (g_strcmp0 (priv->address, addr) == 0) + return; + + if (!addr) { + nm_log_warn (LOGD_BT, "[%s] cannot reset address from '%s' to NULL", priv->path, priv->address); + return; + } + + if (priv->address != NULL) { + nm_log_warn (LOGD_BT, "[%s] cannot reset address from '%s' to '%s'", priv->path, priv->address, addr); + return; + } + + tmp = ether_aton (addr); + if (!tmp) { + if (priv->address) + nm_log_warn (LOGD_BT, "[%s] cannot reset address from '%s' to '%s' (invalid value)", priv->path, priv->address, addr); + else + nm_log_warn (LOGD_BT, "[%s] cannot reset address from NULL to '%s' (invalid value)", priv->path, addr); + return; + } + memcpy (priv->bin_address, tmp->ether_addr_octet, ETH_ALEN); + priv->address = g_strdup (addr); + return; +} + +#if ! WITH_BLUEZ4 +static void +adapter_properties_changed (GDBusProxy *proxy5, + 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; + GVariant *v; + + g_variant_iter_init (&i, changed_properties); + while (g_variant_iter_next (&i, "{&sv}", &property, &v)) { + if (!strcmp (property, "Powered")) { + gboolean powered = g_variant_get_boolean (v); + if (priv->adapter_powered != powered) + priv->adapter_powered = powered; + } + g_variant_unref (v); + } + + check_emit_usable (self); +} + +static void +on_adapter_acquired (GObject *object, GAsyncResult *res, NMBluezDevice *self) +{ + NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); + GError *error; + GVariant *v; + + 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->message); + g_clear_error (&error); + g_signal_emit (self, signals[INITIALIZED], 0, FALSE); + } else { + g_signal_connect (priv->adapter, "g-properties-changed", + G_CALLBACK (adapter_properties_changed), self); + + /* Check adapter's powered state */ + v = g_dbus_proxy_get_cached_property (priv->adapter, "Powered"); + priv->adapter_powered = v ? g_variant_get_boolean (v) : FALSE; + if (v) + g_variant_unref (v); + + priv->initialized = TRUE; + g_signal_emit (self, signals[INITIALIZED], 0, TRUE); + + check_emit_usable (self); + } + + g_object_unref (self); +} + +static void +properties_changed (GDBusProxy *proxy5, + 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; + gint int_val; + const char **strv; + + g_object_freeze_notify (G_OBJECT (self)); + 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); + _set_property_capabilities (self, strv, TRUE); + g_free (strv); + } 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); + } + g_object_thaw_notify (G_OBJECT (self)); + + check_emit_usable (self); +} +#else +static void +property_changed (DBusGProxy *proxy4, const char *property, GValue *value, gpointer user_data) @@ -274,47 +772,100 @@ property_changed (DBusGProxy *proxy, NMBluezDevice *self = NM_BLUEZ_DEVICE (user_data); NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); const char *str; - guint32 uint_val; gint int_val; - if (!strcmp (property, "Name")) { - str = g_value_get_string (value); - if ( (!priv->name && str) - || (priv->name && !str) - || (priv->name && str && strcmp (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_value_get_int (value); - if (priv->rssi != int_val) { - priv->rssi = int_val; - g_object_notify (G_OBJECT (self), NM_BLUEZ_DEVICE_RSSI); - } - } else if (!strcmp (property, "UUIDs")) { - uint_val = convert_uuids_to_capabilities ((const char **) g_value_get_boxed (value)); - if (priv->capabilities != uint_val) { - priv->capabilities = uint_val; - g_object_notify (G_OBJECT (self), NM_BLUEZ_DEVICE_CAPABILITIES); + g_object_freeze_notify (G_OBJECT (self)); + { + if (!strcmp (property, "Name")) { + str = g_value_get_string (value); + if ( (!priv->name && str) + || (priv->name && !str) + || (priv->name && str && strcmp (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_value_get_int (value); + if (priv->rssi != int_val) { + priv->rssi = int_val; + g_object_notify (G_OBJECT (self), NM_BLUEZ_DEVICE_RSSI); + } + } else if (!strcmp (property, "UUIDs")) { + _set_property_capabilities (self, (const char **) g_value_get_boxed (value), TRUE); + } else if (!strcmp (property, "Connected")) { + gboolean connected = g_value_get_boolean (value); + if (priv->connected != connected) { + priv->connected = connected; + g_object_notify (G_OBJECT (self), NM_BLUEZ_DEVICE_CONNECTED); + } } } + g_object_thaw_notify (G_OBJECT (self)); check_emit_usable (self); } +#endif + +#if ! WITH_BLUEZ4 +static void +query_properties (NMBluezDevice *self) +{ + NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); + GVariant *v; + const char **uuids; + + v = g_dbus_proxy_get_cached_property (priv->proxy5, "Address"); + _set_property_address (self, v ? g_variant_get_string (v, NULL) : NULL); + if (v) + g_variant_unref (v); + + v = g_dbus_proxy_get_cached_property (priv->proxy5, "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->proxy5, "RSSI"); + priv->rssi = v ? g_variant_get_int16 (v) : 0; + if (v) + g_variant_unref (v); + + v = g_dbus_proxy_get_cached_property (priv->proxy5, "UUIDs"); + if (v) { + uuids = g_variant_get_strv (v, NULL); + _set_property_capabilities (self, uuids, FALSE); + g_variant_unref (v); + } else + priv->capabilities = NM_BT_CAPABILITY_NONE; + + v = g_dbus_proxy_get_cached_property (priv->proxy5, "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, + g_object_ref (self)); + g_variant_unref (v); + } + /* Check if any connections match this device */ + cp_connections_loaded (priv->provider, self); +} +#else static void -get_properties_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) +get_properties_cb (DBusGProxy *proxy4, DBusGProxyCall *call, gpointer user_data) { NMBluezDevice *self = NM_BLUEZ_DEVICE (user_data); NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); GHashTable *properties = NULL; GError *err = NULL; GValue *value; - const char **uuids; - struct ether_addr *tmp; - if (!dbus_g_proxy_end_call (proxy, call, &err, + if (!dbus_g_proxy_end_call (proxy4, call, &err, DBUS_TYPE_G_MAP_OF_VARIANT, &properties, G_TYPE_INVALID)) { nm_log_warn (LOGD_BT, "bluez error getting device properties: %s", @@ -325,12 +876,7 @@ get_properties_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) } value = g_hash_table_lookup (properties, "Address"); - priv->address = value ? g_value_dup_string (value) : NULL; - if (priv->address) { - tmp = ether_aton (priv->address); - g_assert (tmp); - memcpy (priv->bin_address, tmp->ether_addr_octet, ETH_ALEN); - } + _set_property_address (self, value ? g_value_get_string (value) : NULL); value = g_hash_table_lookup (properties, "Name"); priv->name = value ? g_value_dup_string (value) : NULL; @@ -340,8 +886,7 @@ get_properties_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) value = g_hash_table_lookup (properties, "UUIDs"); if (value) { - uuids = (const char **) g_value_get_boxed (value); - priv->capabilities = convert_uuids_to_capabilities (uuids); + _set_property_capabilities (self, (const char **) g_value_get_boxed (value), FALSE); } else priv->capabilities = NM_BT_CAPABILITY_NONE; @@ -362,7 +907,7 @@ query_properties (NMBluezDevice *self) NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); DBusGProxyCall *call; - call = dbus_g_proxy_begin_call (priv->proxy, "GetProperties", + call = dbus_g_proxy_begin_call (priv->proxy4, "GetProperties", get_properties_cb, self, NULL, G_TYPE_INVALID); @@ -371,6 +916,49 @@ query_properties (NMBluezDevice *self) priv->path); } } +#endif + + +#if ! WITH_BLUEZ4 +static void +on_proxy_acquired (GObject *object, GAsyncResult *res, NMBluezDevice *self) +{ + NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); + GError *error; + + priv->proxy5 = g_dbus_proxy_new_for_bus_finish (res, &error); + + if (!priv->proxy5) { + nm_log_warn (LOGD_BT, "failed to acquire device proxy: %s.", error->message); + g_clear_error (&error); + g_signal_emit (self, signals[INITIALIZED], 0, FALSE); + } else { + g_signal_connect (priv->proxy5, "g-properties-changed", + G_CALLBACK (properties_changed), self); + + query_properties (self); + } + g_object_unref (self); +} +#endif + +static void +on_bus_acquired (GObject *object, GAsyncResult *res, NMBluezDevice *self) +{ + NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); + GError *error = NULL; + + priv->dbus_connection = g_bus_get_finish (res, &error); + + if (!priv->dbus_connection) { + nm_log_warn (LOGD_BT, "failed to acquire bus connection: %s.", error->message); + g_clear_error (&error); + g_signal_emit (self, signals[INITIALIZED], 0, FALSE); + return; + } + + check_emit_usable (self); +} /********************************************************************/ @@ -379,8 +967,10 @@ nm_bluez_device_new (const char *path, NMConnectionProvider *provider) { NMBluezDevice *self; NMBluezDevicePrivate *priv; +#if WITH_BLUEZ4 NMDBusManager *dbus_mgr; DBusGConnection *connection; +#endif g_return_val_if_fail (path != NULL, NULL); g_return_val_if_fail (provider != NULL, NULL); @@ -415,25 +1005,43 @@ nm_bluez_device_new (const char *path, NMConnectionProvider *provider) G_CALLBACK (cp_connections_loaded), self); + g_bus_get (G_BUS_TYPE_SYSTEM, + NULL, + (GAsyncReadyCallback) on_bus_acquired, + self); + +#if ! WITH_BLUEZ4 + g_object_ref (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); +#else dbus_mgr = nm_dbus_manager_get (); connection = nm_dbus_manager_get_connection (dbus_mgr); - priv->proxy = dbus_g_proxy_new_for_name (connection, - BLUEZ_SERVICE, - priv->path, - BLUEZ_DEVICE_INTERFACE); + priv->proxy4 = dbus_g_proxy_new_for_name (connection, + BLUEZ_SERVICE, + priv->path, + BLUEZ_DEVICE_INTERFACE); g_object_unref (dbus_mgr); dbus_g_object_register_marshaller (_nm_marshal_VOID__STRING_BOXED, G_TYPE_NONE, G_TYPE_STRING, G_TYPE_VALUE, G_TYPE_INVALID); - dbus_g_proxy_add_signal (priv->proxy, "PropertyChanged", + dbus_g_proxy_add_signal (priv->proxy4, "PropertyChanged", G_TYPE_STRING, G_TYPE_VALUE, G_TYPE_INVALID); - dbus_g_proxy_connect_signal (priv->proxy, "PropertyChanged", + dbus_g_proxy_connect_signal (priv->proxy4, "PropertyChanged", G_CALLBACK (property_changed), self, NULL); query_properties (self); +#endif return self; } @@ -457,6 +1065,11 @@ dispose (GObject *object) g_signal_handlers_disconnect_by_func (priv->provider, cp_connection_updated, self); g_signal_handlers_disconnect_by_func (priv->provider, cp_connections_loaded, self); +#if ! WITH_BLUEZ4 + g_clear_object (&priv->adapter); +#endif + g_clear_object (&priv->dbus_connection); + G_OBJECT_CLASS (nm_bluez_device_parent_class)->dispose (object); } @@ -468,7 +1081,12 @@ finalize (GObject *object) g_free (priv->path); g_free (priv->address); g_free (priv->name); - g_object_unref (priv->proxy); + g_free (priv->bt_iface); +#if ! WITH_BLUEZ4 + g_object_unref (priv->proxy5); +#else + g_object_unref (priv->proxy4); +#endif G_OBJECT_CLASS (nm_bluez_device_parent_class)->finalize (object); } @@ -498,6 +1116,9 @@ get_property (GObject *object, guint prop_id, 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; @@ -583,13 +1204,20 @@ nm_bluez_device_class_init (NMBluezDeviceClass *config_class) 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, - g_cclosure_marshal_VOID__BOOLEAN, + NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); } |