diff --git a/configure.ac b/configure.ac index be15f7986a..136c1d047b 100644 --- a/configure.ac +++ b/configure.ac @@ -267,6 +267,18 @@ else fi AC_SUBST(UDEV_BASE_DIR) +# BlueZ +AC_ARG_ENABLE(bluez4, AS_HELP_STRING([--enable-bluez4], + [build with BlueZ 4 support instead of BlueZ 5]), + [enable_bluez4=${enableval}]) +if (test "${enable_bluez4}" = "yes"); then + AC_DEFINE(WITH_BLUEZ4, 1, [Define if you have BlueZ 4 support]) +else + AC_DEFINE(WITH_BLUEZ4, 0, [Define if you have BlueZ 4 support]) + enable_bluez4=no +fi +AM_CONDITIONAL(WITH_BLUEZ4, test "${enable_bluez4}" = "yes") + # systemd unit support AC_ARG_WITH([systemdsystemunitdir], AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files])) @@ -814,6 +826,7 @@ echo " modemmanager-1: $with_modem_manager_1" echo " concheck: $enable_concheck" echo " libndp: $libndp_location" echo " libteamdctl: $enable_teamdctl" +echo " bluez 4: $enable_bluez4" echo echo "Configuration plugins" diff --git a/po/POTFILES.in b/po/POTFILES.in index a608f34cf8..e953666cc3 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -36,6 +36,7 @@ libnm-util/nm-setting-wireless.c libnm-util/nm-utils.c policy/org.freedesktop.NetworkManager.policy.in.in src/main.c +src/bluez-manager/nm-bluez-device.c src/dhcp-manager/nm-dhcp-dhclient.c src/dhcp-manager/nm-dhcp-dhclient-utils.c src/dhcp-manager/nm-dhcp-manager.c diff --git a/src/Makefile.am b/src/Makefile.am index b9ca94bb20..7799cbfddd 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,22 @@ nm_sources = \ NetworkManagerUtils.c \ NetworkManagerUtils.h +nm_sources += \ + bluez-manager/nm-bluez-common.h \ + bluez-manager/nm-bluez-device.c \ + bluez-manager/nm-bluez-device.h \ + bluez-manager/nm-bluez-manager.h + +if WITH_BLUEZ4 +nm_sources += \ + bluez-manager/nm-bluez4-adapter.h \ + bluez-manager/nm-bluez4-adapter.c \ + bluez-manager/nm-bluez4-manager.c +else +nm_sources += \ + 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 612962552f..ecc4d70947 100644 --- a/src/bluez-manager/nm-bluez-common.h +++ b/src/bluez-manager/nm-bluez-common.h @@ -21,14 +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_BLUEZ4 + +#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 83175da154..3beb06547f 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 +#include +#include #include #include #include @@ -26,11 +29,14 @@ #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-utils.h" G_DEFINE_TYPE (NMBluezDevice, nm_bluez_device, G_TYPE_OBJECT) @@ -39,18 +45,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; @@ -62,6 +83,7 @@ enum { PROP_CAPABILITIES, PROP_RSSI, PROP_USABLE, + PROP_CONNECTED, LAST_PROP }; @@ -73,6 +95,11 @@ enum { }; static guint signals[LAST_SIGNAL] = { 0 }; + +static void cp_connection_added (NMConnectionProvider *provider, + NMConnection *connection, NMBluezDevice *self); + + /***********************************************************/ const char * @@ -131,13 +158,145 @@ 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, FALSE, &error); + g_signal_handlers_unblock_by_func (priv->provider, cp_connection_added, self); + + if (added) { + g_assert (g_slist_find (priv->connections, 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); @@ -161,6 +320,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; @@ -170,11 +333,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; } @@ -208,6 +371,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); } @@ -236,6 +402,163 @@ 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); + NMBluezDevice *self = NM_BLUEZ_DEVICE (g_async_result_get_source_object (G_ASYNC_RESULT (result))); + 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); +} + +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) { @@ -248,9 +571,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; @@ -265,7 +590,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) @@ -273,47 +766,101 @@ 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 -get_properties_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) +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 *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", @@ -324,12 +871,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; @@ -339,8 +881,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; @@ -361,7 +902,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); @@ -370,6 +911,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); +} /********************************************************************/ @@ -378,7 +962,9 @@ nm_bluez_device_new (const char *path, NMConnectionProvider *provider) { NMBluezDevice *self; NMBluezDevicePrivate *priv; +#if WITH_BLUEZ4 DBusGConnection *connection; +#endif g_return_val_if_fail (path != NULL, NULL); g_return_val_if_fail (provider != NULL, NULL); @@ -413,23 +999,41 @@ 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 connection = nm_dbus_manager_get_connection (nm_dbus_manager_get ()); - 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); dbus_g_object_register_marshaller (g_cclosure_marshal_generic, 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; } @@ -453,6 +1057,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); } @@ -464,7 +1073,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); } @@ -494,6 +1108,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; @@ -579,12 +1196,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, NULL, + NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); } diff --git a/src/bluez-manager/nm-bluez-device.h b/src/bluez-manager/nm-bluez-device.h index 18f9fc30a6..11add38901 100644 --- a/src/bluez-manager/nm-bluez-device.h +++ b/src/bluez-manager/nm-bluez-device.h @@ -23,7 +23,9 @@ #include #include +#include +#include #include "nm-connection.h" #include "nm-connection-provider.h" @@ -40,6 +42,7 @@ #define NM_BLUEZ_DEVICE_CAPABILITIES "capabilities" #define NM_BLUEZ_DEVICE_RSSI "rssi" #define NM_BLUEZ_DEVICE_USABLE "usable" +#define NM_BLUEZ_DEVICE_CONNECTED "connected" typedef struct { GObject parent; @@ -74,5 +77,21 @@ guint32 nm_bluez_device_get_capabilities (NMBluezDevice *self); gint nm_bluez_device_get_rssi (NMBluezDevice *self); +gboolean nm_bluez_device_get_connected (NMBluezDevice *self); + +void +nm_bluez_device_connect_async (NMBluezDevice *self, + NMBluetoothCapabilities connection_bt_type, + GAsyncReadyCallback callback, + gpointer user_data); + +const char * +nm_bluez_device_connect_finish (NMBluezDevice *self, + GAsyncResult *result, + GError **error); + +void +nm_bluez_device_disconnect (NMBluezDevice *self); + #endif /* NM_BLUEZ_DEVICE_H */ diff --git a/src/bluez-manager/nm-bluez-manager.c b/src/bluez-manager/nm-bluez-manager.c index 78a41f8527..3801d5ee37 100644 --- a/src/bluez-manager/nm-bluez-manager.c +++ b/src/bluez-manager/nm-bluez-manager.c @@ -17,20 +17,20 @@ * * Copyright (C) 2007 - 2008 Novell, Inc. * Copyright (C) 2007 - 2012 Red Hat, Inc. + * Copyright (C) 2013 Intel Corporation. */ #include #include #include -#include +#include #include "nm-logging.h" -#include "nm-dbus-glib-types.h" #include "nm-bluez-manager.h" -#include "nm-bluez-adapter.h" -#include "nm-dbus-manager.h" +#include "nm-bluez-device.h" #include "nm-bluez-common.h" +#include "nm-dbus-manager.h" typedef struct { NMDBusManager *dbus_mgr; @@ -38,9 +38,9 @@ typedef struct { NMConnectionProvider *provider; - DBusGProxy *proxy; + GDBusProxy *proxy; - NMBluezAdapter *adapter; + GHashTable *devices; } NMBluezManagerPrivate; #define NM_BLUEZ_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_BLUEZ_MANAGER, NMBluezManagerPrivate)) @@ -56,11 +56,14 @@ enum { static guint signals[LAST_SIGNAL] = { 0 }; -static void +static void device_initialized (NMBluezDevice *device, gboolean success, NMBluezManager *self); +static void device_usable (NMBluezDevice *device, GParamSpec *pspec, NMBluezManager *self); +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), @@ -71,173 +74,216 @@ void nm_bluez_manager_query_devices (NMBluezManager *self) { NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); - GSList *devices, *iter; + NMBluezDevice *device; + GHashTableIter iter; - if (!priv->adapter) - return; - - devices = nm_bluez_adapter_get_devices (priv->adapter); - for (iter = devices; iter; iter = g_slist_next (iter)) - emit_bdaddr_added (self, NM_BLUEZ_DEVICE (iter->data)); - g_slist_free (devices); -} - -static void -device_added (NMBluezAdapter *adapter, NMBluezDevice *device, gpointer user_data) -{ - emit_bdaddr_added (NM_BLUEZ_MANAGER (user_data), device); -} - -static void -device_removed (NMBluezAdapter *adapter, NMBluezDevice *device, gpointer user_data) -{ - NMBluezManager *self = NM_BLUEZ_MANAGER (user_data); - - g_signal_emit (self, signals[BDADDR_REMOVED], 0, - nm_bluez_device_get_address (device), - nm_bluez_device_get_path (device)); -} - -static void -adapter_initialized (NMBluezAdapter *adapter, gboolean success, gpointer user_data) -{ - NMBluezManager *self = NM_BLUEZ_MANAGER (user_data); - NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); - - if (success) { - GSList *devices, *iter; - - devices = nm_bluez_adapter_get_devices (adapter); - for (iter = devices; iter; iter = g_slist_next (iter)) - emit_bdaddr_added (self, NM_BLUEZ_DEVICE (iter->data)); - g_slist_free (devices); - - g_signal_connect (adapter, "device-added", G_CALLBACK (device_added), self); - g_signal_connect (adapter, "device-removed", G_CALLBACK (device_removed), self); - } else { - g_object_unref (priv->adapter); - priv->adapter = NULL; + 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 -adapter_removed (DBusGProxy *proxy, const char *path, NMBluezManager *self) +remove_device (NMBluezManager *self, NMBluezDevice *device) +{ + if (nm_bluez_device_get_usable (device)) { + g_signal_emit (self, signals[BDADDR_REMOVED], 0, + nm_bluez_device_get_address (device), + nm_bluez_device_get_path (device)); + } + g_signal_handlers_disconnect_by_func (device, G_CALLBACK (device_initialized), self); + g_signal_handlers_disconnect_by_func (device, G_CALLBACK (device_usable), self); +} + +static void +remove_all_devices (NMBluezManager *self) +{ + GHashTableIter iter; + NMBluezDevice *device; + NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); + + g_hash_table_iter_init (&iter, priv->devices); + while (g_hash_table_iter_next (&iter, NULL, (gpointer) &device)) { + g_hash_table_iter_steal (&iter); + remove_device (self, device); + g_object_unref (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); - if (priv->adapter && !strcmp (path, nm_bluez_adapter_get_path (priv->adapter))) { - if (nm_bluez_adapter_get_initialized (priv->adapter)) { - GSList *devices, *iter; + 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)); +} - devices = nm_bluez_adapter_get_devices (priv->adapter); - for (iter = devices; iter; iter = g_slist_next (iter)) { - NMBluezDevice *device = NM_BLUEZ_DEVICE (iter->data); +static void +device_added (GDBusProxy *proxy, const gchar *path, NMBluezManager *self) +{ + NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); + NMBluezDevice *device; - g_signal_emit (self, signals[BDADDR_REMOVED], 0, - nm_bluez_device_get_address (device), - nm_bluez_device_get_path (device)); + device = nm_bluez_device_new (path, priv->provider); + 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_hash_table_steal (priv->devices, nm_bluez_device_get_path (device)); + remove_device (NM_BLUEZ_MANAGER (self), 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_slist_free (devices); } - g_object_unref (priv->adapter); - priv->adapter = NULL; + 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 -default_adapter_changed (DBusGProxy *proxy, const char *path, NMBluezManager *self) +get_managed_objects_cb (GDBusProxy *proxy, + GAsyncResult *res, + NMBluezManager *self) { - NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); - const char *cur_path = NULL; + GVariant *variant, *ifaces; + GVariantIter i; + GError *error = NULL; + const char *path; - if (priv->adapter) - cur_path = nm_bluez_adapter_get_path (priv->adapter); + variant = g_dbus_proxy_call_finish (proxy, res, &error); - if (cur_path) { - if (!path || strcmp (path, cur_path)) { - /* Default adapter changed */ - adapter_removed (priv->proxy, cur_path, self); - } else { - /* This adapter is already the default */ - return; + 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); } } - /* Add the new default adapter */ - if (path) { - priv->adapter = nm_bluez_adapter_new (path, priv->provider); - g_signal_connect (priv->adapter, "initialized", G_CALLBACK (adapter_initialized), self); - } + g_variant_unref (variant); } static void -default_adapter_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) +on_proxy_acquired (GObject *object, + GAsyncResult *res, + NMBluezManager *self) { - NMBluezManager *self = NM_BLUEZ_MANAGER (user_data); NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); - const char *default_adapter = NULL; - GError *err = NULL; + GError *error = NULL; - if (!dbus_g_proxy_end_call (proxy, call, &err, - DBUS_TYPE_G_OBJECT_PATH, &default_adapter, - G_TYPE_INVALID)) { - /* Ignore "No such adapter" errors; just means bluetooth isn't active */ - if ( !dbus_g_error_has_name (err, "org.bluez.Error.NoSuchAdapter") - && !dbus_g_error_has_name (err, "org.freedesktop.systemd1.LoadFailed") - && !g_error_matches (err, DBUS_GERROR, DBUS_GERROR_SERVICE_UNKNOWN)) { - nm_log_warn (LOGD_BT, "bluez error getting default adapter: %s", - err && err->message ? err->message : "(unknown)"); - } - g_error_free (err); + 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; } - default_adapter_changed (priv->proxy, default_adapter, self); -} + /* 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); -static void -query_default_adapter (NMBluezManager *self) -{ - NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); - DBusGProxyCall *call; - - call = dbus_g_proxy_begin_call (priv->proxy, "DefaultAdapter", - default_adapter_cb, - self, - NULL, G_TYPE_INVALID); - if (!call) - nm_log_warn (LOGD_BT, "failed to request default Bluetooth adapter."); + 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); - DBusGConnection *connection; g_return_if_fail (priv->proxy == NULL); - connection = nm_dbus_manager_get_connection (priv->dbus_mgr); - if (!connection) - return; - - priv->proxy = dbus_g_proxy_new_for_name (connection, - BLUEZ_SERVICE, - BLUEZ_MANAGER_PATH, - BLUEZ_MANAGER_INTERFACE); - - dbus_g_proxy_add_signal (priv->proxy, "AdapterRemoved", - DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID); - dbus_g_proxy_connect_signal (priv->proxy, "AdapterRemoved", - G_CALLBACK (adapter_removed), self, NULL); - - dbus_g_proxy_add_signal (priv->proxy, "DefaultAdapterChanged", - DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID); - dbus_g_proxy_connect_signal (priv->proxy, "DefaultAdapterChanged", - G_CALLBACK (default_adapter_changed), self, NULL); - - query_default_adapter (self); + 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 @@ -256,14 +302,9 @@ name_owner_changed_cb (NMDBusManager *dbus_mgr, if (strcmp (BLUEZ_SERVICE, name)) return; - if (!old_owner_good && new_owner_good) - query_default_adapter (self); - else if (old_owner_good && !new_owner_good) { - /* Throwing away the adapter removes all devices too */ - if (priv->adapter) { - g_object_unref (priv->adapter); - priv->adapter = NULL; - } + if (old_owner_good && !new_owner_good) { + if (priv->devices) + remove_all_devices (self); } } @@ -277,10 +318,10 @@ bluez_cleanup (NMBluezManager *self, gboolean do_signal) priv->proxy = NULL; } - if (priv->adapter) { - g_object_unref (priv->adapter); - priv->adapter = NULL; - } + if (do_signal) + remove_all_devices (self); + else + g_hash_table_remove_all (priv->devices); } static void @@ -334,6 +375,9 @@ nm_bluez_manager_init (NMBluezManager *self) self); bluez_connect (self); + + priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal, + NULL, g_object_unref); } static void @@ -353,6 +397,16 @@ dispose (GObject *object) 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) { @@ -362,6 +416,7 @@ nm_bluez_manager_class_init (NMBluezManagerClass *klass) /* virtual methods */ object_class->dispose = dispose; + object_class->finalize = finalize; /* Signals */ signals[BDADDR_ADDED] = @@ -370,7 +425,8 @@ nm_bluez_manager_class_init (NMBluezManagerClass *klass) G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (NMBluezManagerClass, bdaddr_added), NULL, NULL, NULL, - G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT); + 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, @@ -380,4 +436,3 @@ nm_bluez_manager_class_init (NMBluezManagerClass *klass) NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING); } - diff --git a/src/bluez-manager/nm-bluez-manager.h b/src/bluez-manager/nm-bluez-manager.h index 2bfc971eb8..95e319c5e0 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 diff --git a/src/bluez-manager/nm-bluez-adapter.c b/src/bluez-manager/nm-bluez4-adapter.c similarity index 99% rename from src/bluez-manager/nm-bluez-adapter.c rename to src/bluez-manager/nm-bluez4-adapter.c index caa50b0f1d..e9f806f71b 100644 --- a/src/bluez-manager/nm-bluez-adapter.c +++ b/src/bluez-manager/nm-bluez4-adapter.c @@ -23,7 +23,7 @@ #include "NetworkManager.h" #include "nm-dbus-manager.h" -#include "nm-bluez-adapter.h" +#include "nm-bluez4-adapter.h" #include "nm-bluez-device.h" #include "nm-bluez-common.h" #include "nm-dbus-glib-types.h" diff --git a/src/bluez-manager/nm-bluez-adapter.h b/src/bluez-manager/nm-bluez4-adapter.h similarity index 100% rename from src/bluez-manager/nm-bluez-adapter.h rename to src/bluez-manager/nm-bluez4-adapter.h diff --git a/src/bluez-manager/nm-bluez4-manager.c b/src/bluez-manager/nm-bluez4-manager.c new file mode 100644 index 0000000000..882028cd04 --- /dev/null +++ b/src/bluez-manager/nm-bluez4-manager.c @@ -0,0 +1,385 @@ +/* -*- 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. + */ + +#include +#include +#include +#include + +#include "nm-logging.h" +#include "nm-dbus-glib-types.h" +#include "nm-bluez-manager.h" +#include "nm-bluez4-adapter.h" +#include "nm-dbus-manager.h" +#include "nm-bluez-common.h" + + +typedef struct { + NMDBusManager *dbus_mgr; + gulong name_owner_changed_id; + + NMConnectionProvider *provider; + + DBusGProxy *proxy; + + NMBluezAdapter *adapter; +} 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); + GSList *devices, *iter; + + if (!priv->adapter) + return; + + devices = nm_bluez_adapter_get_devices (priv->adapter); + for (iter = devices; iter; iter = g_slist_next (iter)) + emit_bdaddr_added (self, NM_BLUEZ_DEVICE (iter->data)); + g_slist_free (devices); +} + +static void +device_added (NMBluezAdapter *adapter, NMBluezDevice *device, gpointer user_data) +{ + emit_bdaddr_added (NM_BLUEZ_MANAGER (user_data), device); +} + +static void +device_removed (NMBluezAdapter *adapter, NMBluezDevice *device, gpointer user_data) +{ + NMBluezManager *self = NM_BLUEZ_MANAGER (user_data); + + g_signal_emit (self, signals[BDADDR_REMOVED], 0, + nm_bluez_device_get_address (device), + nm_bluez_device_get_path (device)); +} + +static void +adapter_initialized (NMBluezAdapter *adapter, gboolean success, gpointer user_data) +{ + NMBluezManager *self = NM_BLUEZ_MANAGER (user_data); + NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); + + if (success) { + GSList *devices, *iter; + + devices = nm_bluez_adapter_get_devices (adapter); + for (iter = devices; iter; iter = g_slist_next (iter)) + emit_bdaddr_added (self, NM_BLUEZ_DEVICE (iter->data)); + g_slist_free (devices); + + g_signal_connect (adapter, "device-added", G_CALLBACK (device_added), self); + g_signal_connect (adapter, "device-removed", G_CALLBACK (device_removed), self); + } else { + g_object_unref (priv->adapter); + priv->adapter = NULL; + } +} + +static void +adapter_removed (DBusGProxy *proxy, const char *path, NMBluezManager *self) +{ + NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); + + if (priv->adapter && !strcmp (path, nm_bluez_adapter_get_path (priv->adapter))) { + if (nm_bluez_adapter_get_initialized (priv->adapter)) { + GSList *devices, *iter; + + devices = nm_bluez_adapter_get_devices (priv->adapter); + for (iter = devices; iter; iter = g_slist_next (iter)) { + NMBluezDevice *device = NM_BLUEZ_DEVICE (iter->data); + + g_signal_emit (self, signals[BDADDR_REMOVED], 0, + nm_bluez_device_get_address (device), + nm_bluez_device_get_path (device)); + } + g_slist_free (devices); + } + + g_object_unref (priv->adapter); + priv->adapter = NULL; + } +} + +static void +default_adapter_changed (DBusGProxy *proxy, const char *path, NMBluezManager *self) +{ + NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); + const char *cur_path = NULL; + + if (priv->adapter) + cur_path = nm_bluez_adapter_get_path (priv->adapter); + + if (cur_path) { + if (!path || strcmp (path, cur_path)) { + /* Default adapter changed */ + adapter_removed (priv->proxy, cur_path, self); + } else { + /* This adapter is already the default */ + return; + } + } + + /* Add the new default adapter */ + if (path) { + priv->adapter = nm_bluez_adapter_new (path, priv->provider); + g_signal_connect (priv->adapter, "initialized", G_CALLBACK (adapter_initialized), self); + } +} + +static void +default_adapter_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data) +{ + NMBluezManager *self = NM_BLUEZ_MANAGER (user_data); + NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); + const char *default_adapter = NULL; + GError *err = NULL; + + if (!dbus_g_proxy_end_call (proxy, call, &err, + DBUS_TYPE_G_OBJECT_PATH, &default_adapter, + G_TYPE_INVALID)) { + /* Ignore "No such adapter" errors; just means bluetooth isn't active */ + if ( !dbus_g_error_has_name (err, "org.bluez.Error.NoSuchAdapter") + && !dbus_g_error_has_name (err, "org.freedesktop.systemd1.LoadFailed") + && !g_error_matches (err, DBUS_GERROR, DBUS_GERROR_SERVICE_UNKNOWN)) { + nm_log_warn (LOGD_BT, "bluez error getting default adapter: %s", + err && err->message ? err->message : "(unknown)"); + } + g_error_free (err); + return; + } + + default_adapter_changed (priv->proxy, default_adapter, self); +} + +static void +query_default_adapter (NMBluezManager *self) +{ + NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); + DBusGProxyCall *call; + + call = dbus_g_proxy_begin_call (priv->proxy, "DefaultAdapter", + default_adapter_cb, + self, + NULL, G_TYPE_INVALID); + if (!call) + nm_log_warn (LOGD_BT, "failed to request default Bluetooth adapter."); +} + +static void +bluez_connect (NMBluezManager *self) +{ + NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); + DBusGConnection *connection; + + g_return_if_fail (priv->proxy == NULL); + + connection = nm_dbus_manager_get_connection (priv->dbus_mgr); + if (!connection) + return; + + priv->proxy = dbus_g_proxy_new_for_name (connection, + BLUEZ_SERVICE, + BLUEZ_MANAGER_PATH, + BLUEZ_MANAGER_INTERFACE); + + dbus_g_proxy_add_signal (priv->proxy, "AdapterRemoved", + DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->proxy, "AdapterRemoved", + G_CALLBACK (adapter_removed), self, NULL); + + dbus_g_proxy_add_signal (priv->proxy, "DefaultAdapterChanged", + DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->proxy, "DefaultAdapterChanged", + G_CALLBACK (default_adapter_changed), self, NULL); + + query_default_adapter (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) + query_default_adapter (self); + else if (old_owner_good && !new_owner_good) { + /* Throwing away the adapter removes all devices too */ + if (priv->adapter) { + g_object_unref (priv->adapter); + priv->adapter = NULL; + } + } +} + +static void +bluez_cleanup (NMBluezManager *self, gboolean do_signal) +{ + NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self); + + if (priv->proxy) { + g_object_unref (priv->proxy); + priv->proxy = NULL; + } + + if (priv->adapter) { + g_object_unref (priv->adapter); + priv->adapter = NULL; + } +} + +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 (NMConnectionProvider *provider) +{ + static NMBluezManager *singleton = NULL; + + if (singleton) + return g_object_ref (singleton); + + singleton = (NMBluezManager *) g_object_new (NM_TYPE_BLUEZ_MANAGER, NULL); + g_assert (singleton); + + /* Cache the connection provider for NMBluezAdapter objects */ + NM_BLUEZ_MANAGER_GET_PRIVATE (singleton)->provider = provider; + + 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); +} + +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 +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; + + /* 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/devices/nm-device-bt.c b/src/devices/nm-device-bt.c index 09c1284c42..b0be0fbfc3 100644 --- a/src/devices/nm-device-bt.c +++ b/src/devices/nm-device-bt.c @@ -26,9 +26,11 @@ #include #include +#include #include "nm-glib-compat.h" #include "nm-bluez-common.h" +#include "nm-bluez-device.h" #include "nm-dbus-manager.h" #include "nm-device-bt.h" #include "nm-device-private.h" @@ -47,8 +49,6 @@ #define MM_OLD_DBUS_SERVICE "org.freedesktop.ModemManager" #define MM_NEW_DBUS_SERVICE "org.freedesktop.ModemManager1" -#define BLUETOOTH_DUN_UUID "dun" -#define BLUETOOTH_NAP_UUID "nap" G_DEFINE_TYPE (NMDeviceBt, nm_device_bt, NM_TYPE_DEVICE) @@ -61,6 +61,8 @@ typedef struct { guint mm_watch_id; gboolean mm_running; + NMBluezDevice *bt_device; + char *bdaddr; char *name; guint32 capabilities; @@ -68,9 +70,6 @@ typedef struct { gboolean connected; gboolean have_iface; - DBusGProxy *type_proxy; - DBusGProxy *dev_proxy; - char *rfcomm_iface; NMModem *modem; guint32 timeout_id; @@ -82,6 +81,7 @@ enum { PROP_0, PROP_BT_NAME, PROP_BT_CAPABILITIES, + PROP_BT_DEVICE, LAST_PROP }; @@ -708,18 +708,19 @@ check_connect_continue (NMDeviceBt *self) } static void -bluez_connect_cb (DBusGProxy *proxy, - DBusGProxyCall *call_id, +bluez_connect_cb (GObject *object, + GAsyncResult *res, void *user_data) { NMDeviceBt *self = NM_DEVICE_BT (user_data); NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self); GError *error = NULL; - char *device; + const char *device; - if (dbus_g_proxy_end_call (proxy, call_id, &error, - G_TYPE_STRING, &device, - G_TYPE_INVALID) == FALSE) { + device = nm_bluez_device_connect_finish (NM_BLUEZ_DEVICE (object), + res, &error); + + if (!device) { nm_log_warn (LOGD_BT, "Error connecting with bluez: %s", error && error->message ? error->message : "(unknown)"); g_clear_error (&error); @@ -730,20 +731,11 @@ bluez_connect_cb (DBusGProxy *proxy, return; } - if (!device || !strlen (device)) { - nm_log_warn (LOGD_BT, "Invalid network device returned by bluez"); - - nm_device_state_changed (NM_DEVICE (self), - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_BT_FAILED); - } - if (priv->bt_type == NM_BT_CAPABILITY_DUN) { g_free (priv->rfcomm_iface); - priv->rfcomm_iface = device; + priv->rfcomm_iface = g_strdup (device); } else if (priv->bt_type == NM_BT_CAPABILITY_NAP) { nm_device_set_ip_iface (NM_DEVICE (self), device); - g_free (device); } nm_log_dbg (LOGD_BT, "(%s): connect request successful", @@ -755,33 +747,17 @@ bluez_connect_cb (DBusGProxy *proxy, } static void -bluez_property_changed (DBusGProxy *proxy, - const char *property, - GValue *value, - gpointer user_data) +bluez_connected_changed (NMBluezDevice *bt_device, + GParamSpec *pspec, + NMDevice *device) { - NMDevice *device = NM_DEVICE (user_data); - NMDeviceBt *self = NM_DEVICE_BT (user_data); + NMDeviceBt *self = NM_DEVICE_BT (device); NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self); gboolean connected; NMDeviceState state; - const char *prop_str = "(unknown)"; - - if (G_VALUE_HOLDS_STRING (value)) - prop_str = g_value_get_string (value); - else if (G_VALUE_HOLDS_BOOLEAN (value)) - prop_str = g_value_get_boolean (value) ? "true" : "false"; - - nm_log_dbg (LOGD_BT, "(%s): bluez property '%s' changed to '%s'", - nm_device_get_iface (device), - property, - prop_str); - - if (strcmp (property, "Connected")) - return; state = nm_device_get_state (device); - connected = g_value_get_boolean (value); + connected = nm_bluez_device_get_connected (bt_device); if (connected) { if (state == NM_DEVICE_STATE_CONFIG) { nm_log_dbg (LOGD_BT, "(%s): connected to the device", @@ -832,8 +808,6 @@ static NMActStageReturn act_stage2_config (NMDevice *device, NMDeviceStateReason *reason) { NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device); - DBusGConnection *bus; - gboolean dun = FALSE; NMConnection *connection; connection = nm_device_get_connection (device); @@ -849,53 +823,13 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason) return NM_ACT_STAGE_RETURN_FAILURE; } - if (priv->bt_type == NM_BT_CAPABILITY_DUN) - dun = TRUE; - else if (priv->bt_type == NM_BT_CAPABILITY_NAP) - dun = FALSE; - else - g_assert_not_reached (); - - bus = nm_dbus_manager_get_connection (priv->dbus_mgr); - priv->dev_proxy = dbus_g_proxy_new_for_name (bus, - BLUEZ_SERVICE, - nm_device_get_udi (device), - BLUEZ_DEVICE_INTERFACE); - if (!priv->dev_proxy) { - // FIXME: set a reason code - return NM_ACT_STAGE_RETURN_FAILURE; - } - - /* Watch for BT device property changes */ - dbus_g_object_register_marshaller (g_cclosure_marshal_generic, - G_TYPE_NONE, - G_TYPE_STRING, G_TYPE_VALUE, - G_TYPE_INVALID); - dbus_g_proxy_add_signal (priv->dev_proxy, "PropertyChanged", - G_TYPE_STRING, G_TYPE_VALUE, G_TYPE_INVALID); - dbus_g_proxy_connect_signal (priv->dev_proxy, "PropertyChanged", - G_CALLBACK (bluez_property_changed), device, NULL); - - priv->type_proxy = dbus_g_proxy_new_for_name (bus, - BLUEZ_SERVICE, - nm_device_get_udi (device), - dun ? BLUEZ_SERIAL_INTERFACE : BLUEZ_NETWORK_INTERFACE); - if (!priv->type_proxy) { - // FIXME: set a reason code - return NM_ACT_STAGE_RETURN_FAILURE; - } - nm_log_dbg (LOGD_BT, "(%s): requesting connection to the device", nm_device_get_iface (device)); /* Connect to the BT device */ - dbus_g_proxy_begin_call_with_timeout (priv->type_proxy, "Connect", - bluez_connect_cb, - device, - NULL, - 20000, - G_TYPE_STRING, dun ? BLUETOOTH_DUN_UUID : BLUETOOTH_NAP_UUID, - G_TYPE_INVALID); + nm_bluez_device_connect_async (priv->bt_device, + priv->bt_type & (NM_BT_CAPABILITY_DUN | NM_BT_CAPABILITY_NAP), + bluez_connect_cb, device); if (priv->timeout_id) g_source_remove (priv->timeout_id); @@ -946,11 +880,14 @@ static void deactivate (NMDevice *device) { NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device); + gboolean dun; + + dun = priv->bt_type == NM_BT_CAPABILITY_DUN; priv->have_iface = FALSE; priv->connected = FALSE; - if (priv->bt_type == NM_BT_CAPABILITY_DUN) { + if (dun) { if (priv->modem) { nm_modem_deactivate (priv->modem, device); @@ -965,32 +902,9 @@ deactivate (NMDevice *device) g_object_unref (priv->modem); priv->modem = NULL; } - - if (priv->type_proxy) { - /* Don't ever pass NULL through dbus; rfcomm_iface - * might happen to be NULL for some reason. - */ - if (priv->rfcomm_iface) { - dbus_g_proxy_call_no_reply (priv->type_proxy, "Disconnect", - G_TYPE_STRING, priv->rfcomm_iface, - G_TYPE_INVALID); - } - g_object_unref (priv->type_proxy); - priv->type_proxy = NULL; - } - } else if (priv->bt_type == NM_BT_CAPABILITY_NAP) { - if (priv->type_proxy) { - dbus_g_proxy_call_no_reply (priv->type_proxy, "Disconnect", - G_TYPE_INVALID); - g_object_unref (priv->type_proxy); - priv->type_proxy = NULL; - } } - if (priv->dev_proxy) { - g_object_unref (priv->dev_proxy); - priv->dev_proxy = NULL; - } + nm_bluez_device_disconnect (priv->bt_device); if (priv->timeout_id) { g_source_remove (priv->timeout_id); @@ -1111,7 +1025,8 @@ mm_name_owner_changed (NMDBusManager *dbus_mgr, /*****************************************************************************/ NMDevice * -nm_device_bt_new (const char *udi, +nm_device_bt_new (NMBluezDevice *bt_device, + const char *udi, const char *bdaddr, const char *name, guint32 capabilities) @@ -1120,12 +1035,14 @@ nm_device_bt_new (const char *udi, g_return_val_if_fail (bdaddr != NULL, NULL); g_return_val_if_fail (name != NULL, NULL); g_return_val_if_fail (capabilities != NM_BT_CAPABILITY_NONE, NULL); + g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (bt_device), NULL); return (NMDevice *) g_object_new (NM_TYPE_DEVICE_BT, NM_DEVICE_UDI, udi, NM_DEVICE_IFACE, bdaddr, NM_DEVICE_DRIVER, "bluez", NM_DEVICE_HW_ADDRESS, bdaddr, + NM_DEVICE_BT_DEVICE, bt_device, NM_DEVICE_BT_NAME, name, NM_DEVICE_BT_CAPABILITIES, capabilities, NM_DEVICE_TYPE_DESC, "Bluetooth", @@ -1168,6 +1085,11 @@ constructed (GObject *object) g_assert (my_hwaddr); g_assert_cmpint (my_hwaddr_len, ==, ETH_ALEN); priv->bdaddr = nm_utils_hwaddr_ntoa (my_hwaddr, ARPHRD_ETHER); + + /* Watch for BT device property changes */ + g_signal_connect (priv->bt_device, "notify::" NM_BLUEZ_DEVICE_CONNECTED, + G_CALLBACK (bluez_connected_changed), + object); } static void @@ -1185,6 +1107,10 @@ set_property (GObject *object, guint prop_id, /* Construct only */ priv->capabilities = g_value_get_uint (value); break; + case PROP_BT_DEVICE: + /* Construct only */ + priv->bt_device = g_value_dup_object (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1204,6 +1130,9 @@ get_property (GObject *object, guint prop_id, case PROP_BT_CAPABILITIES: g_value_set_uint (value, priv->capabilities); break; + case PROP_BT_DEVICE: + g_value_set_object (value, priv->bt_device); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1220,15 +1149,18 @@ dispose (GObject *object) priv->timeout_id = 0; } + g_signal_handlers_disconnect_by_func (priv->bt_device, + G_CALLBACK (bluez_connected_changed), + object); + if (priv->dbus_mgr && priv->mm_watch_id) { g_signal_handler_disconnect (priv->dbus_mgr, priv->mm_watch_id); priv->mm_watch_id = 0; } priv->dbus_mgr = NULL; - g_clear_object (&priv->type_proxy); - g_clear_object (&priv->dev_proxy); g_clear_object (&priv->modem); + g_clear_object (&priv->bt_device); G_OBJECT_CLASS (nm_device_bt_parent_class)->dispose (object); } @@ -1289,6 +1221,14 @@ nm_device_bt_class_init (NMDeviceBtClass *klass) NM_BT_CAPABILITY_NONE, G_MAXUINT, NM_BT_CAPABILITY_NONE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property + (object_class, PROP_BT_DEVICE, + g_param_spec_object (NM_DEVICE_BT_DEVICE, + "NMBluezDevice object for the Device", + "NMBluezDevice object for the Device", + NM_TYPE_BLUEZ_DEVICE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + /* Signals */ signals[PPP_STATS] = g_signal_new ("ppp-stats", diff --git a/src/devices/nm-device-bt.h b/src/devices/nm-device-bt.h index bcc9f822bc..d983285fb7 100644 --- a/src/devices/nm-device-bt.h +++ b/src/devices/nm-device-bt.h @@ -22,6 +22,7 @@ #define NM_DEVICE_BT_H #include +#include "nm-bluez-device.h" #include "nm-modem.h" G_BEGIN_DECLS @@ -41,6 +42,7 @@ typedef enum { #define NM_DEVICE_BT_NAME "name" #define NM_DEVICE_BT_CAPABILITIES "bt-capabilities" +#define NM_DEVICE_BT_DEVICE "bt-device" typedef struct { NMDevice parent; @@ -55,7 +57,8 @@ typedef struct { GType nm_device_bt_get_type (void); -NMDevice *nm_device_bt_new (const char *udi, +NMDevice *nm_device_bt_new (NMBluezDevice *bt_device, + const char *udi, const char *bdaddr, const char *name, guint32 capabilities); diff --git a/src/nm-connection-provider.c b/src/nm-connection-provider.c index 401f8dabb7..467d99d2b1 100644 --- a/src/nm-connection-provider.c +++ b/src/nm-connection-provider.c @@ -40,6 +40,27 @@ nm_connection_provider_get_connections (NMConnectionProvider *self) return NULL; } +gboolean +nm_connection_provider_has_connections_loaded (NMConnectionProvider *self) +{ + g_return_val_if_fail (NM_IS_CONNECTION_PROVIDER (self), FALSE); + + g_assert (NM_CONNECTION_PROVIDER_GET_INTERFACE (self)->has_connections_loaded); + return NM_CONNECTION_PROVIDER_GET_INTERFACE (self)->has_connections_loaded (self); +} + +NMConnection * +nm_connection_provider_add_connection (NMConnectionProvider *self, + NMConnection *connection, + gboolean save_to_disk, + GError **error) +{ + g_return_val_if_fail (NM_IS_CONNECTION_PROVIDER (self), NULL); + + g_assert (NM_CONNECTION_PROVIDER_GET_INTERFACE (self)->add_connection); + return NM_CONNECTION_PROVIDER_GET_INTERFACE (self)->add_connection (self, connection, save_to_disk, error); +} + /*****************************************************************************/ static void diff --git a/src/nm-connection-provider.h b/src/nm-connection-provider.h index 942651f76b..2afbc6d65c 100644 --- a/src/nm-connection-provider.h +++ b/src/nm-connection-provider.h @@ -58,6 +58,13 @@ struct _NMConnectionProvider { const GSList * (*get_connections) (NMConnectionProvider *self); + gboolean (*has_connections_loaded) (NMConnectionProvider *self); + + NMConnection * (*add_connection) (NMConnectionProvider *self, + NMConnection *connection, + gboolean save_to_disk, + GError **error); + /* Signals */ void (*connection_added) (NMConnectionProvider *self, NMConnection *connection); @@ -103,4 +110,31 @@ GSList *nm_connection_provider_get_best_connections (NMConnectionProvider *self, */ const GSList *nm_connection_provider_get_connections (NMConnectionProvider *self); +/** + * nm_connection_provider_has_connections_loaded: + * @self: the #NMConnectionProvider + * + * Returns: TRUE or FALSE indicating whether the connections of the provider are already + * loaded. If they are not yet loaded, the provider will not emit the signals + * NM_CP_SIGNAL_CONNECTION_ADDED, NM_CP_SIGNAL_CONNECTION_UPDATED and + * NM_CP_SIGNAL_CONNECTION_REMOVED until NM_CP_SIGNAL_CONNECTIONS_LOADED gets + * emited. + */ +gboolean nm_connection_provider_has_connections_loaded (NMConnectionProvider *self); + + +/** + * nm_connection_provider_add_connection: + * @self: the #NMConnectionProvider + * @connection: the connection to be added + * @save_to_disk: whether to store the connection on disk + * @error: returns any error if adding fails + * + * returns: a newly added #NMConnection. + */ +NMConnection *nm_connection_provider_add_connection (NMConnectionProvider *self, + NMConnection *connection, + gboolean save_to_disk, + GError **error); + #endif /* NM_CONNECTION_PROVIDER_H */ diff --git a/src/nm-manager.c b/src/nm-manager.c index 11e3b5e0b1..f395f03302 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -139,6 +139,7 @@ static void impl_manager_check_connectivity (NMManager *manager, #include "nm-manager-glue.h" static void bluez_manager_bdaddr_added_cb (NMBluezManager *bluez_mgr, + NMBluezDevice *bt_device, const char *bdaddr, const char *name, const char *object_path, @@ -2184,6 +2185,7 @@ add_device (NMManager *self, NMDevice *device) static void bluez_manager_bdaddr_added_cb (NMBluezManager *bluez_mgr, + NMBluezDevice *bt_device, const char *bdaddr, const char *name, const char *object_path, @@ -2198,12 +2200,13 @@ bluez_manager_bdaddr_added_cb (NMBluezManager *bluez_mgr, g_return_if_fail (name != NULL); g_return_if_fail (object_path != NULL); g_return_if_fail (capabilities != NM_BT_CAPABILITY_NONE); + g_return_if_fail (NM_IS_BLUEZ_DEVICE (bt_device)); /* Make sure the device is not already in the device list */ if (nm_manager_get_device_by_udi (manager, object_path)) return; - device = nm_device_bt_new (object_path, bdaddr, name, capabilities); + device = nm_device_bt_new (bt_device, object_path, bdaddr, name, capabilities); if (device) { nm_log_info (LOGD_HW, "BT device %s (%s) added (%s%s%s)", name, diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c index b18a9af9ca..2d3e278351 100644 --- a/src/settings/nm-settings.c +++ b/src/settings/nm-settings.c @@ -930,6 +930,16 @@ nm_settings_add_connection (NMSettings *self, return NULL; } +static NMConnection * +_nm_connection_provider_add_connection (NMConnectionProvider *provider, + NMConnection *connection, + gboolean save_to_disk, + GError **error) +{ + g_assert (NM_IS_CONNECTION_PROVIDER (provider) && NM_IS_SETTINGS (provider)); + return NM_CONNECTION (nm_settings_add_connection (NM_SETTINGS (provider), connection, save_to_disk, error)); +} + static gboolean secrets_filter_cb (NMSetting *setting, const char *secret, @@ -1635,6 +1645,14 @@ get_connections (NMConnectionProvider *provider) return g_slist_reverse (list); } +static gboolean +has_connections_loaded (NMConnectionProvider *provider) +{ + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (provider); + + return priv->connections_loaded; +} + /***************************************************************/ NMSettings * @@ -1667,6 +1685,8 @@ connection_provider_init (NMConnectionProvider *cp_class) { cp_class->get_best_connections = get_best_connections; cp_class->get_connections = get_connections; + cp_class->has_connections_loaded = has_connections_loaded; + cp_class->add_connection = _nm_connection_provider_add_connection; } static void