summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2019-08-11 10:43:53 +0200
committerThomas Haller <thaller@redhat.com>2019-09-23 12:47:37 +0200
commit4154d9618c904c2286b332b56f3515806cb1bb3b (patch)
tree5fd7a28f36359ad9c659aa04adeac15a86559b5c
parent878d4963ed7e3058c13f03dc1b2978fce1bed9c6 (diff)
downloadNetworkManager-4154d9618c904c2286b332b56f3515806cb1bb3b.tar.gz
bluetooth: refactor BlueZ handling and let NMBluezManager cache ObjectManager data
This is a complete refactoring of the bluetooth code. Now that BlueZ 4 support was dropped, the separation of NMBluezManager and NMBluez5Manager makes no sense. They should be merged. At that point, notice that BlueZ 5's D-Bus API is fully centered around D-Bus's ObjectManager interface. Using that interface, we basically only call GetManagedObjects() once and register to InterfacesAdded, InterfacesRemoved and PropertiesChanged signals. There is no need to fetch individual properties ever. Note how NMBluezDevice used to query the D-Bus properties itself by creating a GDBusProxy. This is redundant, because when using the ObjectManager interfaces, we have all information already. Instead, let NMBluezManager basically become the client-side cache of all of BlueZ's ObjectManager interface. NMBluezDevice was mostly concerned about caching the D-Bus interface's state, tracking suitable profiles (pan_connection), and moderate between bluez and NMDeviceBt. These tasks don't get simpler by moving them to a seprate file. Let them also be handled by NMBluezManager. I mean, just look how it was previously: NMBluez5Manager registers to ObjectManager interface and sees a device appearing. It creates a NMBluezDevice object and registers to its "initialized" and "notify:usable" signal. In the meantime, NMBluezDevice fetches the relevant information from D-Bus (although it was already present in the data provided by the ObjectManager) and eventually emits these usable and initialized signals. Then, NMBlue5Manager emits a "bdaddr-added" signal, for which NMBluezManager creates the NMDeviceBt instance. NMBluezManager, NMBluez5Manager and NMBluezDevice are strongly cooperating to the point that it is simpler to merge them. This is not mere refactoring. This patch aims to make everything asynchronously and always cancellable. Also, it aims to fix races and inconsistencies of the state. - Registering to a NAP server now waits for the response and delays activation of the NMDeviceBridge accordingly. - For NAP connections we now watch the bnep0 interface in platform, and tear down the device when it goes away. Bluez doesn't send us a notification on D-Bus in that case. - Rework establishing a DUN connection. It no longer uses blocking connect() and does not block until rfcomm device appears. It's all async now. It also watches the rfcomm file descriptor for POLLERR/POLLHUP to notice disconnect. - drop nm_device_factory_emit_component_added() and instead let NMDeviceBt directly register to the WWan factory's "added" signal.
-rw-r--r--Makefile.am5
-rw-r--r--libnm-core/nm-core-internal.h4
-rw-r--r--libnm-core/nm-utils.c8
-rw-r--r--po/POTFILES.in2
-rw-r--r--src/devices/bluetooth/meson.build2
-rw-r--r--src/devices/bluetooth/nm-bluez-device.c1207
-rw-r--r--src/devices/bluetooth/nm-bluez-device.h70
-rw-r--r--src/devices/bluetooth/nm-bluez-manager.c2835
-rw-r--r--src/devices/bluetooth/nm-bluez-manager.h39
-rw-r--r--src/devices/bluetooth/nm-bluez5-dun.c910
-rw-r--r--src/devices/bluetooth/nm-bluez5-dun.h41
-rw-r--r--src/devices/bluetooth/nm-bluez5-manager.c585
-rw-r--r--src/devices/bluetooth/nm-bluez5-manager.h27
-rw-r--r--src/devices/bluetooth/nm-device-bt.c855
-rw-r--r--src/devices/bluetooth/nm-device-bt.h34
-rw-r--r--src/devices/bluetooth/tests/nm-bt-test.c202
-rw-r--r--src/devices/nm-device-bridge.c98
-rw-r--r--src/devices/nm-device-bridge.h3
-rw-r--r--src/devices/nm-device-factory.c22
-rw-r--r--src/devices/nm-device-factory.h35
-rw-r--r--src/devices/nm-device.c40
-rw-r--r--src/devices/nm-device.h33
-rw-r--r--src/devices/wwan/nm-modem.c7
-rw-r--r--src/devices/wwan/nm-wwan-factory.c11
-rw-r--r--src/nm-manager.c32
-rw-r--r--src/nm-manager.h2
26 files changed, 4296 insertions, 2813 deletions
diff --git a/Makefile.am b/Makefile.am
index e519cedd2f..55ad993927 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3441,11 +3441,8 @@ $(src_devices_bluetooth_libnm_bluetooth_utils_la_OBJECTS): $(libnm_core_lib_h_pu
core_plugins += src/devices/bluetooth/libnm-device-plugin-bluetooth.la
src_devices_bluetooth_libnm_device_plugin_bluetooth_la_SOURCES = \
- src/devices/bluetooth/nm-bluez-device.c \
- src/devices/bluetooth/nm-bluez-device.h \
src/devices/bluetooth/nm-bluez-manager.c \
- src/devices/bluetooth/nm-bluez5-manager.c \
- src/devices/bluetooth/nm-bluez5-manager.h \
+ src/devices/bluetooth/nm-bluez-manager.h \
src/devices/bluetooth/nm-device-bt.c \
src/devices/bluetooth/nm-device-bt.h \
$(NULL)
diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h
index 37523e2564..9f4f19839f 100644
--- a/libnm-core/nm-core-internal.h
+++ b/libnm-core/nm-core-internal.h
@@ -131,6 +131,10 @@ _nm_setting_secret_flags_valid (NMSettingSecretFlags flags)
/*****************************************************************************/
+const char *nm_bluetooth_capability_to_string (NMBluetoothCapabilities capabilities, char *buf, gsize len);
+
+/*****************************************************************************/
+
typedef enum { /*< skip >*/
NM_SETTING_PARSE_FLAGS_NONE = 0,
NM_SETTING_PARSE_FLAGS_STRICT = 1LL << 0,
diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c
index 5fa0020fa3..ae48b16af4 100644
--- a/libnm-core/nm-utils.c
+++ b/libnm-core/nm-utils.c
@@ -5812,6 +5812,14 @@ nm_utils_version (void)
/*****************************************************************************/
+NM_UTILS_FLAGS2STR_DEFINE (nm_bluetooth_capability_to_string, NMBluetoothCapabilities,
+ NM_UTILS_FLAGS2STR (NM_BT_CAPABILITY_NONE, "NONE"),
+ NM_UTILS_FLAGS2STR (NM_BT_CAPABILITY_DUN, "DUN"),
+ NM_UTILS_FLAGS2STR (NM_BT_CAPABILITY_NAP, "NAP"),
+)
+
+/*****************************************************************************/
+
/**
* nm_utils_base64secret_decode:
* @base64_key: the (possibly invalid) base64 encode key.
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 90ed9ebdbe..d3246bccfb 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -148,7 +148,7 @@ src/dhcp/nm-dhcp-dhclient-utils.c
src/dhcp/nm-dhcp-manager.c
src/dns/nm-dns-manager.c
src/devices/adsl/nm-device-adsl.c
-src/devices/bluetooth/nm-bluez-device.c
+src/devices/bluetooth/nm-bluez-manager.c
src/devices/bluetooth/nm-device-bt.c
src/devices/nm-device-6lowpan.c
src/devices/nm-device-bond.c
diff --git a/src/devices/bluetooth/meson.build b/src/devices/bluetooth/meson.build
index 2def5b74ca..31fbf00674 100644
--- a/src/devices/bluetooth/meson.build
+++ b/src/devices/bluetooth/meson.build
@@ -1,7 +1,5 @@
sources = files(
- 'nm-bluez-device.c',
'nm-bluez-manager.c',
- 'nm-bluez5-manager.c',
'nm-bt-error.c',
'nm-device-bt.c',
)
diff --git a/src/devices/bluetooth/nm-bluez-device.c b/src/devices/bluetooth/nm-bluez-device.c
deleted file mode 100644
index 072582ed87..0000000000
--- a/src/devices/bluetooth/nm-bluez-device.c
+++ /dev/null
@@ -1,1207 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/* NetworkManager -- Network link manager
- *
- * Copyright (C) 2009 - 2012 Red Hat, Inc.
- * Copyright (C) 2013 Intel Corporation.
- */
-
-#include "nm-default.h"
-
-#include "nm-bluez-device.h"
-
-#include "nm-core-internal.h"
-#include "nm-bt-error.h"
-#include "nm-bluez-common.h"
-#include "settings/nm-settings.h"
-#include "settings/nm-settings-connection.h"
-#include "NetworkManagerUtils.h"
-
-#if WITH_BLUEZ5_DUN
-#include "nm-bluez5-dun.h"
-#endif
-
-/*****************************************************************************/
-
-#define VARIANT_IS_OF_TYPE_BOOLEAN(v) ((v) != NULL && ( g_variant_is_of_type ((v), G_VARIANT_TYPE_BOOLEAN) ))
-#define VARIANT_IS_OF_TYPE_STRING(v) ((v) != NULL && ( g_variant_is_of_type ((v), G_VARIANT_TYPE_STRING) ))
-#define VARIANT_IS_OF_TYPE_OBJECT_PATH(v) ((v) != NULL && ( g_variant_is_of_type ((v), G_VARIANT_TYPE_OBJECT_PATH) ))
-#define VARIANT_IS_OF_TYPE_STRING_ARRAY(v) ((v) != NULL && ( g_variant_is_of_type ((v), G_VARIANT_TYPE_STRING_ARRAY) ))
-
-/*****************************************************************************/
-
-NM_GOBJECT_PROPERTIES_DEFINE (NMBluezDevice,
- PROP_PATH,
- PROP_ADDRESS,
- PROP_NAME,
- PROP_CAPABILITIES,
- PROP_USABLE,
- PROP_CONNECTED,
-);
-
-enum {
- INITIALIZED,
- REMOVED,
- LAST_SIGNAL
-};
-
-static guint signals[LAST_SIGNAL] = { 0 };
-
-typedef struct {
- char *path;
-
- GDBusConnection *dbus_connection;
-
- GDBusProxy *proxy;
-
- GDBusProxy *adapter5;
- gboolean adapter_powered;
-
- gboolean initialized;
- gboolean usable;
- NMBluetoothCapabilities connection_bt_type;
-
- guint check_emit_usable_id;
-
- char *adapter_address;
- char *address;
- char *name;
- guint32 capabilities;
- gboolean connected;
- gboolean paired;
-
- char *b4_iface;
-#if WITH_BLUEZ5_DUN
- NMBluez5DunContext *b5_dun_context;
-#endif
-
- NMSettings *settings;
- GSList *connections;
-
- NMSettingsConnection *pan_connection;
- gboolean pan_connection_no_autocreate;
-} NMBluezDevicePrivate;
-
-struct _NMBluezDevice {
- GObject parent;
- NMBluezDevicePrivate _priv;
-};
-
-struct _NMBluezDeviceClass {
- GObjectClass parent;
-};
-
-G_DEFINE_TYPE (NMBluezDevice, nm_bluez_device, G_TYPE_OBJECT)
-
-#define NM_BLUEZ_DEVICE_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMBluezDevice, NM_IS_BLUEZ_DEVICE)
-
-/*****************************************************************************/
-
-#define _NMLOG_PREFIX_NAME "bluez"
-#define _NMLOG_DOMAIN LOGD_BT
-#define _NMLOG(level, ...) \
- G_STMT_START { \
- if (nm_logging_enabled ((level), (_NMLOG_DOMAIN))) { \
- const char *_path = (self) ? NM_BLUEZ_DEVICE_GET_PRIVATE (self)->path : NULL; \
- \
- _nm_log ((level), \
- (_NMLOG_DOMAIN), \
- 0, \
- NULL, \
- NULL, \
- "%s%s%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
- NM_PRINT_FMT_QUOTED (_path, _NMLOG_PREFIX_NAME"[", _path, "]: ", _NMLOG_PREFIX_NAME": ") \
- _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
- } \
- } G_STMT_END
-
-/*****************************************************************************/
-
-static void cp_connection_added (NMSettings *settings,
- NMSettingsConnection *sett_conn,
- NMBluezDevice *self);
-static gboolean connection_compatible (NMBluezDevice *self, NMSettingsConnection *sett_conn);
-
-/*****************************************************************************/
-
-const char *
-nm_bluez_device_get_path (NMBluezDevice *self)
-{
- g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), NULL);
-
- return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->path;
-}
-
-const char *
-nm_bluez_device_get_address (NMBluezDevice *self)
-{
- g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), NULL);
-
- return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->address;
-}
-
-gboolean
-nm_bluez_device_get_initialized (NMBluezDevice *self)
-{
- g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), FALSE);
-
- return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->initialized;
-}
-
-gboolean
-nm_bluez_device_get_usable (NMBluezDevice *self)
-{
- g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), FALSE);
-
- return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->usable;
-}
-
-const char *
-nm_bluez_device_get_name (NMBluezDevice *self)
-{
- g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), NULL);
-
- return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->name;
-}
-
-guint32
-nm_bluez_device_get_capabilities (NMBluezDevice *self)
-{
- g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), 0);
-
- return NM_BLUEZ_DEVICE_GET_PRIVATE (self)->capabilities;
-}
-
-gboolean
-nm_bluez_device_get_connected (NMBluezDevice *self)
-{
- NMBluezDevicePrivate *priv;
-
- g_return_val_if_fail (NM_IS_BLUEZ_DEVICE (self), FALSE);
-
- priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
- return priv->connected;
-}
-
-static void
-pan_connection_check_create (NMBluezDevice *self)
-{
- gs_unref_object NMConnection *connection = NULL;
- NMSettingsConnection *added;
- NMSetting *setting;
- gs_free char *id = NULL;
- char uuid[37];
- 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;
- }
-
- /* 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_simple_connection_new ();
-
- /* Setting: Connection */
- nm_utils_uuid_generate_buf (uuid);
- 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 */
- setting = nm_setting_bluetooth_new ();
- g_object_set (G_OBJECT (setting),
- NM_SETTING_BLUETOOTH_BDADDR, priv->address,
- NM_SETTING_BLUETOOTH_TYPE, NM_SETTING_BLUETOOTH_TYPE_PANU,
- NULL);
- nm_connection_add_setting (connection, setting);
-
- if (!nm_connection_normalize (connection, NULL, NULL, &error)) {
- _LOGE ("couldn't generate a connection for NAP device: %s",
- error->message);
- g_error_free (error);
- g_return_if_reached ();
- }
-
- /* 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->settings, cp_connection_added, self);
- nm_settings_add_connection (priv->settings,
- connection,
- NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
- NM_SETTINGS_CONNECTION_ADD_REASON_NONE,
- NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED,
- &added,
- &error);
- g_signal_handlers_unblock_by_func (priv->settings, cp_connection_added, self);
-
- if (added) {
- nm_assert (!g_slist_find (priv->connections, added));
- nm_assert (connection_compatible (self, added));
- priv->connections = g_slist_prepend (priv->connections, g_object_ref (added));
- priv->pan_connection = added;
- _LOGD ("added new Bluetooth connection for NAP device: '%s' (%s)",
- id, uuid);
- } else {
- _LOGW ("couldn't add new Bluetooth connection for NAP device: '%s' (%s): %s",
- id, uuid, error->message);
- g_clear_error (&error);
- }
-}
-
-static gboolean
-check_emit_usable (NMBluezDevice *self)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
- gboolean new_usable;
-
- /* only expect the supported capabilities set. */
- nm_assert ((priv->capabilities & ~(NM_BT_CAPABILITY_NAP | NM_BT_CAPABILITY_DUN)) == NM_BT_CAPABILITY_NONE );
-
- new_usable = ( priv->initialized
- && priv->capabilities
- && priv->name
- && priv->paired
- && priv->adapter5
- && priv->adapter_powered
- && priv->dbus_connection
- && priv->address
- && priv->adapter_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;
- _notify (self, PROP_USABLE);
- }
-
- return G_SOURCE_REMOVE;
-}
-
-static void
-check_emit_usable_schedule (NMBluezDevice *self)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-
- if (priv->check_emit_usable_id == 0)
- priv->check_emit_usable_id = g_idle_add ((GSourceFunc) check_emit_usable, self);
-}
-
-/*****************************************************************************/
-
-static gboolean
-connection_compatible (NMBluezDevice *self, NMSettingsConnection *sett_conn)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
- NMConnection *connection = nm_settings_connection_get_connection (sett_conn);
- NMSettingBluetooth *s_bt;
- const char *bt_type;
- const char *bdaddr;
-
- if (!nm_connection_is_type (connection, NM_SETTING_BLUETOOTH_SETTING_NAME))
- return FALSE;
-
- s_bt = nm_connection_get_setting_bluetooth (connection);
- if (!s_bt)
- return FALSE;
-
- if (!priv->address)
- return FALSE;
-
- bdaddr = nm_setting_bluetooth_get_bdaddr (s_bt);
- if (!bdaddr)
- return FALSE;
- if (!nm_utils_hwaddr_matches (bdaddr, -1, priv->address, -1))
- return FALSE;
-
- bt_type = nm_setting_bluetooth_get_connection_type (s_bt);
-
- if (nm_streq (bt_type, NM_SETTING_BLUETOOTH_TYPE_NAP))
- return FALSE;
-
- if ( g_str_equal (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN)
- && !(priv->capabilities & NM_BT_CAPABILITY_DUN))
- return FALSE;
-
- if ( g_str_equal (bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU)
- && !(priv->capabilities & NM_BT_CAPABILITY_NAP))
- return FALSE;
-
- return TRUE;
-}
-
-static gboolean
-_internal_track_connection (NMBluezDevice *self,
- NMSettingsConnection *sett_conn,
- gboolean tracked)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
- gboolean was_tracked;
-
- was_tracked = !!g_slist_find (priv->connections, sett_conn);
- if (was_tracked == !!tracked)
- return FALSE;
-
- if (tracked)
- priv->connections = g_slist_prepend (priv->connections, g_object_ref (sett_conn));
- else {
- priv->connections = g_slist_remove (priv->connections, sett_conn);
- if (priv->pan_connection == sett_conn)
- priv->pan_connection = NULL;
- g_object_unref (sett_conn);
- }
-
- return TRUE;
-}
-
-static void
-cp_connection_added (NMSettings *settings,
- NMSettingsConnection *sett_conn,
- NMBluezDevice *self)
-{
- if (connection_compatible (self, sett_conn)) {
- if (_internal_track_connection (self, sett_conn, TRUE))
- check_emit_usable (self);
- }
-}
-
-static void
-cp_connection_removed (NMSettings *settings,
- NMSettingsConnection *sett_conn,
- NMBluezDevice *self)
-{
- if (_internal_track_connection (self, sett_conn, FALSE))
- check_emit_usable (self);
-}
-
-static void
-cp_connection_updated (NMSettings *settings,
- NMSettingsConnection *sett_conn,
- guint update_reason_u,
- NMBluezDevice *self)
-{
- if (_internal_track_connection (self, sett_conn,
- connection_compatible (self, sett_conn)))
- check_emit_usable_schedule (self);
-}
-
-static void
-load_connections (NMBluezDevice *self)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
- NMSettingsConnection *const*connections;
- guint i;
- gboolean changed = FALSE;
-
- connections = nm_settings_get_connections (priv->settings, NULL);
- for (i = 0; connections[i]; i++) {
- if (connection_compatible (self, connections[i]))
- changed |= _internal_track_connection (self, connections[i], TRUE);
- }
- if (changed)
- check_emit_usable (self);
-}
-
-/*****************************************************************************/
-
-static void
-bluez_disconnect_cb (GDBusConnection *dbus_connection,
- GAsyncResult *res,
- gpointer user_data)
-{
- gs_unref_object NMBluezDevice *self = user_data;
- gs_free_error GError *error = NULL;
- gs_unref_variant GVariant *variant = NULL;
-
- variant = g_dbus_connection_call_finish (dbus_connection, res, &error);
- if (!variant) {
- if (!strstr (error->message, "org.bluez.Error.NotConnected"))
- _LOGW ("failed to disconnect: %s", error->message);
- }
-}
-
-void
-nm_bluez_device_disconnect (NMBluezDevice *self)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
- GVariant *args = NULL;
- const char *dbus_iface = NULL;
-
- g_return_if_fail (priv->dbus_connection);
-
- /* FIXME: if we are in the process of connecting and cancel the
- * connection attempt, we must complete the pending connect request.
- * However, we must also ensure that we don't leave a connected device. */
- if (priv->connection_bt_type == NM_BT_CAPABILITY_DUN) {
-#if WITH_BLUEZ5_DUN
- nm_bluez5_dun_cleanup (priv->b5_dun_context);
-#endif
- priv->connected = FALSE;
- goto out;
- } else if (priv->connection_bt_type == NM_BT_CAPABILITY_NAP) {
- dbus_iface = NM_BLUEZ5_NETWORK_INTERFACE;
- } else
- nm_assert_not_reached ();
-
- g_dbus_connection_call (priv->dbus_connection,
- NM_BLUEZ_SERVICE,
- priv->path,
- dbus_iface,
- "Disconnect",
- args ?: g_variant_new("()"),
- NULL,
- G_DBUS_CALL_FLAGS_NONE,
- 10000,
- NULL,
- (GAsyncReadyCallback) bluez_disconnect_cb,
- g_object_ref (self));
-
-out:
- g_clear_pointer (&priv->b4_iface, g_free);
- priv->connection_bt_type = NM_BT_CAPABILITY_NONE;
-}
-
-static void
-_connect_complete (NMBluezDevice *self,
- const char *device,
- NMBluezDeviceConnectCallback callback,
- gpointer callback_user_data,
- GError *error)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-
- nm_assert ((device || error) && !(device && error));
-
- if (device) {
- priv->connected = TRUE;
- _notify (self, PROP_CONNECTED);
- }
-
- if (callback)
- callback (self, device, error, callback_user_data);
-}
-
-static void
-_connect_cb (GObject *source_object,
- GAsyncResult *res,
- gpointer user_data)
-{
- gs_unref_object NMBluezDevice *self = NULL;
- NMBluezDevicePrivate *priv;
- NMBluezDeviceConnectCallback callback;
- gpointer callback_user_data;
- gs_free_error GError *error = NULL;
- char *device = NULL;
- gs_unref_variant GVariant *variant = NULL;
-
- nm_utils_user_data_unpack (user_data, &self, &callback, &callback_user_data);
-
- priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-
- variant = _nm_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), res, G_VARIANT_TYPE ("(s)"), &error);
- if (variant) {
- g_variant_get (variant, "(s)", &device);
- priv->b4_iface = device;
- }
-
- _connect_complete (self, device, callback, callback_user_data, error);
-}
-
-#if WITH_BLUEZ5_DUN
-static void
-_connect_cb_bluez5_dun (NMBluez5DunContext *context,
- const char *device,
- GError *error,
- gpointer user_data)
-{
- gs_unref_object NMBluezDevice *self = NULL;
- gs_unref_object GCancellable *cancellable = NULL;
- NMBluezDeviceConnectCallback callback;
- gpointer callback_user_data;
- gs_free_error GError *cancelled_error = NULL;
-
- nm_utils_user_data_unpack (user_data, &self, &cancellable, &callback, &callback_user_data);
-
- /* FIXME(shutdown): the async operation nm_bluez5_dun_connect() should be cancellable.
- * Fake it here. */
- if (g_cancellable_set_error_if_cancelled (cancellable, &cancelled_error))
- error = cancelled_error;
-
- _connect_complete (self, device, callback, callback_user_data, error);
-}
-#else /* WITH_BLUEZ5_DUN */
-static void
-_connect_cb_bluez5_dun_idle_no_b5 (gpointer user_data,
- GCancellable *cancellable)
-{
- gs_unref_object NMBluezDevice *self = NULL;
- NMBluezDeviceConnectCallback callback;
- gpointer callback_user_data;
- gs_free_error GError *error = NULL;
-
- nm_utils_user_data_unpack (user_data, &self, &callback, &callback_user_data);
-
- if (!g_cancellable_set_error_if_cancelled (cancellable, &error)) {
- g_set_error (&error,
- NM_BT_ERROR,
- NM_BT_ERROR_DUN_CONNECT_FAILED,
- "NetworkManager built without support for Bluez 5");
- }
- callback (self, NULL, error, callback_user_data);
-}
-#endif /* WITH_BLUEZ5_DUN */
-
-void
-nm_bluez_device_connect_async (NMBluezDevice *self,
- NMBluetoothCapabilities connection_bt_type,
- GCancellable *cancellable,
- NMBluezDeviceConnectCallback callback,
- gpointer callback_user_data)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
- const char *dbus_iface = NULL;
- const char *connect_type = NULL;
-
- g_return_if_fail (priv->capabilities & connection_bt_type & (NM_BT_CAPABILITY_DUN | NM_BT_CAPABILITY_NAP));
-
- priv->connection_bt_type = connection_bt_type;
-
- if (connection_bt_type == NM_BT_CAPABILITY_NAP) {
- connect_type = BLUETOOTH_CONNECT_NAP;
- dbus_iface = NM_BLUEZ5_NETWORK_INTERFACE;
- } else if (connection_bt_type == NM_BT_CAPABILITY_DUN) {
- connect_type = BLUETOOTH_CONNECT_DUN;
-#if WITH_BLUEZ5_DUN
- if (priv->b5_dun_context == NULL)
- priv->b5_dun_context = nm_bluez5_dun_new (priv->adapter_address, priv->address);
- nm_bluez5_dun_connect (priv->b5_dun_context,
- _connect_cb_bluez5_dun,
- nm_utils_user_data_pack (g_object_ref (self),
- nm_g_object_ref (cancellable),
- callback,
- callback_user_data));
-#else
- if (callback) {
- nm_utils_invoke_on_idle (_connect_cb_bluez5_dun_idle_no_b5,
- nm_utils_user_data_pack (g_object_ref (self),
- callback,
- callback_user_data),
- cancellable);
- }
-#endif
- return;
- } else
- g_return_if_reached ();
-
- /* FIXME: we need to remember that a connect is in progress.
- * So, if the request gets cancelled, that we disconnect the
- * connection that was established in the meantime. */
- g_dbus_connection_call (priv->dbus_connection,
- NM_BLUEZ_SERVICE,
- priv->path,
- dbus_iface,
- "Connect",
- g_variant_new ("(s)", connect_type),
- NULL,
- G_DBUS_CALL_FLAGS_NONE,
- 20000,
- cancellable,
- _connect_cb,
- nm_utils_user_data_pack (g_object_ref (self),
- callback,
- callback_user_data));
-}
-
-/*****************************************************************************/
-
-static void
-set_adapter_address (NMBluezDevice *self, const char *address)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-
- g_return_if_fail (address);
-
- if (priv->adapter_address)
- g_free (priv->adapter_address);
- priv->adapter_address = g_strdup (address);
-}
-
-static guint32
-convert_uuids_to_capabilities (const char **strings)
-{
- const char **iter;
- guint32 capabilities = 0;
-
- for (iter = strings; iter && *iter; iter++) {
- char **parts;
-
- parts = g_strsplit (*iter, "-", -1);
- if (parts && parts[0]) {
- switch (g_ascii_strtoull (parts[0], NULL, 16)) {
- case 0x1103:
- capabilities |= NM_BT_CAPABILITY_DUN;
- break;
- case 0x1116:
- capabilities |= NM_BT_CAPABILITY_NAP;
- break;
- default:
- break;
- }
- }
- g_strfreev (parts);
- }
-
- return capabilities;
-}
-
-static void
-_set_property_capabilities (NMBluezDevice *self, const char **uuids)
-{
- 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 */
- _LOGW ("ignore change of capabilities for Bluetooth device from %u to %u",
- priv->capabilities, uint_val);
- return;
- }
- _LOGD ("set capabilities for Bluetooth device: %s%s%s",
- 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;
- _notify (self, PROP_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.
- **/
-static void
-_set_property_address (NMBluezDevice *self, const char *addr)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-
- if (g_strcmp0 (priv->address, addr) == 0)
- return;
-
- if (!addr) {
- _LOGW ("cannot reset address from '%s' to NULL", priv->address);
- return;
- }
-
- if (priv->address != NULL) {
- _LOGW ("cannot reset address from '%s' to '%s'", priv->address, addr);
- return;
- }
-
- if (!nm_utils_hwaddr_valid (addr, ETH_ALEN)) {
- _LOGW ("cannot set address to '%s' (invalid value)", addr);
- return;
- }
-
- priv->address = g_strdup (addr);
- _notify (self, PROP_ADDRESS);
-}
-
-static void
-_take_variant_property_address (NMBluezDevice *self, GVariant *v)
-{
- _set_property_address (self, VARIANT_IS_OF_TYPE_STRING (v) ? g_variant_get_string (v, NULL) : NULL);
- if (v)
- g_variant_unref (v);
-}
-
-static void
-_take_variant_property_name (NMBluezDevice *self, GVariant *v)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
- const char *str;
-
- if (VARIANT_IS_OF_TYPE_STRING (v)) {
- str = g_variant_get_string (v, NULL);
- if (g_strcmp0 (priv->name, str)) {
- g_free (priv->name);
- priv->name = g_strdup (str);
- _notify (self, PROP_NAME);
- }
- }
- if (v)
- g_variant_unref (v);
-}
-
-static void
-_take_variant_property_uuids (NMBluezDevice *self, GVariant *v)
-{
- if (VARIANT_IS_OF_TYPE_STRING_ARRAY (v)) {
- const char **uuids = g_variant_get_strv (v, NULL);
-
- _set_property_capabilities (self, uuids);
- g_free (uuids);
- }
- if (v)
- g_variant_unref (v);
-}
-
-static void
-_take_variant_property_connected (NMBluezDevice *self, GVariant *v)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-
- if (VARIANT_IS_OF_TYPE_BOOLEAN (v)) {
- gboolean connected = g_variant_get_boolean (v);
-
- if (priv->connected != connected) {
- priv->connected = connected;
- _notify (self, PROP_CONNECTED);
- }
- }
- if (v)
- g_variant_unref (v);
-}
-
-static void
-_take_variant_property_paired (NMBluezDevice *self, GVariant *v)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-
- if (VARIANT_IS_OF_TYPE_BOOLEAN (v))
- priv->paired = g_variant_get_boolean (v);
-
- if (v)
- g_variant_unref (v);
-}
-
-static void
-adapter5_on_properties_changed (GDBusProxy *proxy,
- GVariant *changed_properties,
- GStrv invalidated_properties,
- gpointer user_data)
-{
- NMBluezDevice *self = NM_BLUEZ_DEVICE (user_data);
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
- GVariantIter i;
- const char *property;
- GVariant *v;
-
- g_variant_iter_init (&i, changed_properties);
- while (g_variant_iter_next (&i, "{&sv}", &property, &v)) {
- if (!strcmp (property, "Powered") && VARIANT_IS_OF_TYPE_BOOLEAN (v)) {
- 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
-adapter5_on_acquired (GObject *object, GAsyncResult *res, NMBluezDevice *self)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
- GError *error = NULL;
- GVariant *v;
-
- priv->adapter5 = g_dbus_proxy_new_for_bus_finish (res, &error);
- if (!priv->adapter5) {
- _LOGW ("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->adapter5, "g-properties-changed",
- G_CALLBACK (adapter5_on_properties_changed), self);
-
- /* Check adapter's powered state */
- v = g_dbus_proxy_get_cached_property (priv->adapter5, "Powered");
- priv->adapter_powered = VARIANT_IS_OF_TYPE_BOOLEAN (v) ? g_variant_get_boolean (v) : FALSE;
- if (v)
- g_variant_unref (v);
-
- v = g_dbus_proxy_get_cached_property (priv->adapter5, "Address");
- if (VARIANT_IS_OF_TYPE_STRING (v))
- set_adapter_address (self, g_variant_get_string (v, NULL));
-
- priv->initialized = TRUE;
- g_signal_emit (self, signals[INITIALIZED], 0, TRUE);
-
- check_emit_usable (self);
- }
-
- g_object_unref (self);
-}
-
-static void
-_take_one_variant_property (NMBluezDevice *self, const char *property, GVariant *v)
-{
- if (v) {
- if (!g_strcmp0 (property, "Address"))
- _take_variant_property_address (self, v);
- else if (!g_strcmp0 (property, "Connected"))
- _take_variant_property_connected (self, v);
- else if (!g_strcmp0 (property, "Paired"))
- _take_variant_property_paired (self, v);
- else if (!g_strcmp0 (property, "Name"))
- _take_variant_property_name (self, v);
- else if (!g_strcmp0 (property, "UUIDs"))
- _take_variant_property_uuids (self, v);
- else
- g_variant_unref (v);
- }
-}
-
-static void
-_set_properties (NMBluezDevice *self, GVariant *properties)
-{
- GVariantIter i;
- const char *property;
- GVariant *v;
-
- g_object_freeze_notify (G_OBJECT (self));
- g_variant_iter_init (&i, properties);
- while (g_variant_iter_next (&i, "{&sv}", &property, &v))
- _take_one_variant_property (self, property, v);
- g_object_thaw_notify (G_OBJECT (self));
-}
-
-static void
-properties_changed (GDBusProxy *proxy,
- GVariant *changed_properties,
- GStrv invalidated_properties,
- gpointer user_data)
-{
- NMBluezDevice *self = NM_BLUEZ_DEVICE (user_data);
-
- _set_properties (self, changed_properties);
- check_emit_usable (self);
-}
-
-static void
-query_properties (NMBluezDevice *self)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
- GVariant *v;
-
- g_object_freeze_notify (G_OBJECT (self));
- _take_variant_property_address (self, g_dbus_proxy_get_cached_property (priv->proxy, "Address"));
- _take_variant_property_connected (self, g_dbus_proxy_get_cached_property (priv->proxy, "Connected"));
- _take_variant_property_paired (self, g_dbus_proxy_get_cached_property (priv->proxy, "Paired"));
- _take_variant_property_name (self, g_dbus_proxy_get_cached_property (priv->proxy, "Name"));
- _take_variant_property_uuids (self, g_dbus_proxy_get_cached_property (priv->proxy, "UUIDs"));
- g_object_thaw_notify (G_OBJECT (self));
-
- v = g_dbus_proxy_get_cached_property (priv->proxy, "Adapter");
- if (VARIANT_IS_OF_TYPE_OBJECT_PATH (v)) {
- g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
- G_DBUS_PROXY_FLAGS_NONE,
- NULL,
- NM_BLUEZ_SERVICE,
- g_variant_get_string (v, NULL),
- NM_BLUEZ5_ADAPTER_INTERFACE,
- NULL,
- (GAsyncReadyCallback) adapter5_on_acquired,
- g_object_ref (self));
- g_variant_unref (v);
- } else {
- /* If the Adapter property is unset at this point, we won't try to acquire the adapter later on
- * and the device stays unusable. This should not happen, but if it does, log a debug message. */
- _LOGD ("device has no adapter property and cannot be used");
- }
-
- /* Check if any connections match this device */
- load_connections (self);
-}
-
-static void
-on_proxy_acquired (GObject *object, GAsyncResult *res, NMBluezDevice *self)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
- GError *error = NULL;
-
- priv->proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
-
- if (!priv->proxy) {
- _LOGW ("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->proxy, "g-properties-changed",
- G_CALLBACK (properties_changed), self);
- query_properties (self);
- }
- g_object_unref (self);
-}
-
-/*****************************************************************************/
-
-static void
-get_property (GObject *object, guint prop_id,
- GValue *value, GParamSpec *pspec)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE ((NMBluezDevice *) object);
-
- switch (prop_id) {
- case PROP_PATH:
- g_value_set_string (value, priv->path);
- break;
- case PROP_ADDRESS:
- g_value_set_string (value, priv->address);
- break;
- case PROP_NAME:
- g_value_set_string (value, priv->name);
- break;
- case PROP_CAPABILITIES:
- g_value_set_uint (value, priv->capabilities);
- break;
- case PROP_USABLE:
- g_value_set_boolean (value, priv->usable);
- break;
- case PROP_CONNECTED:
- g_value_set_boolean (value, priv->connected);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-set_property (GObject *object, guint prop_id,
- const GValue *value, GParamSpec *pspec)
-{
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE ((NMBluezDevice *) object);
-
- switch (prop_id) {
- case PROP_PATH:
- /* construct-only */
- priv->path = g_value_dup_string (value);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-/*****************************************************************************/
-
-static void
-nm_bluez_device_init (NMBluezDevice *self)
-{
-}
-
-NMBluezDevice *
-nm_bluez_device_new (GDBusConnection *dbus_connection,
- const char *path,
- NMSettings *settings)
-{
- NMBluezDevice *self;
- NMBluezDevicePrivate *priv;
-
- g_return_val_if_fail (path != NULL, NULL);
- g_return_val_if_fail (NM_IS_SETTINGS (settings), NULL);
- g_return_val_if_fail (G_IS_DBUS_CONNECTION (dbus_connection), NULL);
-
- self = (NMBluezDevice *) g_object_new (NM_TYPE_BLUEZ_DEVICE,
- NM_BLUEZ_DEVICE_PATH, path,
- NULL);
- if (!self)
- return NULL;
-
- _LOGD ("create NMBluezDevice");
-
- priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-
- priv->settings = g_object_ref (settings);
-
- g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_ADDED, G_CALLBACK (cp_connection_added), self);
- g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_REMOVED, G_CALLBACK (cp_connection_removed), self);
- g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_UPDATED, G_CALLBACK (cp_connection_updated), self);
-
- priv->dbus_connection = g_object_ref (dbus_connection);
-
- g_dbus_proxy_new (priv->dbus_connection,
- G_DBUS_PROXY_FLAGS_NONE,
- NULL,
- NM_BLUEZ_SERVICE,
- priv->path,
- NM_BLUEZ5_DEVICE_INTERFACE,
- NULL,
- (GAsyncReadyCallback) on_proxy_acquired,
- g_object_ref (self));
-
- return self;
-}
-
-static void
-dispose (GObject *object)
-{
- NMBluezDevice *self = NM_BLUEZ_DEVICE (object);
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
- NMSettingsConnection *to_delete = NULL;
-
- nm_clear_g_source (&priv->check_emit_usable_id);
-
- if (priv->pan_connection) {
- /* Check whether we want to remove the created connection. If so, we take a reference
- * and delete it at the end of dispose(). */
- if (NM_FLAGS_HAS (nm_settings_connection_get_flags (priv->pan_connection),
- NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED))
- to_delete = g_object_ref (priv->pan_connection);
-
- priv->pan_connection = NULL;
- }
-
-#if WITH_BLUEZ5_DUN
- if (priv->b5_dun_context) {
- nm_bluez5_dun_free (priv->b5_dun_context);
- priv->b5_dun_context = NULL;
- }
-#endif
-
- if (priv->settings) {
- g_signal_handlers_disconnect_by_func (priv->settings, cp_connection_added, self);
- g_signal_handlers_disconnect_by_func (priv->settings, cp_connection_removed, self);
- g_signal_handlers_disconnect_by_func (priv->settings, cp_connection_updated, self);
- }
-
- g_slist_free_full (priv->connections, g_object_unref);
- priv->connections = NULL;
-
- if (priv->adapter5) {
- g_signal_handlers_disconnect_by_func (priv->adapter5, adapter5_on_properties_changed, self);
- g_clear_object (&priv->adapter5);
- }
-
- g_clear_object (&priv->dbus_connection);
-
- G_OBJECT_CLASS (nm_bluez_device_parent_class)->dispose (object);
-
- if (to_delete) {
- _LOGD ("removing Bluetooth connection for NAP device: '%s' (%s)",
- nm_settings_connection_get_id (to_delete),
- nm_settings_connection_get_uuid (to_delete));
- nm_settings_connection_delete (to_delete, FALSE);
- g_object_unref (to_delete);
- }
-
- g_clear_object (&priv->settings);
-}
-
-static void
-finalize (GObject *object)
-{
- NMBluezDevice *self = NM_BLUEZ_DEVICE (object);
- NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-
- _LOGD ("finalize NMBluezDevice");
-
- g_free (priv->path);
- g_free (priv->adapter_address);
- g_free (priv->address);
- g_free (priv->name);
- g_free (priv->b4_iface);
-
- if (priv->proxy)
- g_signal_handlers_disconnect_by_data (priv->proxy, object);
- g_clear_object (&priv->proxy);
-
- G_OBJECT_CLASS (nm_bluez_device_parent_class)->finalize (object);
-}
-
-static void
-nm_bluez_device_class_init (NMBluezDeviceClass *config_class)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (config_class);
-
- object_class->get_property = get_property;
- object_class->set_property = set_property;
- object_class->dispose = dispose;
- object_class->finalize = finalize;
-
- obj_properties[PROP_PATH] =
- g_param_spec_string (NM_BLUEZ_DEVICE_PATH, "", "",
- NULL,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_STATIC_STRINGS);
-
- obj_properties[PROP_ADDRESS] =
- g_param_spec_string (NM_BLUEZ_DEVICE_ADDRESS, "", "",
- NULL,
- G_PARAM_READABLE |
- G_PARAM_STATIC_STRINGS);
-
- obj_properties[PROP_NAME] =
- g_param_spec_string (NM_BLUEZ_DEVICE_NAME, "", "",
- NULL,
- G_PARAM_READABLE |
- G_PARAM_STATIC_STRINGS);
-
- obj_properties[PROP_CAPABILITIES] =
- g_param_spec_uint (NM_BLUEZ_DEVICE_CAPABILITIES, "", "",
- 0, G_MAXUINT, 0,
- G_PARAM_READABLE |
- G_PARAM_STATIC_STRINGS);
-
- obj_properties[PROP_USABLE] =
- g_param_spec_boolean (NM_BLUEZ_DEVICE_USABLE, "", "",
- FALSE,
- G_PARAM_READABLE |
- G_PARAM_STATIC_STRINGS);
-
- obj_properties[PROP_CONNECTED] =
- g_param_spec_boolean (NM_BLUEZ_DEVICE_CONNECTED, "", "",
- FALSE,
- G_PARAM_READABLE |
- G_PARAM_STATIC_STRINGS);
-
- g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
-
- signals[INITIALIZED] = g_signal_new (NM_BLUEZ_DEVICE_INITIALIZED,
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_LAST,
- 0,
- NULL, NULL, NULL,
- G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
-
- signals[REMOVED] = g_signal_new (NM_BLUEZ_DEVICE_REMOVED,
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_LAST,
- 0,
- NULL, NULL, NULL,
- G_TYPE_NONE, 0);
-}
-
diff --git a/src/devices/bluetooth/nm-bluez-device.h b/src/devices/bluetooth/nm-bluez-device.h
deleted file mode 100644
index 3bccea8a88..0000000000
--- a/src/devices/bluetooth/nm-bluez-device.h
+++ /dev/null
@@ -1,70 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/* NetworkManager -- Network link manager
- *
- * Copyright (C) 2009 - 2014 Red Hat, Inc.
- */
-
-#ifndef __NETWORKMANAGER_BLUEZ_DEVICE_H__
-#define __NETWORKMANAGER_BLUEZ_DEVICE_H__
-
-#include "nm-connection.h"
-
-#define NM_TYPE_BLUEZ_DEVICE (nm_bluez_device_get_type ())
-#define NM_BLUEZ_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_BLUEZ_DEVICE, NMBluezDevice))
-#define NM_BLUEZ_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_BLUEZ_DEVICE, NMBluezDeviceClass))
-#define NM_IS_BLUEZ_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_BLUEZ_DEVICE))
-#define NM_IS_BLUEZ_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_BLUEZ_DEVICE))
-#define NM_BLUEZ_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_BLUEZ_DEVICE, NMBluezDeviceClass))
-
-/* Properties */
-#define NM_BLUEZ_DEVICE_PATH "path"
-#define NM_BLUEZ_DEVICE_ADDRESS "address"
-#define NM_BLUEZ_DEVICE_NAME "name"
-#define NM_BLUEZ_DEVICE_CAPABILITIES "capabilities"
-#define NM_BLUEZ_DEVICE_USABLE "usable"
-#define NM_BLUEZ_DEVICE_CONNECTED "connected"
-
-/* Signals */
-#define NM_BLUEZ_DEVICE_INITIALIZED "initialized"
-#define NM_BLUEZ_DEVICE_REMOVED "removed"
-
-typedef struct _NMBluezDevice NMBluezDevice;
-typedef struct _NMBluezDeviceClass NMBluezDeviceClass;
-
-GType nm_bluez_device_get_type (void);
-
-NMBluezDevice *nm_bluez_device_new (GDBusConnection *dbus_connection,
- const char *path,
- NMSettings *settings);
-
-const char *nm_bluez_device_get_path (NMBluezDevice *self);
-
-gboolean nm_bluez_device_get_initialized (NMBluezDevice *self);
-
-gboolean nm_bluez_device_get_usable (NMBluezDevice *self);
-
-const char *nm_bluez_device_get_address (NMBluezDevice *self);
-
-const char *nm_bluez_device_get_name (NMBluezDevice *self);
-
-guint32 nm_bluez_device_get_capabilities (NMBluezDevice *self);
-
-gboolean nm_bluez_device_get_connected (NMBluezDevice *self);
-
-typedef void (*NMBluezDeviceConnectCallback) (NMBluezDevice *self,
- const char *device,
- GError *error,
- gpointer user_data);
-
-void
-nm_bluez_device_connect_async (NMBluezDevice *self,
- NMBluetoothCapabilities connection_bt_type,
- GCancellable *cancellable,
- NMBluezDeviceConnectCallback callback,
- gpointer callback_user_data);
-
-void
-nm_bluez_device_disconnect (NMBluezDevice *self);
-
-#endif /* __NETWORKMANAGER_BLUEZ_DEVICE_H__ */
-
diff --git a/src/devices/bluetooth/nm-bluez-manager.c b/src/devices/bluetooth/nm-bluez-manager.c
index cf9e521c74..22b40c0f85 100644
--- a/src/devices/bluetooth/nm-bluez-manager.c
+++ b/src/devices/bluetooth/nm-bluez-manager.c
@@ -6,52 +6,181 @@
#include "nm-default.h"
+#include "nm-bluez-manager.h"
+
#include <signal.h>
#include <stdlib.h>
#include <gmodule.h>
+#include "nm-glib-aux/nm-dbus-aux.h"
+#include "nm-glib-aux/nm-c-list.h"
+#include "nm-dbus-manager.h"
#include "devices/nm-device-factory.h"
#include "devices/nm-device-bridge.h"
#include "nm-setting-bluetooth.h"
#include "settings/nm-settings.h"
-#include "nm-bluez5-manager.h"
-#include "nm-bluez-device.h"
#include "nm-bluez-common.h"
#include "nm-device-bt.h"
+#include "nm-manager.h"
+#include "nm-bluez5-dun.h"
#include "nm-core-internal.h"
#include "platform/nm-platform.h"
#include "nm-std-aux/nm-dbus-compat.h"
/*****************************************************************************/
-#define NM_TYPE_BLUEZ_MANAGER (nm_bluez_manager_get_type ())
-#define NM_BLUEZ_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_BLUEZ_MANAGER, NMBluezManager))
-#define NM_BLUEZ_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_BLUEZ_MANAGER, NMBluezManagerClass))
-#define NM_IS_BLUEZ_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_BLUEZ_MANAGER))
-#define NM_IS_BLUEZ_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_BLUEZ_MANAGER))
-#define NM_BLUEZ_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_BLUEZ_MANAGER, NMBluezManagerClass))
+#if WITH_BLUEZ5_DUN
+#define _NM_BT_CAPABILITY_SUPPORTED_DUN NM_BT_CAPABILITY_DUN
+#else
+#define _NM_BT_CAPABILITY_SUPPORTED_DUN NM_BT_CAPABILITY_NONE
+#endif
+#define _NM_BT_CAPABILITY_SUPPORTED (NM_BT_CAPABILITY_NAP | _NM_BT_CAPABILITY_SUPPORTED_DUN)
+
+typedef struct {
+ const char *bdaddr;
+ CList lst_head;
+ NMBluetoothCapabilities bt_type:8;
+ char bdaddr_data[];
+} ConnDataHead;
+
+typedef struct {
+ NMSettingsConnection *sett_conn;
+ ConnDataHead *cdata_hd;
+ CList lst;
+} ConnDataElem;
+
+typedef struct {
+ GCancellable *ext_cancellable;
+ GCancellable *int_cancellable;
+ NMBtVTableRegisterCallback callback;
+ gpointer callback_user_data;
+ gulong ext_cancelled_id;
+} NetworkServerRegisterReqData;
typedef struct {
+ GCancellable *ext_cancellable;
+ GCancellable *int_cancellable;
+ NMBluezManagerConnectCb callback;
+ gpointer callback_user_data;
+ char *device_name;
+ gulong ext_cancelled_id;
+ guint timeout_id;
+ guint timeout_wait_connect_id;
+} DeviceConnectReqData;
+typedef struct {
+ const char *object_path;
+
+ NMBluezManager *self;
+
+ /* Fields name with "d_" prefix are purely cached values from BlueZ's
+ * ObjectManager D-Bus interface. There is no logic whatsoever about
+ * them.
+ */
+
+ CList process_change_lst;
+
+ struct {
+ char *address;
+ } d_adapter;
+
+ struct {
+ char *address;
+ char *name;
+ char *adapter;
+ } d_device;
+
+ struct {
+ char *interface;
+ } d_network;
+
+ struct {
+ CList lst;
+ char *adapter_address;
+ NMDevice *device_br;
+ NetworkServerRegisterReqData *r_req_data;
+ } x_network_server;
+
+ struct {
+ NMSettingsConnection *panu_connection;
+ NMDeviceBt *device_bt;
+ DeviceConnectReqData *c_req_data;
+ NMBluez5DunContext *connect_dun_context;
+ gulong device_bt_signal_id;
+ } x_device;
+
+ /* indicate whether the D-Bus object has the particular D-Bus interface. */
+ bool d_has_adapter_iface:1;
+ bool d_has_device_iface:1;
+ bool d_has_network_iface:1;
+ bool d_has_network_server_iface:1;
+
+ /* cached D-Bus properties for Device1 ("d_device*"). */
+ NMBluetoothCapabilities d_device_capabilities:6;
+ bool d_device_connected:1;
+ bool d_device_paired:1;
+
+ /* cached D-Bus properties for Network1 ("d_network*"). */
+ bool d_network_connected:1;
+
+ /* cached D-Bus properties for Adapter1 ("d_adapter*"). */
+ bool d_adapter_powered:1;
+
+ /* properties related to device ("x_device*"). */
+ NMBluetoothCapabilities x_device_connect_bt_type:6;
+ bool x_device_is_usable:1;
+ bool x_device_is_connected:1;
+
+ bool x_device_panu_connection_allow_create:1;
+
+ /* flag to remember last time when we checked wether the object
+ * was a suitable adapter that is usable to a device. */
+ bool was_usable_adapter_for_device_before:1;
+
+ char _object_path_intern[];
+} BzDBusObj;
+
+typedef struct {
+ NMManager *manager;
NMSettings *settings;
- NMBluez5Manager *manager5;
- guint watch_name_id;
+ GDBusConnection *dbus_connection;
+
+ NMBtVTableNetworkServer vtable_network_server;
+
+ GCancellable *name_owner_get_cancellable;
+ GCancellable *get_managed_objects_cancellable;
+
+ GHashTable *bzobjs;
+
+ char *name_owner;
+
+ GHashTable *conn_data_heads;
+ GHashTable *conn_data_elems;
+
+ CList network_server_lst_head;
- GDBusProxy *introspect_proxy;
- GCancellable *async_cancellable;
+ CList process_change_lst_head;
+
+ guint name_owner_changed_id;
+
+ guint managed_objects_changed_id;
+
+ guint properties_changed_id;
+
+ guint process_change_idle_id;
+
+ bool settings_registered:1;
} NMBluezManagerPrivate;
-typedef struct {
+struct _NMBluezManager {
NMDeviceFactory parent;
NMBluezManagerPrivate _priv;
-} NMBluezManager;
+};
-typedef struct {
+struct _NMBluezManagerClass {
NMDeviceFactoryClass parent;
-} NMBluezManagerClass;
-
-static GType nm_bluez_manager_get_type (void);
+};
G_DEFINE_TYPE (NMBluezManager, nm_bluez_manager, NM_TYPE_DEVICE_FACTORY);
@@ -77,256 +206,2583 @@ nm_device_factory_create (GError **error)
/*****************************************************************************/
-static void check_bluez_and_try_setup (NMBluezManager *self);
+static NMBluetoothCapabilities
+convert_uuids_to_capabilities (const char *const*strv)
+{
+ NMBluetoothCapabilities capabilities = NM_BT_CAPABILITY_NONE;
+
+ if (strv) {
+ for (; strv[0]; strv++) {
+ gs_free char *s_part1 = NULL;
+ const char *str = strv[0];
+ const char *s;
+
+ s = strchr (str, '-');
+ if (!s)
+ continue;
+
+ s_part1 = g_strndup (str, s - str);
+ switch (g_ascii_strtoull (s_part1, NULL, 16)) {
+ case 0x1103:
+ capabilities |= NM_BT_CAPABILITY_DUN;
+ break;
+ case 0x1116:
+ capabilities |= NM_BT_CAPABILITY_NAP;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ return capabilities;
+}
+
+/*****************************************************************************/
+
+static void _cleanup_for_name_owner (NMBluezManager *self);
+static void _connect_disconnect (NMBluezManager *self,
+ BzDBusObj *bzobj,
+ const char *reason);
+static gboolean _bzobjs_network_server_is_usable (const BzDBusObj *bzobj,
+ gboolean require_powered);
+static gboolean _bzobjs_is_dead (const BzDBusObj *bzobj);
+static gboolean _bzobjs_device_is_usable (const BzDBusObj *bzobj,
+ BzDBusObj **out_adapter_bzobj,
+ gboolean *out_create_panu_connection);
+static gboolean _bzobjs_adapter_is_usable_for_device (const BzDBusObj *bzobj);
+static ConnDataHead *_conn_track_find_head (NMBluezManager *self,
+ NMBluetoothCapabilities bt_type,
+ const char *bdaddr);
+static void _process_change_idle_schedule (NMBluezManager *self,
+ BzDBusObj *bzobj);
+static void _network_server_unregister_bridge (NMBluezManager *self,
+ BzDBusObj *bzobj,
+ const char *reason);
+static gboolean _connect_timeout_wait_connected_cb (gpointer user_data);
+
+/*****************************************************************************/
+
+static void
+_dbus_call_complete_cb_nop (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ /* we don't do anything at all. The only reason to register this
+ * callback is so that GDBusConnection keeps the cancellable alive
+ * long enough until the call completes.
+ *
+ * Note that this cancellable in turn is registered via
+ * nm_shutdown_wait_obj_register_*(), to block shutdown until
+ * we are done. */
+}
+
+/*****************************************************************************/
+
+static void
+_network_server_register_req_data_complete (NetworkServerRegisterReqData *r_req_data,
+ GError *error)
+{
+ nm_clear_g_signal_handler (r_req_data->ext_cancellable, &r_req_data->ext_cancelled_id);
+
+ nm_clear_g_cancellable (&r_req_data->int_cancellable);
+
+ if (r_req_data->callback) {
+ gs_free GError *error_cancelled = NULL;
+
+ if (g_cancellable_set_error_if_cancelled (r_req_data->ext_cancellable, &error_cancelled))
+ error = error_cancelled;
+
+ r_req_data->callback (error, r_req_data->callback_user_data);
+ }
+
+ g_object_unref (r_req_data->ext_cancellable);
+ nm_g_slice_free (r_req_data);
+}
+
+static void
+_device_connect_req_data_complete (DeviceConnectReqData *c_req_data,
+ NMBluezManager *self,
+ const char *device_name,
+ GError *error)
+{
+ nm_assert ((!!device_name) != (!!error));
+
+ nm_clear_g_signal_handler (c_req_data->ext_cancellable, &c_req_data->ext_cancelled_id);
+
+ nm_clear_g_cancellable (&c_req_data->int_cancellable);
+ nm_clear_g_source (&c_req_data->timeout_id);
+ nm_clear_g_source (&c_req_data->timeout_wait_connect_id);
+
+ if (c_req_data->callback) {
+ gs_free GError *error_cancelled = NULL;
+
+ if (g_cancellable_set_error_if_cancelled (c_req_data->ext_cancellable, &error_cancelled)) {
+ error = error_cancelled;
+ device_name = NULL;
+ }
+
+ c_req_data->callback (self, TRUE, device_name, error, c_req_data->callback_user_data);
+ }
+
+ g_object_unref (c_req_data->ext_cancellable);
+ nm_clear_g_free (&c_req_data->device_name);
+ nm_g_slice_free (c_req_data);
+}
/*****************************************************************************/
-struct AsyncData {
+static BzDBusObj *
+_bz_dbus_obj_new (NMBluezManager *self,
+ const char *object_path)
+{
+ BzDBusObj *bzobj;
+ gsize l;
+
+ nm_assert (NM_IS_BLUEZ_MANAGER (self));
+
+ l = strlen (object_path) + 1;
+
+ bzobj = g_malloc (sizeof (BzDBusObj) + l);
+ *bzobj = (BzDBusObj) {
+ .object_path = bzobj->_object_path_intern,
+ .self = self,
+ .x_network_server.lst = C_LIST_INIT (bzobj->x_network_server.lst),
+ .process_change_lst = C_LIST_INIT (bzobj->process_change_lst),
+ .x_device_panu_connection_allow_create = TRUE,
+ };
+ memcpy (bzobj->_object_path_intern, object_path, l);
+
+ return bzobj;
+}
+
+static void
+_bz_dbus_obj_free (BzDBusObj *bzobj)
+{
+ nm_assert (bzobj);
+ nm_assert (NM_IS_BLUEZ_MANAGER (bzobj->self));
+ nm_assert (!bzobj->x_network_server.device_br);
+ nm_assert (!bzobj->x_network_server.r_req_data);
+ nm_assert (!bzobj->x_device.c_req_data);
+
+ c_list_unlink_stale (&bzobj->process_change_lst);
+ c_list_unlink_stale (&bzobj->x_network_server.lst);
+ g_free (bzobj->x_network_server.adapter_address);
+ g_free (bzobj->d_adapter.address);
+ g_free (bzobj->d_network.interface);
+ g_free (bzobj->d_device.address);
+ g_free (bzobj->d_device.name);
+ g_free (bzobj->d_device.adapter);
+ g_free (bzobj);
+}
+
+/*****************************************************************************/
+
+static const char *
+_bzobj_to_string (const BzDBusObj *bzobj, char *buf, gsize len)
+{
+ char *buf0 = buf;
+ const char *prefix = "";
+ gboolean device_is_usable;
+ gboolean create_panu_connection = FALSE;
+ gboolean network_server_is_usable;
+ char sbuf_cap[100];
+
+ if (len > 0)
+ buf[0] = '\0';
+
+ if (bzobj->d_has_adapter_iface) {
+ nm_utils_strbuf_append_str (&buf, &len, prefix);
+ prefix = ", ";
+ nm_utils_strbuf_append_str (&buf, &len, "Adapter1 {");
+ if (bzobj->d_adapter.address) {
+ nm_utils_strbuf_append (&buf, &len, " d.address: \"%s\"", bzobj->d_adapter.address);
+ if (bzobj->d_adapter_powered)
+ nm_utils_strbuf_append_str (&buf, &len, ",");
+ }
+ if (bzobj->d_adapter_powered)
+ nm_utils_strbuf_append (&buf, &len, " d.powered: 1");
+ nm_utils_strbuf_append_str (&buf, &len, " }");
+ }
+
+ if (bzobj->d_has_device_iface) {
+ const char *prefix1 = "";
+
+ nm_utils_strbuf_append_str (&buf, &len, prefix);
+ prefix = ", ";
+ nm_utils_strbuf_append_str (&buf, &len, "Device1 {");
+ if (bzobj->d_device.address) {
+ nm_utils_strbuf_append (&buf, &len, "%s d.address: \"%s\"", prefix1, bzobj->d_device.address);
+ prefix1 = ",";
+ }
+ if (bzobj->d_device.name) {
+ nm_utils_strbuf_append (&buf, &len, "%s d.name: \"%s\"", prefix1, bzobj->d_device.name);
+ prefix1 = ",";
+ }
+ if (bzobj->d_device.adapter) {
+ nm_utils_strbuf_append (&buf, &len, "%s d.adapter: \"%s\"", prefix1, bzobj->d_device.adapter);
+ prefix1 = ",";
+ }
+ if (bzobj->d_device_capabilities != NM_BT_CAPABILITY_NONE) {
+ nm_utils_strbuf_append (&buf, &len, "%s d.capabilities: \"%s\"",
+ prefix1,
+ nm_bluetooth_capability_to_string (bzobj->d_device_capabilities, sbuf_cap, sizeof (sbuf_cap)));
+ prefix1 = ",";
+ }
+ if (bzobj->d_device_connected) {
+ nm_utils_strbuf_append (&buf, &len, "%s d.connected: 1", prefix1);
+ prefix1 = ",";
+ }
+ if (bzobj->d_device_paired) {
+ nm_utils_strbuf_append (&buf, &len, "%s d.paired: 1", prefix1);
+ prefix1 = ",";
+ }
+ nm_utils_strbuf_append_str (&buf, &len, " }");
+ }
+
+ network_server_is_usable = _bzobjs_network_server_is_usable (bzobj, TRUE);
+
+ if ( bzobj->d_has_network_server_iface
+ || network_server_is_usable != (!c_list_is_empty (&bzobj->x_network_server.lst))
+ || !c_list_is_empty (&bzobj->x_network_server.lst)
+ || !nm_streq0 (bzobj->d_has_adapter_iface ? bzobj->d_adapter.address : NULL, bzobj->x_network_server.adapter_address)
+ || bzobj->x_network_server.device_br
+ || bzobj->x_network_server.r_req_data) {
+
+ nm_utils_strbuf_append_str (&buf, &len, prefix);
+ prefix = ", ";
+
+ nm_utils_strbuf_append (&buf, &len, "NetworkServer1 { ");
+
+ if (!bzobj->d_has_network_server_iface)
+ nm_utils_strbuf_append (&buf, &len, " has-d-iface: 0, ");
+
+ if (network_server_is_usable != (!c_list_is_empty (&bzobj->x_network_server.lst)))
+ nm_utils_strbuf_append (&buf, &len, "usable: %d, used: %d", !!network_server_is_usable, !network_server_is_usable);
+ else if (network_server_is_usable)
+ nm_utils_strbuf_append (&buf, &len, "used: 1");
+ else
+ nm_utils_strbuf_append (&buf, &len, "usable: 0");
+
+ if (!nm_streq0 (bzobj->d_has_adapter_iface ? bzobj->d_adapter.address : NULL, bzobj->x_network_server.adapter_address)) {
+ if (bzobj->x_network_server.adapter_address)
+ nm_utils_strbuf_append (&buf, &len, ", adapter-address: \"%s\"", bzobj->x_network_server.adapter_address);
+ else
+ nm_utils_strbuf_append (&buf, &len, ", adapter-address: <NULL>");
+ }
+
+ if (bzobj->x_network_server.device_br)
+ nm_utils_strbuf_append (&buf, &len, ", bridge-device: 1");
+
+ if (bzobj->x_network_server.r_req_data)
+ nm_utils_strbuf_append (&buf, &len, ", register-in-progress: 1");
+
+ nm_utils_strbuf_append_str (&buf, &len, " }");
+ }
+
+ device_is_usable = _bzobjs_device_is_usable (bzobj, NULL, &create_panu_connection);
+
+ if ( bzobj->d_has_network_iface
+ || bzobj->d_network.interface
+ || bzobj->d_network_connected
+ || create_panu_connection
+ || bzobj->x_device.panu_connection
+ || device_is_usable != bzobj->x_device_is_usable
+ || bzobj->x_device.device_bt
+ || bzobj->x_device_connect_bt_type != NM_BT_CAPABILITY_NONE
+ || bzobj->x_device.connect_dun_context
+ || bzobj->x_device.c_req_data
+ || bzobj->x_device_is_connected != bzobj->d_network_connected) {
+
+ nm_utils_strbuf_append_str (&buf, &len, prefix);
+ prefix = ", ";
+ nm_utils_strbuf_append_str (&buf, &len, "Network1 {");
+ if (bzobj->d_network.interface)
+ nm_utils_strbuf_append (&buf, &len, " d.interface: \"%s\", ", bzobj->d_network.interface);
+ if (bzobj->d_network_connected)
+ nm_utils_strbuf_append (&buf, &len, " d.connected: %d, ", !!bzobj->d_network_connected);
+ if (!bzobj->d_has_network_iface)
+ nm_utils_strbuf_append (&buf, &len, " has-d-iface: 0, ");
+ if (device_is_usable != bzobj->x_device_is_usable)
+ nm_utils_strbuf_append (&buf, &len, " usable: %d, used: %d", !!device_is_usable, !device_is_usable);
+ else if (device_is_usable)
+ nm_utils_strbuf_append (&buf, &len, " used: 1");
+ else
+ nm_utils_strbuf_append (&buf, &len, " usable: 0");
+
+ if (create_panu_connection)
+ nm_utils_strbuf_append (&buf, &len, ", create-panu-connection: 1");
+
+ if (bzobj->x_device.panu_connection)
+ nm_utils_strbuf_append (&buf, &len, ", has-panu-connection: 1");
+
+ if (bzobj->x_device.device_bt)
+ nm_utils_strbuf_append (&buf, &len, ", has-device: 1");
+
+ if ( bzobj->x_device_connect_bt_type != NM_BT_CAPABILITY_NONE
+ || bzobj->x_device.connect_dun_context) {
+ nm_utils_strbuf_append (&buf, &len, ", connect: %s%s",
+ nm_bluetooth_capability_to_string (bzobj->x_device_connect_bt_type, sbuf_cap, sizeof (sbuf_cap)),
+ bzobj->x_device.connect_dun_context ? ",with-dun-context" : "");
+ }
+
+ if (bzobj->x_device.c_req_data)
+ nm_utils_strbuf_append (&buf, &len, ", connecting: 1");
+
+ if (bzobj->x_device_is_connected != bzobj->d_network_connected)
+ nm_utils_strbuf_append (&buf, &len, ", connected: %d", !!bzobj->x_device_is_connected);
+
+ nm_utils_strbuf_append_str (&buf, &len, " }");
+ }
+
+ if (_bzobjs_is_dead (bzobj)) {
+ nm_utils_strbuf_append_str (&buf, &len, prefix);
+ prefix = ", ";
+ nm_utils_strbuf_append_str (&buf, &len, "dead: 1");
+ }
+
+ if (!c_list_is_empty (&bzobj->process_change_lst)) {
+ nm_utils_strbuf_append_str (&buf, &len, prefix);
+ prefix = ", ";
+ nm_utils_strbuf_append (&buf, &len, "change-pending-on-idle: 1");
+ }
+
+ if (_bzobjs_adapter_is_usable_for_device (bzobj) != bzobj->was_usable_adapter_for_device_before) {
+ nm_utils_strbuf_append_str (&buf, &len, prefix);
+ prefix = ", ";
+ nm_utils_strbuf_append (&buf, &len, "change-usable-adapter-for-device: 1");
+ }
+
+ return buf0;
+}
+
+#define _LOG_bzobj(bzobj, context) \
+ G_STMT_START { \
+ const BzDBusObj *const _bzobj = (bzobj); \
+ char _buf[500]; \
+ \
+ _LOGT ("change %-21s %s : { %s }", \
+ (context), \
+ _bzobj->object_path, \
+ _bzobj_to_string (_bzobj, _buf, sizeof (_buf))); \
+ } G_STMT_END
+
+static gboolean
+_bzobjs_is_dead (const BzDBusObj *bzobj)
+{
+ return !bzobj->d_has_adapter_iface
+ && !bzobj->d_has_device_iface
+ && !bzobj->d_has_network_iface
+ && !bzobj->d_has_network_server_iface
+ && c_list_is_empty (&bzobj->process_change_lst);
+}
+
+static BzDBusObj *
+_bzobjs_get (NMBluezManager *self, const char *object_path)
+{
+ return g_hash_table_lookup (NM_BLUEZ_MANAGER_GET_PRIVATE (self)->bzobjs, &object_path);
+}
+
+static BzDBusObj *
+_bzobjs_add (NMBluezManager *self,
+ const char *object_path)
+{
+ NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+ BzDBusObj *bzobj;
+
+ bzobj = _bz_dbus_obj_new (self, object_path);
+ if (!g_hash_table_add (priv->bzobjs, bzobj))
+ nm_assert_not_reached ();
+ return bzobj;
+}
+
+static void
+_bzobjs_del (BzDBusObj *bzobj)
+{
+ nm_assert (bzobj);
+ nm_assert (bzobj == _bzobjs_get (bzobj->self, bzobj->object_path));
+
+ if (!g_hash_table_remove (NM_BLUEZ_MANAGER_GET_PRIVATE (bzobj->self)->bzobjs, bzobj))
+ nm_assert_not_reached ();
+}
+
+static void
+_bzobjs_del_if_dead (BzDBusObj *bzobj)
+{
+ if (_bzobjs_is_dead (bzobj))
+ _bzobjs_del (bzobj);
+}
+
+static BzDBusObj *
+_bzobjs_init (NMBluezManager *self, BzDBusObj **inout, const char *object_path)
+{
+ nm_assert (NM_IS_BLUEZ_MANAGER (self));
+ nm_assert (object_path);
+ nm_assert (inout);
+
+ if (!*inout) {
+ *inout = _bzobjs_get (self, object_path);
+ if (!*inout)
+ *inout = _bzobjs_add (self, object_path);
+ }
+
+ nm_assert (nm_streq ((*inout)->object_path, object_path));
+ nm_assert (*inout == _bzobjs_get (self, object_path));
+ return *inout;
+}
+
+static gboolean
+_bzobjs_adapter_is_usable_for_device (const BzDBusObj *bzobj)
+{
+ return bzobj->d_has_adapter_iface
+ && bzobj->d_adapter.address
+ && bzobj->d_adapter_powered;
+}
+
+static gboolean
+_bzobjs_device_is_usable (const BzDBusObj *bzobj,
+ BzDBusObj **out_adapter_bzobj,
+ gboolean *out_create_panu_connection)
+{
NMBluezManager *self;
- GCancellable *async_cancellable;
-};
+ NMBluezManagerPrivate *priv;
+ gboolean usable_dun = FALSE;
+ gboolean usable_nap = FALSE;
+ BzDBusObj *bzobj_adapter;
+ gboolean create_panu_connection = FALSE;
+
+ if ( !bzobj->d_has_device_iface
+ || !NM_FLAGS_ANY ((NMBluetoothCapabilities) bzobj->d_device_capabilities, _NM_BT_CAPABILITY_SUPPORTED)
+ || !bzobj->d_device.name
+ || !bzobj->d_device.address
+ || !bzobj->d_device_paired
+ || !bzobj->d_device.adapter)
+ goto out_unusable;
+
+ self = bzobj->self;
+
+ priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+
+ if (!priv->settings_registered)
+ goto out_unusable;
+
+ bzobj_adapter = _bzobjs_get (self, bzobj->d_device.adapter);
+ if ( !bzobj_adapter
+ || !_bzobjs_adapter_is_usable_for_device (bzobj_adapter))
+ goto out_unusable;
-static struct AsyncData *
-async_data_pack (NMBluezManager *self)
+#if WITH_BLUEZ5_DUN
+ if (NM_FLAGS_HAS (bzobj->d_device_capabilities, NM_BT_CAPABILITY_DUN)) {
+ if (_conn_track_find_head (self, NM_BT_CAPABILITY_DUN, bzobj->d_device.address))
+ usable_dun = TRUE;
+ }
+#endif
+
+ if (NM_FLAGS_HAS (bzobj->d_device_capabilities, NM_BT_CAPABILITY_NAP)) {
+ if (!bzobj->d_has_network_iface)
+ usable_nap = FALSE;
+ else if (_conn_track_find_head (self, NM_BT_CAPABILITY_NAP, bzobj->d_device.address))
+ usable_nap = TRUE;
+ else if (bzobj->x_device_panu_connection_allow_create) {
+ /* We didn't yet try to create a connection. Presume we are going to create
+ * it when the time comes... */
+ usable_nap = TRUE;
+ create_panu_connection = TRUE;
+ }
+ }
+
+ if ( !usable_dun
+ && !usable_nap) {
+ if ( bzobj->x_device.device_bt
+ && nm_device_get_state (NM_DEVICE (bzobj->x_device.device_bt)) > NM_DEVICE_STATE_DISCONNECTED) {
+ /* The device is still activated... the absence of a profile does not
+ * render it unusable (yet). But since there is no more profile, the
+ * device is probably about to disconnect. */
+ } else
+ goto out_unusable;
+ }
+
+ NM_SET_OUT (out_create_panu_connection, create_panu_connection);
+ NM_SET_OUT (out_adapter_bzobj, bzobj_adapter);
+ return TRUE;
+
+out_unusable:
+ NM_SET_OUT (out_create_panu_connection, FALSE);
+ NM_SET_OUT (out_adapter_bzobj, NULL);
+ return FALSE;
+}
+
+static gboolean
+_bzobjs_device_is_connected (const BzDBusObj *bzobj)
+{
+ nm_assert (_bzobjs_device_is_usable (bzobj, NULL, NULL));
+
+ if ( !bzobj->d_has_device_iface
+ || !bzobj->d_device_connected)
+ return FALSE;
+
+ if ( bzobj->d_has_network_iface
+ && bzobj->d_network_connected)
+ return TRUE;
+ if (bzobj->x_device.connect_dun_context) {
+ /* As long as we have a dun-context, we consider it connected.
+ *
+ * We require NMDeviceBt to try to connect to the modem, and if that fails,
+ * it will disconnect. */
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean
+_bzobjs_network_server_is_usable (const BzDBusObj *bzobj,
+ gboolean require_powered)
+{
+ return bzobj->d_has_network_server_iface
+ && bzobj->d_has_adapter_iface
+ && bzobj->d_adapter.address
+ && ( !require_powered
+ || bzobj->d_adapter_powered);
+}
+
+/*****************************************************************************/
+
+static ConnDataHead *
+_conn_data_head_new (NMBluetoothCapabilities bt_type,
+ const char *bdaddr)
+{
+ ConnDataHead *cdata_hd;
+ gsize l;
+
+ nm_assert (NM_IN_SET (bt_type, NM_BT_CAPABILITY_DUN,
+ NM_BT_CAPABILITY_NAP));
+ nm_assert (bdaddr);
+
+ l = strlen (bdaddr) + 1;
+ cdata_hd = g_malloc (sizeof (ConnDataHead) + l);
+ *cdata_hd = (ConnDataHead) {
+ .bdaddr = cdata_hd->bdaddr_data,
+ .lst_head = C_LIST_INIT (cdata_hd->lst_head),
+ .bt_type = bt_type,
+ };
+ memcpy (cdata_hd->bdaddr_data, bdaddr, l);
+
+ nm_assert (cdata_hd->bt_type == bt_type);
+
+ return cdata_hd;
+}
+
+static guint
+_conn_data_head_hash (gconstpointer ptr)
+{
+ const ConnDataHead *cdata_hd = ptr;
+ NMHashState h;
+
+ nm_hash_init (&h, 520317467u);
+ nm_hash_update_val (&h, (NMBluetoothCapabilities) cdata_hd->bt_type);
+ nm_hash_update_str (&h, cdata_hd->bdaddr);
+ return nm_hash_complete (&h);
+}
+
+static gboolean
+_conn_data_head_equal (gconstpointer a, gconstpointer b)
+{
+ const ConnDataHead *cdata_hd_a = a;
+ const ConnDataHead *cdata_hd_b = b;
+
+ return cdata_hd_a->bt_type == cdata_hd_b->bt_type
+ && nm_streq (cdata_hd_a->bdaddr, cdata_hd_b->bdaddr);
+}
+
+static ConnDataHead *
+_conn_track_find_head (NMBluezManager *self,
+ NMBluetoothCapabilities bt_type,
+ const char *bdaddr)
+{
+ ConnDataHead cdata_hd = {
+ .bt_type = bt_type,
+ .bdaddr = bdaddr,
+ };
+
+ return g_hash_table_lookup (NM_BLUEZ_MANAGER_GET_PRIVATE (self)->conn_data_heads, &cdata_hd);
+}
+
+static ConnDataElem *
+_conn_track_find_elem (NMBluezManager *self,
+ NMSettingsConnection *sett_conn)
+{
+ G_STATIC_ASSERT (G_STRUCT_OFFSET (ConnDataElem, sett_conn) == 0);
+
+ return g_hash_table_lookup (NM_BLUEZ_MANAGER_GET_PRIVATE (self)->conn_data_elems, &sett_conn);
+}
+
+static gboolean
+_conn_track_is_relevant_connection (NMConnection *connection,
+ NMBluetoothCapabilities *out_bt_type,
+ const char **out_bdaddr)
+{
+ NMSettingBluetooth *s_bt;
+ NMBluetoothCapabilities bt_type;
+ const char *bdaddr;
+ const char *b_type;
+
+ s_bt = nm_connection_get_setting_bluetooth (connection);
+ if (!s_bt)
+ return FALSE;
+
+ if (!nm_connection_is_type (connection, NM_SETTING_BLUETOOTH_SETTING_NAME))
+ return FALSE;
+
+ bdaddr = nm_setting_bluetooth_get_bdaddr (s_bt);
+ if (!bdaddr)
+ return FALSE;
+
+ b_type = nm_setting_bluetooth_get_connection_type (s_bt);
+
+ if (nm_streq (b_type, NM_SETTING_BLUETOOTH_TYPE_DUN))
+ bt_type = NM_BT_CAPABILITY_DUN;
+ else if (nm_streq (b_type, NM_SETTING_BLUETOOTH_TYPE_PANU))
+ bt_type = NM_BT_CAPABILITY_NAP;
+ else
+ return FALSE;
+
+ NM_SET_OUT (out_bt_type, bt_type);
+ NM_SET_OUT (out_bdaddr, bdaddr);
+ return TRUE;
+}
+
+static gboolean
+_conn_track_is_relevant_sett_conn (NMSettingsConnection *sett_conn,
+ NMBluetoothCapabilities *out_bt_type,
+ const char **out_bdaddr)
+{
+ NMConnection *connection;
+
+ connection = nm_settings_connection_get_connection (sett_conn);
+ if (!connection)
+ return FALSE;
+
+ return _conn_track_is_relevant_connection (connection, out_bt_type, out_bdaddr);
+}
+
+static gboolean
+_conn_track_is_relevant_for_sett_conn (NMSettingsConnection *sett_conn,
+ NMBluetoothCapabilities bt_type,
+ const char *bdaddr)
+{
+ NMBluetoothCapabilities x_bt_type;
+ const char *x_bdaddr;
+
+ return bdaddr
+ && _conn_track_is_relevant_sett_conn (sett_conn, &x_bt_type, &x_bdaddr)
+ && x_bt_type == bt_type
+ && nm_streq (x_bdaddr, bdaddr);
+}
+
+static void
+_conn_track_schedule_notify (NMBluezManager *self,
+ NMBluetoothCapabilities bt_type,
+ const char *bdaddr)
+{
+ NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+ GHashTableIter iter;
+ BzDBusObj *bzobj;
+
+ g_hash_table_iter_init (&iter, priv->bzobjs);
+ while (g_hash_table_iter_next (&iter, (gpointer *) &bzobj, NULL)) {
+ gboolean device_is_usable;
+
+ device_is_usable = _bzobjs_device_is_usable (bzobj, NULL, NULL);
+ if (bzobj->x_device_is_usable != device_is_usable)
+ _process_change_idle_schedule (self, bzobj);
+ }
+}
+
+static void
+_conn_track_update (NMBluezManager *self,
+ NMSettingsConnection *sett_conn,
+ gboolean track,
+ gboolean *out_changed,
+ gboolean *out_changed_usable,
+ ConnDataElem **out_conn_data_elem)
+{
+ NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+ ConnDataHead *cdata_hd;
+ ConnDataElem *cdata_el;
+ ConnDataElem *cdata_el_remove = NULL;
+ NMBluetoothCapabilities bt_type;
+ const char *bdaddr;
+ gboolean changed = FALSE;
+ gboolean changed_usable = FALSE;
+ char sbuf_cap[100];
+
+ nm_assert (NM_IS_SETTINGS_CONNECTION (sett_conn));
+
+ cdata_el = _conn_track_find_elem (self, sett_conn);
+
+ if (track)
+ track = _conn_track_is_relevant_sett_conn (sett_conn, &bt_type, &bdaddr);
+
+ if (!track) {
+ cdata_el_remove = g_steal_pointer (&cdata_el);
+ goto out_remove;
+ }
+
+ if (cdata_el) {
+ cdata_hd = cdata_el->cdata_hd;
+ if ( cdata_hd->bt_type != bt_type
+ || !nm_streq (cdata_hd->bdaddr, bdaddr))
+ cdata_el_remove = g_steal_pointer (&cdata_el);
+ }
+
+ if (!cdata_el) {
+ _LOGT ("connecton: track for %s, %s: %s (%s)",
+ nm_bluetooth_capability_to_string (bt_type, sbuf_cap, sizeof (sbuf_cap)),
+ bdaddr,
+ nm_settings_connection_get_uuid (sett_conn),
+ nm_settings_connection_get_id (sett_conn));
+ changed = TRUE;
+ cdata_hd = _conn_track_find_head (self, bt_type, bdaddr);
+ if (!cdata_hd) {
+ changed_usable = TRUE;
+ cdata_hd = _conn_data_head_new (bt_type, bdaddr);
+ if (!g_hash_table_add (priv->conn_data_heads, cdata_hd))
+ nm_assert_not_reached ();
+ _conn_track_schedule_notify (self, bt_type, bdaddr);
+ }
+ cdata_el = g_slice_new (ConnDataElem);
+ cdata_el->sett_conn = sett_conn;
+ cdata_el->cdata_hd = cdata_hd;
+ c_list_link_tail (&cdata_hd->lst_head, &cdata_el->lst);
+ if (!g_hash_table_add (priv->conn_data_elems, cdata_el))
+ nm_assert_not_reached ();
+ }
+
+out_remove:
+ if (cdata_el_remove) {
+ GHashTableIter iter;
+ BzDBusObj *bzobj;
+
+ _LOGT ("connecton: untrack for %s, %s: %s (%s)",
+ nm_bluetooth_capability_to_string (cdata_el_remove->cdata_hd->bt_type, sbuf_cap, sizeof (sbuf_cap)),
+ cdata_el_remove->cdata_hd->bdaddr,
+ nm_settings_connection_get_uuid (sett_conn),
+ nm_settings_connection_get_id (sett_conn));
+
+ g_hash_table_iter_init (&iter, priv->bzobjs);
+ while (g_hash_table_iter_next (&iter, (gpointer *) &bzobj, NULL)) {
+ if (bzobj->x_device.panu_connection == sett_conn)
+ bzobj->x_device.panu_connection = NULL;
+ }
+
+ changed = TRUE;
+ cdata_hd = cdata_el_remove->cdata_hd;
+ c_list_unlink_stale (&cdata_el_remove->lst);
+ if (!g_hash_table_remove (priv->conn_data_elems, cdata_el_remove))
+ nm_assert_not_reached ();
+ if (c_list_is_empty (&cdata_hd->lst_head)) {
+ changed_usable = TRUE;
+ _conn_track_schedule_notify (self, cdata_hd->bt_type, cdata_hd->bdaddr);
+ if (!g_hash_table_remove (priv->conn_data_heads, cdata_hd))
+ nm_assert_not_reached ();
+ }
+ }
+
+ NM_SET_OUT (out_changed, changed);
+ NM_SET_OUT (out_changed_usable, changed_usable);
+ NM_SET_OUT (out_conn_data_elem, cdata_el);
+}
+
+/*****************************************************************************/
+
+static void
+cp_connection_added (NMSettings *settings,
+ NMSettingsConnection *sett_conn,
+ NMBluezManager *self)
{
- struct AsyncData *data = g_new (struct AsyncData, 1);
+ _conn_track_update (self, sett_conn, TRUE, NULL, NULL, NULL);
+}
- data->self = self;
- data->async_cancellable = g_object_ref (NM_BLUEZ_MANAGER_GET_PRIVATE (self)->async_cancellable);
- return data;
+static void
+cp_connection_updated (NMSettings *settings,
+ NMSettingsConnection *sett_conn,
+ guint update_reason_u,
+ NMBluezManager *self)
+{
+ _conn_track_update (self, sett_conn, TRUE, NULL, NULL, NULL);
+}
+
+static void
+cp_connection_removed (NMSettings *settings,
+ NMSettingsConnection *sett_conn,
+ NMBluezManager *self)
+{
+ _conn_track_update (self, sett_conn, FALSE, NULL, NULL, NULL);
}
+/*****************************************************************************/
+
static NMBluezManager *
-async_data_unpack (struct AsyncData *async_data)
+_network_server_get_bluez_manager (const NMBtVTableNetworkServer *vtable_network_server)
{
- NMBluezManager *self = g_cancellable_is_cancelled (async_data->async_cancellable)
- ? NULL : async_data->self;
+ NMBluezManager *self;
+
+ self = (NMBluezManager *) (((char *) vtable_network_server) - G_STRUCT_OFFSET (NMBluezManager, _priv.vtable_network_server));
+
+ g_return_val_if_fail (NM_IS_BLUEZ_MANAGER (self), NULL);
- g_object_unref (async_data->async_cancellable);
- g_free (async_data);
return self;
}
-/**
- * Cancel any current attempt to detect the version and cleanup
- * the related fields.
- **/
+static BzDBusObj *
+_network_server_find_has_device (NMBluezManagerPrivate *priv,
+ NMDevice *device)
+{
+ BzDBusObj *bzobj;
+
+ c_list_for_each_entry (bzobj, &priv->network_server_lst_head, x_network_server.lst) {
+ if (bzobj->x_network_server.device_br == device)
+ return bzobj;
+ }
+ return NULL;
+}
+
+static BzDBusObj *
+_network_server_find_available (NMBluezManagerPrivate *priv,
+ const char *addr,
+ NMDevice *device_accept_busy)
+{
+ BzDBusObj *bzobj;
+
+ c_list_for_each_entry (bzobj, &priv->network_server_lst_head, x_network_server.lst) {
+ if (bzobj->x_network_server.device_br) {
+ if (bzobj->x_network_server.device_br != device_accept_busy)
+ continue;
+ }
+ if ( addr
+ && !nm_streq (addr, bzobj->d_adapter.address))
+ continue;
+ nm_assert (!bzobj->x_network_server.r_req_data);
+ return bzobj;
+ }
+ return NULL;
+}
+
+static gboolean
+_network_server_vt_is_available (const NMBtVTableNetworkServer *vtable,
+ const char *addr,
+ NMDevice *device_accept_busy)
+{
+ NMBluezManager *self = _network_server_get_bluez_manager (vtable);
+ NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+
+ return !!_network_server_find_available (priv, addr, device_accept_busy);
+}
+
static void
-cleanup_checking (NMBluezManager *self, gboolean do_unwatch_name)
+_network_server_register_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
{
+ gs_unref_variant GVariant *ret = NULL;
+ gs_free_error GError *error = NULL;
+ BzDBusObj *bzobj;
+
+ ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), res, &error);
+ if ( !ret
+ && nm_utils_error_is_cancelled (error, FALSE))
+ return;
+
+ bzobj = user_data;
+
+ if (!ret) {
+ _LOGT ("NAP: [%s]: registering failed: %s", bzobj->object_path, error->message);
+ } else
+ _LOGT ("NAP: [%s]: registration successful", bzobj->object_path);
+
+ g_clear_object (&bzobj->x_network_server.r_req_data->int_cancellable);
+ _network_server_register_req_data_complete (g_steal_pointer (&bzobj->x_network_server.r_req_data), error);
+}
+
+static void
+_network_server_register_cancelled_cb (GCancellable *cancellable,
+ BzDBusObj *bzobj)
+{
+ _network_server_unregister_bridge (bzobj->self, bzobj, "registration cancelled");
+}
+
+static gboolean
+_network_server_vt_register_bridge (const NMBtVTableNetworkServer *vtable,
+ const char *addr,
+ NMDevice *device,
+ GCancellable *cancellable,
+ NMBtVTableRegisterCallback callback,
+ gpointer callback_user_data,
+ GError **error)
+{
+ NMBluezManager *self = _network_server_get_bluez_manager (vtable);
NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+ NetworkServerRegisterReqData *r_req_data;
+ BzDBusObj *bzobj;
+ const char *ifname;
+
+ g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
+ g_return_val_if_fail (G_IS_CANCELLABLE (cancellable), FALSE);
+
+ nm_assert (!g_cancellable_is_cancelled (cancellable));
+ nm_assert (!_network_server_find_has_device (priv, device));
+
+ ifname = nm_device_get_iface (device);
+ g_return_val_if_fail (ifname, FALSE);
+
+ g_return_val_if_fail (ifname, FALSE);
+
+ bzobj = _network_server_find_available (priv, addr, NULL);
+ if (!bzobj) {
+ /* The device checked that a network server is available, before
+ * starting the activation, but for some reason it no longer is.
+ * Indicate that the activation should not proceed. */
+ if (addr) {
+ nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
+ "adapter %s is not available for %s",
+ addr, ifname);
+ } else {
+ nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
+ "no adapter available for %s",
+ ifname);
+ }
+ return FALSE;
+ }
+
+ _LOGD ("NAP: [%s]: registering \"%s\" on adapter %s",
+ bzobj->object_path,
+ ifname,
+ bzobj->d_adapter.address);
+
+ r_req_data = g_slice_new (NetworkServerRegisterReqData);
+ *r_req_data = (NetworkServerRegisterReqData) {
+ .int_cancellable = g_cancellable_new (),
+ .ext_cancellable = g_object_ref (cancellable),
+ .callback = callback,
+ .callback_user_data = callback_user_data,
+ .ext_cancelled_id = g_signal_connect (cancellable,
+ "cancelled",
+ G_CALLBACK (_network_server_register_cancelled_cb),
+ bzobj),
+ };
+
+ bzobj->x_network_server.device_br = g_object_ref (device);
+ bzobj->x_network_server.r_req_data = r_req_data;
+
+ g_dbus_connection_call (priv->dbus_connection,
+ priv->name_owner,
+ bzobj->object_path,
+ NM_BLUEZ5_NETWORK_SERVER_INTERFACE,
+ "Register",
+ g_variant_new ("(ss)",
+ BLUETOOTH_CONNECT_NAP,
+ ifname),
+ NULL,
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ -1,
+ bzobj->x_network_server.r_req_data->int_cancellable,
+ _network_server_register_cb,
+ bzobj);
+ return TRUE;
+}
+
+static void
+_network_server_unregister_bridge_complete_on_idle_cb (gpointer user_data,
+ GCancellable *cancellable)
+{
+ gs_free_error GError *error = NULL;
+ gs_free char *reason = NULL;
+ NetworkServerRegisterReqData *r_req_data;
+
+ nm_utils_user_data_unpack (user_data, &r_req_data, &reason);
+
+ nm_utils_error_set (&error, NM_UTILS_ERROR_UNKNOWN,
+ "registration was aborted due to %s",
+ reason);
+ _network_server_register_req_data_complete (r_req_data, error);
+}
+
+static void
+_network_server_unregister_bridge (NMBluezManager *self,
+ BzDBusObj *bzobj,
+ const char *reason)
+{
+ NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+ _nm_unused gs_unref_object NMDevice *device = NULL;
+ NetworkServerRegisterReqData *r_req_data;
+
+ nm_assert (NM_IS_DEVICE (bzobj->x_network_server.device_br));
+
+ _LOGD ("NAP: [%s]: unregistering \"%s\" (%s)",
+ bzobj->object_path,
+ nm_device_get_iface (bzobj->x_network_server.device_br),
+ reason);
+
+ device = g_steal_pointer (&bzobj->x_network_server.device_br);
+
+ r_req_data = g_steal_pointer (&bzobj->x_network_server.r_req_data);
+
+ if (priv->name_owner) {
+ gs_unref_object GCancellable *cancellable = NULL;
+
+ cancellable = g_cancellable_new ();
- nm_clear_g_cancellable (&priv->async_cancellable);
+ nm_shutdown_wait_obj_register_cancellable_full (cancellable,
+ g_strdup_printf ("bt-unregister-nap[%s]", bzobj->object_path),
+ TRUE);
- g_clear_object (&priv->introspect_proxy);
+ g_dbus_connection_call (priv->dbus_connection,
+ priv->name_owner,
+ bzobj->object_path,
+ NM_BLUEZ5_NETWORK_SERVER_INTERFACE,
+ "Unregister",
+ g_variant_new ("(s)", BLUETOOTH_CONNECT_NAP),
+ NULL,
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ -1,
+ cancellable,
+ _dbus_call_complete_cb_nop,
+ NULL);
+ }
- if (do_unwatch_name && priv->watch_name_id) {
- g_bus_unwatch_name (priv->watch_name_id);
- priv->watch_name_id = 0;
+ if (r_req_data) {
+ nm_clear_g_cancellable (&r_req_data->int_cancellable);
+ nm_utils_invoke_on_idle (_network_server_unregister_bridge_complete_on_idle_cb,
+ nm_utils_user_data_pack (r_req_data, g_strdup (reason)),
+ r_req_data->ext_cancellable);
}
+
+ _nm_device_bridge_notify_unregister_bt_nap (device, reason);
+}
+
+static gboolean
+_network_server_vt_unregister_bridge (const NMBtVTableNetworkServer *vtable,
+ NMDevice *device)
+{
+ NMBluezManager *self = _network_server_get_bluez_manager (vtable);
+ NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+ BzDBusObj *bzobj;
+
+ g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
+
+ bzobj = _network_server_find_has_device (priv, device);
+ if (bzobj)
+ _network_server_unregister_bridge (self, bzobj, "disconnecting");
+
+ return TRUE;
}
static void
-manager_bdaddr_added_cb (GObject *manager,
- NMBluezDevice *bt_device,
- const char *bdaddr,
- const char *name,
- const char *object_path,
- guint32 capabilities,
- gpointer user_data)
+_network_server_process_change (BzDBusObj *bzobj,
+ gboolean *out_emit_device_availability_changed)
{
- NMBluezManager *self = NM_BLUEZ_MANAGER (user_data);
- NMDevice *device;
- gboolean has_dun = (capabilities & NM_BT_CAPABILITY_DUN);
- gboolean has_nap = (capabilities & NM_BT_CAPABILITY_NAP);
+ NMBluezManager *self = bzobj->self;
+ NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+ gboolean network_server_is_usable;
+ gboolean emit_device_availability_changed = FALSE;
+
+ network_server_is_usable = _bzobjs_network_server_is_usable (bzobj, TRUE);
+
+ if (!network_server_is_usable) {
+
+ if (!c_list_is_empty (&bzobj->x_network_server.lst)) {
+ emit_device_availability_changed = TRUE;
+ c_list_unlink (&bzobj->x_network_server.lst);
+ }
+
+ nm_clear_g_free (&bzobj->x_network_server.adapter_address);
+
+ if (bzobj->x_network_server.device_br) {
+ _network_server_unregister_bridge (self,
+ bzobj,
+ _bzobjs_network_server_is_usable (bzobj, FALSE)
+ ? "adapter disabled"
+ : "adapter disappeared");
+ }
+
+ } else {
+
+ if (!nm_streq0 (bzobj->x_network_server.adapter_address, bzobj->d_adapter.address)) {
+ emit_device_availability_changed = TRUE;
+ g_free (bzobj->x_network_server.adapter_address);
+ bzobj->x_network_server.adapter_address = g_strdup (bzobj->d_adapter.address);
+ }
- g_return_if_fail (bdaddr != NULL);
- 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));
+ if (c_list_is_empty (&bzobj->x_network_server.lst)) {
+ emit_device_availability_changed = TRUE;
+ c_list_link_tail (&priv->network_server_lst_head, &bzobj->x_network_server.lst);
+ }
- device = nm_device_bt_new (bt_device, object_path, bdaddr, name, capabilities);
- if (!device)
+ }
+
+ if (emit_device_availability_changed)
+ NM_SET_OUT (out_emit_device_availability_changed, TRUE);
+}
+
+/*****************************************************************************/
+
+static void
+_conn_create_panu_connection (NMBluezManager *self,
+ BzDBusObj *bzobj)
+{
+ NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+ gs_unref_object NMConnection *connection = NULL;
+ NMSettingsConnection *added;
+ NMSetting *setting;
+ gs_free char *id = NULL;
+ char uuid[37];
+ gs_free_error GError *error = NULL;
+
+ nm_utils_uuid_generate_buf (uuid);
+ id = g_strdup_printf (_("%s Network"), bzobj->d_device.name);
+
+ connection = nm_simple_connection_new ();
+
+ 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 = nm_setting_bluetooth_new ();
+ g_object_set (setting,
+ NM_SETTING_BLUETOOTH_BDADDR, bzobj->d_device.address,
+ NM_SETTING_BLUETOOTH_TYPE, NM_SETTING_BLUETOOTH_TYPE_PANU,
+ NULL);
+ nm_connection_add_setting (connection, setting);
+
+ if (!nm_connection_normalize (connection, NULL, NULL, &error)) {
+ _LOGE ("connection: couldn't generate a connection for NAP device: %s",
+ error->message);
+ g_return_if_reached ();
+ }
+
+ nm_assert (_conn_track_is_relevant_connection (connection, NULL, NULL));
+
+ _LOGT ("connection: create in-memory PANU connection %s (%s) for device \"%s\" (%s)",
+ uuid,
+ id,
+ bzobj->d_device.name,
+ bzobj->d_device.address);
+
+ nm_settings_add_connection (priv->settings,
+ connection,
+ NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
+ NM_SETTINGS_CONNECTION_ADD_REASON_NONE,
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED,
+ &added,
+ &error);
+ if (!added) {
+ _LOGW ("connection: couldn't add new Bluetooth connection for NAP device: '%s' (%s): %s",
+ id, uuid, error->message);
return;
+ }
+
+ if ( !_conn_track_is_relevant_for_sett_conn (added, NM_BT_CAPABILITY_NAP, bzobj->d_device.address)
+ || !_conn_track_find_elem (self, added)
+ || bzobj->x_device.panu_connection) {
+ _LOGE ("connection: something went wrong creating PANU connection %s (%s) for device '%s'",
+ uuid, id, bzobj->d_device.address);
+ g_return_if_reached ();
+ }
+
+ bzobj->x_device.panu_connection = added;
+}
+
+/*****************************************************************************/
+
+static void
+_device_state_changed_cb (NMDevice *device,
+ guint new_state_u,
+ guint old_state_u,
+ guint reason_u,
+ gpointer user_data)
+{
+ BzDBusObj *bzobj = user_data;
- _LOGI ("BT device %s (%s) added (%s%s%s)",
- name,
- bdaddr,
- has_dun ? "DUN" : "",
- has_dun && has_nap ? " " : "",
- has_nap ? "NAP" : "");
- g_signal_emit_by_name (self, NM_DEVICE_FACTORY_DEVICE_ADDED, device);
- g_object_unref (device);
+ if (!_bzobjs_device_is_usable (bzobj, NULL, NULL)) {
+ /* the device got unusable? Need to revisit it... */
+ _process_change_idle_schedule (bzobj->self, bzobj);
+ }
}
static void
-manager_network_server_added_cb (GObject *manager,
- gpointer user_data)
+_device_process_change (BzDBusObj *bzobj)
{
- nm_device_factory_emit_component_added (NM_DEVICE_FACTORY (user_data), NULL);
+ NMBluezManager *self = bzobj->self;
+ gs_unref_object NMDeviceBt *device_added = NULL;
+ gs_unref_object NMDeviceBt *device_deleted = NULL;
+ gboolean device_is_usable;
+ gboolean create_panu_connection = FALSE;
+
+ device_is_usable = _bzobjs_device_is_usable (bzobj, NULL, &create_panu_connection);
+
+ if (create_panu_connection) {
+ bzobj->x_device_panu_connection_allow_create = FALSE;
+ _conn_create_panu_connection (self, bzobj);
+ device_is_usable = _bzobjs_device_is_usable (bzobj, NULL, NULL);
+ } else {
+ if ( device_is_usable
+ && bzobj->x_device_panu_connection_allow_create
+ && NM_FLAGS_HAS (bzobj->d_device_capabilities, NM_BT_CAPABILITY_NAP)
+ && _conn_track_find_head (self, NM_BT_CAPABILITY_NAP, bzobj->d_device.address) ) {
+ /* We have a useable device and also a panu-connection. We block future attemps
+ * to generate a connection. */
+ bzobj->x_device_panu_connection_allow_create = FALSE;
+ }
+ if (bzobj->x_device.panu_connection) {
+ if (!NM_FLAGS_HAS (nm_settings_connection_get_flags (bzobj->x_device.panu_connection),
+ NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)) {
+ /* the connection that we generated earlier still exists, but it's not longer the same
+ * as it was when we created it. Forget about it, so that we don't delete the profile later... */
+ bzobj->x_device.panu_connection = NULL;
+ } else {
+ if ( !device_is_usable
+ || !_conn_track_is_relevant_for_sett_conn (bzobj->x_device.panu_connection,
+ NM_BT_CAPABILITY_NAP,
+ bzobj->d_device.address)) {
+ _LOGT ("connection: delete in-memory PANU connection %s (%s) as device %s",
+ nm_settings_connection_get_uuid (bzobj->x_device.panu_connection),
+ nm_settings_connection_get_id (bzobj->x_device.panu_connection),
+ !device_is_usable ? "is now unusable" : "no longer matches");
+ bzobj->x_device_panu_connection_allow_create = TRUE;
+ nm_settings_connection_delete (g_steal_pointer (&bzobj->x_device.panu_connection), FALSE);
+ }
+ }
+ }
+ }
+
+ bzobj->x_device_is_connected = device_is_usable
+ && _bzobjs_device_is_connected (bzobj);
+
+ bzobj->x_device_is_usable = device_is_usable;
+
+ if (bzobj->x_device.device_bt) {
+ const char *device_to_delete_msg;
+
+ if (!device_is_usable)
+ device_to_delete_msg = "device became unusable";
+ else if (!_nm_device_bt_for_same_device (bzobj->x_device.device_bt,
+ bzobj->object_path,
+ bzobj->d_device.address,
+ NULL,
+ bzobj->d_device_capabilities))
+ device_to_delete_msg = "device is no longer compatible";
+ else
+ device_to_delete_msg = NULL;
+
+ if (device_to_delete_msg) {
+ nm_clear_g_signal_handler (bzobj->x_device.device_bt, &bzobj->x_device.device_bt_signal_id);
+
+ device_deleted = g_steal_pointer (&bzobj->x_device.device_bt);
+
+ _LOGD ("[%s]: drop device because %s",
+ bzobj->object_path,
+ device_to_delete_msg);
+
+ _connect_disconnect (self, bzobj, device_to_delete_msg);
+ }
+ }
+
+ if (device_is_usable) {
+ if (!bzobj->x_device.device_bt) {
+ bzobj->x_device.device_bt = nm_device_bt_new (self,
+ bzobj->object_path,
+ bzobj->d_device.address,
+ bzobj->d_device.name,
+ bzobj->d_device_capabilities);
+ device_added = g_object_ref (bzobj->x_device.device_bt);
+ bzobj->x_device.device_bt_signal_id = g_signal_connect (device_added,
+ NM_DEVICE_STATE_CHANGED,
+ G_CALLBACK (_device_state_changed_cb),
+ bzobj);
+ } else
+ _nm_device_bt_notify_set_name (bzobj->x_device.device_bt, bzobj->d_device.name);
+
+ _nm_device_bt_notify_set_connected (bzobj->x_device.device_bt, bzobj->x_device_is_connected);
+ }
+
+ if ( bzobj->x_device.c_req_data
+ && !bzobj->x_device.c_req_data->int_cancellable
+ && bzobj->x_device_is_connected) {
+ gs_free char *device_name = g_steal_pointer (&bzobj->x_device.c_req_data->device_name);
+
+ _device_connect_req_data_complete (g_steal_pointer (&bzobj->x_device.c_req_data),
+ self,
+ device_name,
+ NULL);
+ }
+
+ if (device_added)
+ g_signal_emit_by_name (self, NM_DEVICE_FACTORY_DEVICE_ADDED, device_added);
+
+ if (device_deleted)
+ _nm_device_bt_notify_removed (device_deleted);
}
+/*****************************************************************************/
+
static void
-setup_bluez5 (NMBluezManager *self)
+_process_change_idle_all (NMBluezManager *self,
+ gboolean *out_emit_device_availability_changed)
{
- NMBluez5Manager *manager;
NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+ BzDBusObj *bzobj;
+
+ while ((bzobj = c_list_first_entry (&priv->process_change_lst_head, BzDBusObj, process_change_lst))) {
+
+ c_list_unlink (&bzobj->process_change_lst);
- g_return_if_fail (!priv->manager5);
+ _LOG_bzobj (bzobj, "before-processing");
- cleanup_checking (self, TRUE);
+ _device_process_change (bzobj);
- priv->manager5 = manager = nm_bluez5_manager_new (priv->settings);
+ _network_server_process_change (bzobj, out_emit_device_availability_changed);
- g_signal_connect (manager,
- NM_BLUEZ_MANAGER_BDADDR_ADDED,
- G_CALLBACK (manager_bdaddr_added_cb),
- self);
- g_signal_connect (manager,
- NM_BLUEZ_MANAGER_NETWORK_SERVER_ADDED,
- G_CALLBACK (manager_network_server_added_cb),
- self);
+ _LOG_bzobj (bzobj, "after-processing");
- nm_bluez5_manager_query_devices (manager);
+ _bzobjs_del_if_dead (bzobj);
+ }
+
+ nm_clear_g_source (&priv->process_change_idle_id);
+}
+
+static gboolean
+_process_change_idle_cb (gpointer user_data)
+{
+ NMBluezManager *self = user_data;
+ NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+ gboolean emit_device_availability_changed = FALSE;
+
+ _process_change_idle_all (self, &emit_device_availability_changed);
+
+ if (emit_device_availability_changed)
+ nm_manager_notify_device_availibility_maybe_changed (priv->manager);
+
+ return G_SOURCE_CONTINUE;
}
static void
-watch_name_on_appeared (GDBusConnection *connection,
- const char *name,
- const char *name_owner,
- gpointer user_data)
+_process_change_idle_schedule (NMBluezManager *self,
+ BzDBusObj *bzobj)
{
- check_bluez_and_try_setup (NM_BLUEZ_MANAGER (user_data));
+ NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+
+ nm_c_list_move_tail (&priv->process_change_lst_head, &bzobj->process_change_lst);
+ if (priv->process_change_idle_id == 0)
+ priv->process_change_idle_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, _process_change_idle_cb, self, NULL);
}
static void
-check_bluez_and_try_setup_final_step (NMBluezManager *self, gboolean ready, const char *reason)
+_dbus_process_changes (NMBluezManager *self,
+ BzDBusObj *bzobj,
+ const char *log_reason)
{
NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+ gboolean network_server_is_usable;
+ gboolean adapter_is_usable_for_device;
+ gboolean device_is_usable;
+ gboolean changes = FALSE;
+ gboolean recheck_devices_for_adapter = FALSE;
+
+ nm_assert (bzobj);
+
+ _LOG_bzobj (bzobj, log_reason);
+
+ device_is_usable = _bzobjs_device_is_usable (bzobj, NULL, NULL);
+
+ if (bzobj->x_device_is_usable != device_is_usable)
+ changes = TRUE;
+ else if (bzobj->x_device.device_bt) {
+ if (!device_is_usable)
+ changes = TRUE;
+ else {
+ if ( bzobj->x_device_is_connected != _bzobjs_device_is_connected (bzobj)
+ || !_nm_device_bt_for_same_device (bzobj->x_device.device_bt,
+ bzobj->object_path,
+ bzobj->d_device.address,
+ bzobj->d_device.name,
+ bzobj->d_device_capabilities))
+ changes = TRUE;
+ }
+ }
- if (ready) {
- setup_bluez5 (self);
+ adapter_is_usable_for_device = _bzobjs_adapter_is_usable_for_device (bzobj);
+ if (adapter_is_usable_for_device != bzobj->was_usable_adapter_for_device_before) {
+ /* this function does not modify bzobj in any other cases except here.
+ * Usually changes are processed delayed, in the idle handler.
+ *
+ * But the bzobj->was_usable_adapter_for_device_before only exists to know whether
+ * we need to re-check device availability. It is correct to set the flag
+ * here, right before we checked. */
+ bzobj->was_usable_adapter_for_device_before = adapter_is_usable_for_device;
+ recheck_devices_for_adapter = TRUE;
+ changes = TRUE;
+ }
+
+ if (!changes) {
+ network_server_is_usable = _bzobjs_network_server_is_usable (bzobj, TRUE);
+
+ if (network_server_is_usable != (!c_list_is_empty (&bzobj->x_network_server.lst)))
+ changes = TRUE;
+ else if ( bzobj->x_network_server.device_br
+ && !network_server_is_usable)
+ changes = TRUE;
+ else if (!nm_streq0 (bzobj->d_has_adapter_iface ? bzobj->d_adapter.address : NULL,
+ bzobj->x_network_server.adapter_address))
+ changes = TRUE;
+ }
+
+ if (changes)
+ _process_change_idle_schedule (self, bzobj);
+
+ if (recheck_devices_for_adapter) {
+ GHashTableIter iter;
+ BzDBusObj *bzobj2;
+
+ /* we got a change to the availability of an adapter. We might need to recheck
+ * all devices that use this adapter... */
+ g_hash_table_iter_init (&iter, priv->bzobjs);
+ while (g_hash_table_iter_next (&iter, (gpointer *) &bzobj2, NULL)) {
+ if (bzobj2 == bzobj)
+ continue;
+ if (!nm_streq0 (bzobj2->d_device.adapter, bzobj->object_path))
+ continue;
+ if (c_list_is_empty (&bzobj2->process_change_lst))
+ _dbus_process_changes (self, bzobj2, "adapter-changed");
+ else
+ nm_c_list_move_tail (&priv->process_change_lst_head, &bzobj2->process_change_lst);
+ }
+ }
+
+ _bzobjs_del_if_dead (bzobj);
+}
+
+/*****************************************************************************/
+
+#define ALL_RELEVANT_INTERFACE_NAMES NM_MAKE_STRV (NM_BLUEZ5_ADAPTER_INTERFACE, \
+ NM_BLUEZ5_DEVICE_INTERFACE, \
+ NM_BLUEZ5_NETWORK_INTERFACE, \
+ NM_BLUEZ5_NETWORK_SERVER_INTERFACE)
+
+static gboolean
+_dbus_handle_properties_changed (NMBluezManager *self,
+ const char *object_path,
+ const char *interface_name,
+ GVariant *changed_properties,
+ const char *const*invalidated_properties,
+ BzDBusObj **inout_bzobj)
+{
+ BzDBusObj *bzobj = NULL;
+ gboolean changed = FALSE;
+ const char *property_name;
+ GVariant *property_value;
+ GVariantIter iter_prop;
+ gsize i;
+
+ if (!invalidated_properties)
+ invalidated_properties = NM_PTRARRAY_EMPTY (const char *);
+
+ nm_assert (g_variant_is_of_type (changed_properties, G_VARIANT_TYPE ("a{sv}")));
+
+ if (inout_bzobj) {
+ bzobj = *inout_bzobj;
+ nm_assert (!bzobj || nm_streq (object_path, bzobj->object_path));
+ }
+
+ if (changed_properties)
+ g_variant_iter_init (&iter_prop, changed_properties);
+
+ if (nm_streq (interface_name, NM_BLUEZ5_ADAPTER_INTERFACE)) {
+ _bzobjs_init (self, &bzobj, object_path);
+ if (!bzobj->d_has_adapter_iface) {
+ changed = TRUE;
+ bzobj->d_has_adapter_iface = TRUE;
+ }
+
+ while ( changed_properties
+ && g_variant_iter_next (&iter_prop, "{&sv}", &property_name, &property_value)) {
+ _nm_unused gs_unref_variant GVariant *property_value_free = property_value;
+
+ if (nm_streq (property_name, "Address")) {
+ gs_free char *s = g_variant_is_of_type (property_value, G_VARIANT_TYPE_STRING)
+ ? nm_utils_hwaddr_canonical (g_variant_get_string (property_value, NULL), ETH_ALEN)
+ : NULL;
+
+ if (!nm_streq0 (bzobj->d_adapter.address, s)) {
+ changed = TRUE;
+ nm_clear_g_free (&bzobj->d_adapter.address);
+ bzobj->d_adapter.address = g_steal_pointer (&s);
+ }
+ continue;
+ }
+ if (nm_streq (property_name, "Powered")) {
+ bool v = g_variant_is_of_type (property_value, G_VARIANT_TYPE_BOOLEAN)
+ && g_variant_get_boolean (property_value);
+
+ if (bzobj->d_adapter_powered != v) {
+ changed = TRUE;
+ bzobj->d_adapter_powered = v;
+ }
+ continue;
+ }
+ }
+
+ for (i = 0; (property_name = invalidated_properties[i]); i++) {
+ if (nm_streq (property_name, "Address")) {
+ if (bzobj->d_adapter.address) {
+ changed = TRUE;
+ nm_clear_g_free (&bzobj->d_adapter.address);
+ }
+ continue;
+ }
+ if (nm_streq (property_name, "Powered")) {
+ if (bzobj->d_adapter_powered) {
+ changed = TRUE;
+ bzobj->d_adapter_powered = FALSE;
+ }
+ continue;
+ }
+ }
+
+ } else if (nm_streq (interface_name, NM_BLUEZ5_DEVICE_INTERFACE)) {
+ _bzobjs_init (self, &bzobj, object_path);
+ if (!bzobj->d_has_device_iface) {
+ changed = TRUE;
+ bzobj->d_has_device_iface = TRUE;
+ }
+
+ while ( changed_properties
+ && g_variant_iter_next (&iter_prop, "{&sv}", &property_name, &property_value)) {
+ _nm_unused gs_unref_variant GVariant *property_value_free = property_value;
+
+ if (nm_streq (property_name, "Address")) {
+ gs_free char *s = g_variant_is_of_type (property_value, G_VARIANT_TYPE_STRING)
+ ? nm_utils_hwaddr_canonical (g_variant_get_string (property_value, NULL), ETH_ALEN)
+ : NULL;
+
+ if (!nm_streq0 (bzobj->d_device.address, s)) {
+ changed = TRUE;
+ nm_clear_g_free (&bzobj->d_device.address);
+ bzobj->d_device.address = g_steal_pointer (&s);
+ }
+ continue;
+ }
+ if (nm_streq (property_name, "Name")) {
+ const char *s = g_variant_is_of_type (property_value, G_VARIANT_TYPE_STRING)
+ ? g_variant_get_string (property_value, NULL)
+ : NULL;
+
+ if (!nm_streq0 (bzobj->d_device.name, s)) {
+ changed = TRUE;
+ nm_clear_g_free (&bzobj->d_device.name);
+ bzobj->d_device.name = g_strdup (s);
+ }
+ continue;
+ }
+ if (nm_streq (property_name, "Adapter")) {
+ const char *s = g_variant_is_of_type (property_value, G_VARIANT_TYPE_OBJECT_PATH)
+ ? g_variant_get_string (property_value, NULL)
+ : NULL;
+
+ if (!nm_streq0 (bzobj->d_device.adapter, s)) {
+ changed = TRUE;
+ nm_clear_g_free (&bzobj->d_device.adapter);
+ bzobj->d_device.adapter = g_strdup (s);
+ }
+ continue;
+ }
+ if (nm_streq (property_name, "UUIDs")) {
+ NMBluetoothCapabilities capabilities = NM_BT_CAPABILITY_NONE;
+
+ if (g_variant_is_of_type (property_value, G_VARIANT_TYPE_STRING_ARRAY)) {
+ gs_free const char **s = g_variant_get_strv (property_value, NULL);
+
+ capabilities = convert_uuids_to_capabilities (s);
+ }
+ if (bzobj->d_device_capabilities != capabilities) {
+ changed = TRUE;
+ bzobj->d_device_capabilities = capabilities;
+ nm_assert (bzobj->d_device_capabilities == capabilities);
+ }
+ continue;
+ }
+ if (nm_streq (property_name, "Connected")) {
+ bool v = g_variant_is_of_type (property_value, G_VARIANT_TYPE_BOOLEAN)
+ && g_variant_get_boolean (property_value);
+
+ if (bzobj->d_device_connected != v) {
+ changed = TRUE;
+ bzobj->d_device_connected = v;
+ }
+ continue;
+ }
+ if (nm_streq (property_name, "Paired")) {
+ bool v = g_variant_is_of_type (property_value, G_VARIANT_TYPE_BOOLEAN)
+ && g_variant_get_boolean (property_value);
+
+ if (bzobj->d_device_paired != v) {
+ changed = TRUE;
+ bzobj->d_device_paired = v;
+ }
+ continue;
+ }
+ }
+
+ for (i = 0; (property_name = invalidated_properties[i]); i++) {
+ if (nm_streq (property_name, "Address")) {
+ if (bzobj->d_device.address) {
+ changed = TRUE;
+ nm_clear_g_free (&bzobj->d_device.address);
+ }
+ continue;
+ }
+ if (nm_streq (property_name, "Name")) {
+ if (bzobj->d_device.name) {
+ changed = TRUE;
+ nm_clear_g_free (&bzobj->d_device.name);
+ }
+ continue;
+ }
+ if (nm_streq (property_name, "Adapter")) {
+ if (bzobj->d_device.adapter) {
+ changed = TRUE;
+ nm_clear_g_free (&bzobj->d_device.adapter);
+ }
+ continue;
+ }
+ if (nm_streq (property_name, "UUIDs")) {
+ if (bzobj->d_device_capabilities != NM_BT_CAPABILITY_NONE) {
+ changed = TRUE;
+ bzobj->d_device_capabilities = NM_BT_CAPABILITY_NONE;
+ }
+ continue;
+ }
+ if (nm_streq (property_name, "Connected")) {
+ if (bzobj->d_device_connected) {
+ changed = TRUE;
+ bzobj->d_device_connected = FALSE;
+ }
+ continue;
+ }
+ if (nm_streq (property_name, "Paired")) {
+ if (bzobj->d_device_paired) {
+ changed = TRUE;
+ bzobj->d_device_paired = FALSE;
+ }
+ continue;
+ }
+ }
+
+ } else if (nm_streq (interface_name, NM_BLUEZ5_NETWORK_INTERFACE)) {
+ _bzobjs_init (self, &bzobj, object_path);
+ if (!bzobj->d_has_network_iface) {
+ changed = TRUE;
+ bzobj->d_has_network_iface = TRUE;
+ }
+
+ while ( changed_properties
+ && g_variant_iter_next (&iter_prop, "{&sv}", &property_name, &property_value)) {
+ _nm_unused gs_unref_variant GVariant *property_value_free = property_value;
+
+ if (nm_streq (property_name, "Interface")) {
+ const char *s = g_variant_is_of_type (property_value, G_VARIANT_TYPE_STRING)
+ ? g_variant_get_string (property_value, NULL)
+ : NULL;
+
+ if (!nm_streq0 (bzobj->d_network.interface, s)) {
+ changed = TRUE;
+ nm_clear_g_free (&bzobj->d_network.interface);
+ bzobj->d_network.interface = g_strdup (s);
+ }
+ continue;
+ }
+ if (nm_streq (property_name, "Connected")) {
+ bool v = g_variant_is_of_type (property_value, G_VARIANT_TYPE_BOOLEAN)
+ && g_variant_get_boolean (property_value);
+
+ if (bzobj->d_network_connected != v) {
+ changed = TRUE;
+ bzobj->d_network_connected = v;
+ }
+ continue;
+ }
+ }
+
+ for (i = 0; (property_name = invalidated_properties[i]); i++) {
+ if (nm_streq (property_name, "Interface")) {
+ if (bzobj->d_network.interface) {
+ changed = TRUE;
+ nm_clear_g_free (&bzobj->d_network.interface);
+ }
+ continue;
+ }
+ if (nm_streq (property_name, "Connected")) {
+ if (bzobj->d_network_connected) {
+ changed = TRUE;
+ bzobj->d_network_connected = FALSE;
+ }
+ continue;
+ }
+ }
+
+ } else if (nm_streq (interface_name, NM_BLUEZ5_NETWORK_SERVER_INTERFACE)) {
+ _bzobjs_init (self, &bzobj, object_path);
+ if (!bzobj->d_has_network_server_iface) {
+ changed = TRUE;
+ bzobj->d_has_network_server_iface = TRUE;
+ }
+ }
+
+ nm_assert (!changed || bzobj);
+
+ if (inout_bzobj)
+ *inout_bzobj = bzobj;
+
+ return changed;
+}
+
+static void
+_dbus_handle_interface_added (NMBluezManager *self,
+ const char *object_path,
+ GVariant *ifaces,
+ gboolean initial_get_managed_objects)
+{
+ BzDBusObj *bzobj = NULL;
+ gboolean changed = FALSE;
+ const char *interface_name;
+ GVariant *changed_properties;
+ GVariantIter iter_ifaces;
+
+ nm_assert (g_variant_is_of_type (ifaces, G_VARIANT_TYPE ("a{sa{sv}}")));
+
+ g_variant_iter_init (&iter_ifaces, ifaces);
+ while (g_variant_iter_next (&iter_ifaces, "{&s@a{sv}}", &interface_name, &changed_properties)) {
+ _nm_unused gs_unref_variant GVariant *changed_properties_free = changed_properties;
+
+ if (_dbus_handle_properties_changed (self, object_path, interface_name, changed_properties, NULL, &bzobj))
+ changed = TRUE;
+ }
+
+ if (changed) {
+ _dbus_process_changes (self,
+ bzobj,
+ initial_get_managed_objects
+ ? "dbus-init"
+ : "dbus-iface-added");
+ }
+}
+
+static gboolean
+_dbus_handle_interface_removed (NMBluezManager *self,
+ const char *object_path,
+ BzDBusObj **inout_bzobj,
+ const char *const*removed_interfaces)
+{
+ gboolean changed = FALSE;
+ BzDBusObj *bzobj;
+ gsize i;
+
+ if ( inout_bzobj
+ && *inout_bzobj) {
+ bzobj = *inout_bzobj;
+ nm_assert (bzobj == _bzobjs_get (self, object_path));
+ } else {
+ bzobj = _bzobjs_get (self, object_path);
+ if (!bzobj)
+ return FALSE;
+ NM_SET_OUT (inout_bzobj, bzobj);
+ }
+
+ for (i = 0; removed_interfaces[i]; i++) {
+ const char *interface_name = removed_interfaces[i];
+
+ if (nm_streq (interface_name, NM_BLUEZ5_ADAPTER_INTERFACE)) {
+ if (bzobj->d_has_adapter_iface) {
+ changed = TRUE;
+ bzobj->d_has_adapter_iface = FALSE;
+ }
+ if (bzobj->d_adapter.address) {
+ changed = TRUE;
+ nm_clear_g_free (&bzobj->d_adapter.address);
+ }
+ if (bzobj->d_adapter_powered) {
+ changed = TRUE;
+ bzobj->d_adapter_powered = FALSE;
+ }
+ } else if (nm_streq (interface_name, NM_BLUEZ5_DEVICE_INTERFACE)) {
+ if (bzobj->d_has_device_iface) {
+ changed = TRUE;
+ bzobj->d_has_device_iface = FALSE;
+ }
+ if (bzobj->d_device.address) {
+ changed = TRUE;
+ nm_clear_g_free (&bzobj->d_device.address);
+ }
+ if (bzobj->d_device.name) {
+ changed = TRUE;
+ nm_clear_g_free (&bzobj->d_device.name);
+ }
+ if (bzobj->d_device.adapter) {
+ changed = TRUE;
+ nm_clear_g_free (&bzobj->d_device.adapter);
+ }
+ if (bzobj->d_device_capabilities != NM_BT_CAPABILITY_NONE) {
+ changed = TRUE;
+ bzobj->d_device_capabilities = NM_BT_CAPABILITY_NONE;
+ }
+ if (bzobj->d_device_connected) {
+ changed = TRUE;
+ bzobj->d_device_connected = FALSE;
+ }
+ if (bzobj->d_device_paired) {
+ changed = TRUE;
+ bzobj->d_device_paired = FALSE;
+ }
+ } else if (nm_streq (interface_name, NM_BLUEZ5_NETWORK_INTERFACE)) {
+ if (bzobj->d_has_network_iface) {
+ changed = TRUE;
+ bzobj->d_has_network_iface = FALSE;
+ }
+ if (bzobj->d_network.interface) {
+ changed = TRUE;
+ nm_clear_g_free (&bzobj->d_network.interface);
+ }
+ if (bzobj->d_network_connected) {
+ changed = TRUE;
+ bzobj->d_network_connected = FALSE;
+ }
+ } else if (nm_streq (interface_name, NM_BLUEZ5_NETWORK_SERVER_INTERFACE)) {
+ if (bzobj->d_has_network_server_iface) {
+ changed = TRUE;
+ bzobj->d_has_network_server_iface = FALSE;
+ }
+ }
+ }
+
+ return changed;
+}
+
+static void
+_dbus_managed_objects_changed_cb (const char *object_path,
+ GVariant *added_interfaces_and_properties,
+ const char *const*removed_interfaces,
+ gpointer user_data)
+{
+ NMBluezManager *self = user_data;
+ NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+ BzDBusObj *bzobj = NULL;
+ gboolean changed;
+
+ if (priv->get_managed_objects_cancellable) {
+ /* we still wait for the initial GetManagedObjects(). Ignore the event. */
return;
}
- _LOGD ("detecting BlueZ version failed: %s", reason);
+ if (!added_interfaces_and_properties) {
+ changed = _dbus_handle_interface_removed (self, object_path, &bzobj, removed_interfaces);
+ if (changed)
+ _dbus_process_changes (self, bzobj, "dbus-iface-removed");
+ } else
+ _dbus_handle_interface_added (self, object_path, added_interfaces_and_properties, FALSE);
+}
- /* cancel current attempts to detect the version. */
- cleanup_checking (self, FALSE);
- if (!priv->watch_name_id) {
- priv->watch_name_id = g_bus_watch_name (G_BUS_TYPE_SYSTEM,
- NM_BLUEZ_SERVICE,
- G_BUS_NAME_WATCHER_FLAGS_NONE,
- watch_name_on_appeared,
- NULL,
- self,
- NULL);
+static void
+_dbus_properties_changed_cb (GDBusConnection *connection,
+ const char *sender_name,
+ const char *object_path,
+ const char *signal_interface_name,
+ const char *signal_name,
+ GVariant *parameters,
+ gpointer user_data)
+{
+ NMBluezManager *self = user_data;
+ NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+ const char *interface_name;
+ gs_unref_variant GVariant *changed_properties = NULL;
+ gs_free const char **invalidated_properties = NULL;
+ BzDBusObj *bzobj = NULL;
+
+ if (priv->get_managed_objects_cancellable) {
+ /* we still wait for the initial GetManagedObjects(). Ignore the event. */
+ return;
}
+
+ if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sa{sv}as)")))
+ return;
+
+ g_variant_get (parameters,
+ "(&s@a{sv}^a&s)",
+ &interface_name,
+ &changed_properties,
+ &invalidated_properties);
+
+ if (_dbus_handle_properties_changed (self, object_path, interface_name, changed_properties, invalidated_properties, &bzobj))
+ _dbus_process_changes (self, bzobj, "dbus-property-changed");
}
static void
-check_bluez_and_try_setup_do_introspect (GObject *source_object,
- GAsyncResult *res,
- gpointer user_data)
+_dbus_get_managed_objects_cb (GVariant *result,
+ GError *error,
+ gpointer user_data)
{
- NMBluezManager *self = async_data_unpack (user_data);
+ NMBluezManager *self;
NMBluezManagerPrivate *priv;
- GError *error = NULL;
- gs_unref_variant GVariant *result = NULL;
- const char *reason = NULL;
+ GVariantIter iter;
+ const char *object_path;
+ GVariant *ifaces;
- if (!self)
+ if ( !result
+ && nm_utils_error_is_cancelled (error, FALSE))
return;
+ self = user_data;
priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
- g_return_if_fail (priv->introspect_proxy);
- g_return_if_fail (!g_cancellable_is_cancelled (priv->async_cancellable));
-
- g_clear_object (&priv->async_cancellable);
+ g_clear_object (&priv->get_managed_objects_cancellable);
- result = _nm_dbus_proxy_call_finish (priv->introspect_proxy, res,
- G_VARIANT_TYPE ("(s)"), &error);
if (!result) {
- char *reason2;
-
- g_dbus_error_strip_remote_error (error);
- reason2 = g_strdup_printf ("introspect failed with %s", error->message);
- check_bluez_and_try_setup_final_step (self, FALSE, reason2);
- g_error_free (error);
- g_free (reason2);
+ _LOGT ("initial GetManagedObjects() call failed: %s", error->message);
+ _cleanup_for_name_owner (self);
return;
}
- check_bluez_and_try_setup_final_step (self, TRUE, reason);
+ _LOGT ("initial GetManagedObjects call succeeded");
+
+ g_variant_iter_init (&iter, result);
+ while (g_variant_iter_next (&iter, "{&o@a{sa{sv}}}", &object_path, &ifaces)) {
+ _nm_unused gs_unref_variant GVariant *ifaces_free = ifaces;
+
+ _dbus_handle_interface_added (self, object_path, ifaces, TRUE);
+ }
}
+/*****************************************************************************/
+
static void
-check_bluez_and_try_setup_on_new_proxy (GObject *source_object,
- GAsyncResult *res,
- gpointer user_data)
+_cleanup_for_name_owner (NMBluezManager *self)
{
- NMBluezManager *self = async_data_unpack (user_data);
- NMBluezManagerPrivate *priv;
- GError *error = NULL;
+ NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+ gboolean emit_device_availability_changed = FALSE;
+ GHashTableIter iter;
+ BzDBusObj *bzobj;
+ gboolean first = TRUE;
+
+ nm_clear_g_cancellable (&priv->get_managed_objects_cancellable);
+
+ nm_clear_g_dbus_connection_signal (priv->dbus_connection,
+ &priv->managed_objects_changed_id);
+ nm_clear_g_dbus_connection_signal (priv->dbus_connection,
+ &priv->properties_changed_id);
+
+ nm_clear_g_free (&priv->name_owner);
+
+ g_hash_table_iter_init (&iter, priv->bzobjs);
+ while (g_hash_table_iter_next (&iter, (gpointer *) &bzobj, NULL)) {
+ if (first) {
+ first = FALSE;
+ _LOGT ("drop all objects form D-Bus cache...");
+ }
+ _dbus_handle_interface_removed (self,
+ bzobj->object_path,
+ &bzobj,
+ ALL_RELEVANT_INTERFACE_NAMES);
+ nm_c_list_move_tail (&priv->process_change_lst_head, &bzobj->process_change_lst);
+ }
+ _process_change_idle_all (self, &emit_device_availability_changed);
+ nm_assert (g_hash_table_size (priv->bzobjs) == 0);
+
+ if (emit_device_availability_changed)
+ nm_manager_notify_device_availibility_maybe_changed (priv->manager);
+}
+
+static void
+name_owner_changed (NMBluezManager *self,
+ const char *owner)
+{
+ _nm_unused gs_unref_object NMBluezManager *self_keep_alive = g_object_ref (self);
+ NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+
+ owner = nm_str_not_empty (owner);
+
+ if (!owner)
+ _LOGT ("D-Bus name for bluez has no owner");
+ else
+ _LOGT ("D-Bus name for bluez has owner %s", owner);
+
+ nm_clear_g_cancellable (&priv->name_owner_get_cancellable);
- if (!self)
+ if (nm_streq0 (priv->name_owner, owner))
return;
- priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+ _cleanup_for_name_owner (self);
- g_return_if_fail (!priv->introspect_proxy);
- g_return_if_fail (!g_cancellable_is_cancelled (priv->async_cancellable));
+ if (!owner)
+ return;
- priv->introspect_proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
+ priv->name_owner = g_strdup (owner);
+
+ priv->get_managed_objects_cancellable = g_cancellable_new ();
+
+ priv->managed_objects_changed_id = nm_dbus_connection_signal_subscribe_object_manager (priv->dbus_connection,
+ priv->name_owner,
+ NM_BLUEZ_MANAGER_PATH,
+ _dbus_managed_objects_changed_cb,
+ self,
+ NULL);
+
+ priv->properties_changed_id = nm_dbus_connection_signal_subscribe_properties_changed (priv->dbus_connection,
+ priv->name_owner,
+ NULL,
+ NULL,
+ _dbus_properties_changed_cb,
+ self,
+ NULL);
+
+ nm_dbus_connection_call_get_managed_objects (priv->dbus_connection,
+ priv->name_owner,
+ NM_BLUEZ_MANAGER_PATH,
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ 20000,
+ priv->get_managed_objects_cancellable,
+ _dbus_get_managed_objects_cb,
+ self);
+}
- if (!priv->introspect_proxy) {
- char *reason = g_strdup_printf ("bluez error creating dbus proxy: %s", error->message);
+static void
+name_owner_changed_cb (GDBusConnection *connection,
+ const char *sender_name,
+ const char *object_path,
+ const char *interface_name,
+ const char *signal_name,
+ GVariant *parameters,
+ gpointer user_data)
+{
+ NMBluezManager *self = user_data;
+ const char *new_owner;
- check_bluez_and_try_setup_final_step (self, FALSE, reason);
- g_error_free (error);
- g_free (reason);
+ if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sss)")))
return;
- }
- g_dbus_proxy_call (priv->introspect_proxy,
- "Introspect",
- NULL,
- G_DBUS_CALL_FLAGS_NO_AUTO_START,
- 3000,
- priv->async_cancellable,
- check_bluez_and_try_setup_do_introspect,
- async_data_pack (self));
+ g_variant_get (parameters,
+ "(&s&s&s)",
+ NULL,
+ NULL,
+ &new_owner);
+
+ name_owner_changed (self, new_owner);
}
static void
-check_bluez_and_try_setup (NMBluezManager *self)
+name_owner_get_cb (const char *name_owner,
+ GError *error,
+ gpointer user_data)
+{
+ if ( name_owner
+ || !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ name_owner_changed (user_data, name_owner);
+}
+
+/*****************************************************************************/
+
+static void
+_cleanup_all (NMBluezManager *self)
{
NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
- /* there should be no ongoing detection. Anyway, cleanup_checking. */
- cleanup_checking (self, FALSE);
+ priv->settings_registered = FALSE;
+
+ g_signal_handlers_disconnect_by_func (priv->settings, cp_connection_added, self);
+ g_signal_handlers_disconnect_by_func (priv->settings, cp_connection_updated, self);
+ g_signal_handlers_disconnect_by_func (priv->settings, cp_connection_removed, self);
+
+ g_hash_table_remove_all (priv->conn_data_elems);
+ g_hash_table_remove_all (priv->conn_data_heads);
- priv->async_cancellable = g_cancellable_new ();
+ _cleanup_for_name_owner (self);
- g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
- G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
- NULL,
- NM_BLUEZ_SERVICE,
- "/",
- DBUS_INTERFACE_INTROSPECTABLE,
- priv->async_cancellable,
- check_bluez_and_try_setup_on_new_proxy,
- async_data_pack (self));
+ nm_clear_g_cancellable (&priv->name_owner_get_cancellable);
+
+ nm_clear_g_dbus_connection_signal (priv->dbus_connection,
+ &priv->name_owner_changed_id);
}
static void
start (NMDeviceFactory *factory)
{
- check_bluez_and_try_setup (NM_BLUEZ_MANAGER (factory));
+ NMBluezManager *self;
+ NMBluezManagerPrivate *priv;
+ NMSettingsConnection *const*sett_conns;
+ guint n_sett_conns;
+ guint i;
+
+ g_return_if_fail (NM_IS_BLUEZ_MANAGER (factory));
+
+ self = NM_BLUEZ_MANAGER (factory);
+ priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+
+ _cleanup_all (self);
+
+ if (!priv->dbus_connection) {
+ _LOGI ("no D-Bus connection available");
+ return;
+ }
+
+ g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_ADDED, G_CALLBACK (cp_connection_added), self);
+ g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_UPDATED, G_CALLBACK (cp_connection_updated), self);
+ g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_REMOVED, G_CALLBACK (cp_connection_removed), self);
+
+ priv->settings_registered = TRUE;
+
+ sett_conns = nm_settings_get_connections (priv->settings, &n_sett_conns);
+ for (i = 0; i < n_sett_conns; i++)
+ _conn_track_update (self, sett_conns[i], TRUE, NULL, NULL, NULL);
+
+ priv->name_owner_changed_id = nm_dbus_connection_signal_subscribe_name_owner_changed (priv->dbus_connection,
+ NM_BLUEZ_SERVICE,
+ name_owner_changed_cb,
+ self,
+ NULL);
+
+ priv->name_owner_get_cancellable = g_cancellable_new ();
+
+ nm_dbus_connection_call_get_name_owner (priv->dbus_connection,
+ NM_BLUEZ_SERVICE,
+ 10000,
+ priv->name_owner_get_cancellable,
+ name_owner_get_cb,
+ self);
+}
+
+/*****************************************************************************/
+
+static void
+_connect_returned (NMBluezManager *self,
+ BzDBusObj *bzobj,
+ NMBluetoothCapabilities bt_type,
+ const char *device_name,
+ NMBluez5DunContext *dun_context,
+ GError *error)
+{
+ char sbuf_cap[100];
+
+ if (error) {
+ nm_assert (!device_name);
+ nm_assert (!dun_context);
+
+ _LOGI ("%s [%s]: connect failed: %s",
+ nm_bluetooth_capability_to_string (bzobj->x_device_connect_bt_type, sbuf_cap, sizeof (sbuf_cap)),
+ bzobj->object_path,
+ error->message);
+
+ _device_connect_req_data_complete (g_steal_pointer (&bzobj->x_device.c_req_data),
+ self,
+ NULL,
+ error);
+ _connect_disconnect (self, bzobj, "cleanup after connect failure");
+ return;
+ }
+
+ nm_assert (bzobj->x_device_connect_bt_type == bt_type);
+ nm_assert (device_name);
+ nm_assert ((bt_type == NM_BT_CAPABILITY_DUN) == (!!dun_context));
+ nm_assert (bzobj->x_device.c_req_data);
+
+ g_clear_object (&bzobj->x_device.c_req_data->int_cancellable);
+
+ bzobj->x_device.connect_dun_context = dun_context;
+
+ _LOGD ("%s [%s]: connect successful to device %s",
+ nm_bluetooth_capability_to_string (bzobj->x_device_connect_bt_type, sbuf_cap, sizeof (sbuf_cap)),
+ bzobj->object_path,
+ device_name);
+
+ /* we already have another over-all timer running. But after we connected the device,
+ * we still need to wait for bluez to acknowledge the connected state (via D-Bus, for NAP).
+ * For DUN profiles we likely are already fully connected by now.
+ *
+ * Anyway, schedule another timeout that is possibly shorter than the overall, original
+ * timeout. Now this should go down fast. */
+ bzobj->x_device.c_req_data->timeout_wait_connect_id = g_timeout_add (5000,
+ _connect_timeout_wait_connected_cb,
+ bzobj),
+ bzobj->x_device.c_req_data->device_name = g_strdup (device_name);
+
+ if ( _bzobjs_device_is_usable (bzobj, NULL, NULL)
+ && _bzobjs_device_is_connected (bzobj)) {
+ /* We are now connected. Schedule the task that completes the state. */
+ _process_change_idle_schedule (self, bzobj);
+ }
+}
+
+#if WITH_BLUEZ5_DUN
+static void
+_connect_dun_notify_tty_hangup_cb (NMBluez5DunContext *context,
+ gpointer user_data)
+{
+ BzDBusObj *bzobj = user_data;
+
+ _connect_disconnect (bzobj->self,
+ bzobj,
+ "DUN connection hung up");
+}
+
+static void
+_connect_dun_step2_cb (NMBluez5DunContext *context,
+ const char *rfcomm_dev,
+ GError *error,
+ gpointer user_data)
+{
+ BzDBusObj *bzobj;
+
+ if (nm_utils_error_is_cancelled (error, FALSE))
+ return;
+
+ bzobj = user_data;
+
+ if (rfcomm_dev) {
+ /* We want to early notifiy about the rfcomm path. That is because we might still delay
+ * to signal full activation longer (asynchronously). But the earliest time the callback
+ * is invoked with the rfcomm path, we just created the device synchronously.
+ *
+ * By already notifying the caller about the path early, it avoids a race where ModemManager
+ * would find the modem before the bluetooth code considers the profile fully activated. */
+
+ nm_assert (!error);
+ nm_assert (bzobj->x_device.c_req_data);
+
+ if (!g_cancellable_is_cancelled (bzobj->x_device.c_req_data->ext_cancellable))
+ bzobj->x_device.c_req_data->callback (bzobj->self, FALSE, rfcomm_dev, NULL, bzobj->x_device.c_req_data->callback_user_data);
+
+ if (!context) {
+ /* No context set. This means, we just got notified about the rfcomm path and need to wait
+ * longer, for the next callback. */
+ return;
+ }
+ }
+
+ _connect_returned (bzobj->self, bzobj, NM_BT_CAPABILITY_DUN, rfcomm_dev, context, error);
+}
+
+static void
+_connect_dun_step1_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ gs_unref_variant GVariant *ret = NULL;
+ gs_free_error GError *error = NULL;
+ BzDBusObj *bzobj_adapter;
+ BzDBusObj *bzobj;
+
+ ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), res, &error);
+
+ if ( !ret
+ && nm_utils_error_is_cancelled (error, FALSE))
+ return;
+
+ bzobj = user_data;
+
+ if (error) {
+ _LOGT ("DUN: [%s]: bluetooth device connect failed: %s", bzobj->object_path, error->message);
+ /* we actually ignore this error. Let's try, maybe we still can connect via DUN. */
+ g_clear_error (&error);
+ } else
+ _LOGT ("DUN: [%s]: bluetooth device connected successfully", bzobj->object_path);
+
+ if (!_bzobjs_device_is_usable (bzobj, &bzobj_adapter, NULL)) {
+ nm_utils_error_set (&error, NM_UTILS_ERROR_UNKNOWN,
+ "device %s is not usable for DUN after connect",
+ bzobj->object_path);
+ _connect_returned (bzobj->self, bzobj, NM_BT_CAPABILITY_DUN, NULL, NULL, error);
+ return;
+ }
+
+ if (!nm_bluez5_dun_connect (bzobj_adapter->d_adapter.address,
+ bzobj->d_device.address,
+ bzobj->x_device.c_req_data->int_cancellable,
+ _connect_dun_step2_cb,
+ bzobj,
+ _connect_dun_notify_tty_hangup_cb,
+ bzobj,
+ &error)) {
+ _connect_returned (bzobj->self, bzobj, NM_BT_CAPABILITY_DUN, NULL, NULL, error);
+ return;
+ }
+}
+#endif
+
+static void
+_connect_nap_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ gs_unref_variant GVariant *ret = NULL;
+ const char *network_iface_name = NULL;
+ gs_free_error GError *error = NULL;
+ BzDBusObj *bzobj;
+
+ ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), res, &error);
+
+ if ( !ret
+ && nm_utils_error_is_cancelled (error, FALSE))
+ return;
+
+ if (ret)
+ g_variant_get (ret, "(&s)", &network_iface_name);
+
+ bzobj = user_data;
+
+ _connect_returned (bzobj->self, bzobj, NM_BT_CAPABILITY_NAP, network_iface_name, NULL, error);
+}
+
+static void
+_connect_cancelled_cb (GCancellable *cancellable,
+ BzDBusObj *bzobj)
+{
+ _connect_disconnect (bzobj->self, bzobj, "connect cancelled");
+}
+
+static gboolean
+_connect_timeout_wait_connected_cb (gpointer user_data)
+{
+ BzDBusObj *bzobj = user_data;
+
+ bzobj->x_device.c_req_data->timeout_wait_connect_id = 0;
+ _connect_disconnect (bzobj->self, bzobj, "timeout waiting for connected");
+ return G_SOURCE_REMOVE;
+}
+
+static gboolean
+_connect_timeout_cb (gpointer user_data)
+{
+ BzDBusObj *bzobj = user_data;
+
+ bzobj->x_device.c_req_data->timeout_id = 0;
+ _connect_disconnect (bzobj->self, bzobj, "timeout connecting");
+ return G_SOURCE_REMOVE;
}
+static void
+_connect_disconnect (NMBluezManager *self,
+ BzDBusObj *bzobj,
+ const char *reason)
+{
+ NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+ DeviceConnectReqData *c_req_data;
+ char sbuf_cap[100];
+ gboolean bt_type;
+
+ if (bzobj->x_device_connect_bt_type == NM_BT_CAPABILITY_NONE) {
+ nm_assert (!bzobj->x_device.c_req_data);
+ return;
+ }
+
+ bt_type = bzobj->x_device_connect_bt_type;
+ nm_assert (NM_IN_SET (bt_type, NM_BT_CAPABILITY_DUN, NM_BT_CAPABILITY_NAP));
+ bzobj->x_device_connect_bt_type = NM_BT_CAPABILITY_NONE;
+
+ c_req_data = g_steal_pointer (&bzobj->x_device.c_req_data);
+
+ _LOGD ("%s [%s]: disconnect due to %s",
+ nm_bluetooth_capability_to_string (bt_type, sbuf_cap, sizeof (sbuf_cap)),
+ bzobj->object_path,
+ reason);
+
+ if (c_req_data)
+ nm_clear_g_cancellable (&c_req_data->int_cancellable);
+
+ if (bt_type == NM_BT_CAPABILITY_DUN) {
+ /* For DUN devices, we also called org.bluez.Device1.Connect() (because in order
+ * for nm_bluez5_dun_connect() to succeed, we need to be already connected *why??).
+ *
+ * But upon disconnect we don't call Disconnect() because we don't know whether somebody
+ * else also uses the bluetooth device for other purposes. During disconnect we only
+ * terminate the DUN connection, but don't disconnect entirely. I think that's the
+ * best we can do. */
+#if WITH_BLUEZ5_DUN
+ nm_clear_pointer (&bzobj->x_device.connect_dun_context, nm_bluez5_dun_disconnect);
+#else
+ nm_assert_not_reached ();
+#endif
+ } else {
+ if (priv->name_owner) {
+ gs_unref_object GCancellable *cancellable = NULL;
+
+ cancellable = g_cancellable_new ();
+
+ nm_shutdown_wait_obj_register_cancellable_full (cancellable,
+ g_strdup_printf ("bt-disconnect-nap[%s]", bzobj->object_path),
+ TRUE);
+
+ g_dbus_connection_call (priv->dbus_connection,
+ priv->name_owner,
+ bzobj->object_path,
+ NM_BLUEZ5_NETWORK_INTERFACE,
+ "Disconnect",
+ g_variant_new("()"),
+ NULL,
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ -1,
+ cancellable,
+ _dbus_call_complete_cb_nop,
+ NULL);
+ }
+ }
+
+ if (c_req_data) {
+ gs_free_error GError *error = NULL;
+
+ nm_utils_error_set (&error,
+ NM_UTILS_ERROR_UNKNOWN,
+ "connect aborted due to %s",
+ reason);
+ _device_connect_req_data_complete (c_req_data, self, NULL, error);
+ }
+}
+
+gboolean
+nm_bluez_manager_connect (NMBluezManager *self,
+ const char *object_path,
+ NMBluetoothCapabilities connection_bt_type,
+ int timeout_msec,
+ GCancellable *cancellable,
+ NMBluezManagerConnectCb callback,
+ gpointer callback_user_data,
+ GError **error)
+{
+ gs_unref_object GCancellable *int_cancellable = NULL;
+ DeviceConnectReqData *c_req_data;
+ NMBluezManagerPrivate *priv;
+ BzDBusObj *bzobj;
+ char sbuf_cap[100];
+
+ g_return_val_if_fail (NM_IS_BLUEZ_MANAGER (self), FALSE);
+ g_return_val_if_fail (NM_IN_SET (connection_bt_type, NM_BT_CAPABILITY_DUN,
+ NM_BT_CAPABILITY_NAP), FALSE);
+ g_return_val_if_fail (callback, FALSE);
+
+ nm_assert (timeout_msec > 0);
+
+ priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+
+ bzobj = _bzobjs_get (self, object_path);
+
+ if (!bzobj) {
+ nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
+ "device %s does not exist",
+ object_path);
+ return FALSE;
+ }
+
+ if (!_bzobjs_device_is_usable (bzobj, NULL, NULL)) {
+ nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
+ "device %s is not usable",
+ object_path);
+ return FALSE;
+ }
+
+ if (!NM_FLAGS_ALL (bzobj->d_device_capabilities, connection_bt_type)) {
+ nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
+ "device %s has not the required capabilities",
+ object_path);
+ return FALSE;
+ }
+
+#if !WITH_BLUEZ5_DUN
+ if (connection_bt_type == NM_BT_CAPABILITY_DUN) {
+ nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN,
+ "DUN is not supported");
+ return FALSE;
+ }
+#endif
+
+ _connect_disconnect (self, bzobj, "new activation");
+
+ _LOGD ("%s [%s]: connecting...",
+ nm_bluetooth_capability_to_string (connection_bt_type, sbuf_cap, sizeof (sbuf_cap)),
+ bzobj->object_path);
+
+ int_cancellable = g_cancellable_new();
+
+#if WITH_BLUEZ5_DUN
+ if (connection_bt_type == NM_BT_CAPABILITY_DUN) {
+ g_dbus_connection_call (priv->dbus_connection,
+ priv->name_owner,
+ bzobj->object_path,
+ NM_BLUEZ5_DEVICE_INTERFACE,
+ "Connect",
+ NULL,
+ NULL,
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ timeout_msec,
+ int_cancellable,
+ _connect_dun_step1_cb,
+ bzobj);
+ } else
+#endif
+ {
+ nm_assert (connection_bt_type == NM_BT_CAPABILITY_NAP);
+ g_dbus_connection_call (priv->dbus_connection,
+ priv->name_owner,
+ bzobj->object_path,
+ NM_BLUEZ5_NETWORK_INTERFACE,
+ "Connect",
+ g_variant_new ("(s)", BLUETOOTH_CONNECT_NAP),
+ G_VARIANT_TYPE ("(s)"),
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ timeout_msec,
+ int_cancellable,
+ _connect_nap_cb,
+ bzobj);
+ }
+
+ c_req_data = g_slice_new (DeviceConnectReqData);
+ *c_req_data = (DeviceConnectReqData) {
+ .int_cancellable = g_steal_pointer (&int_cancellable),
+ .ext_cancellable = g_object_ref (cancellable),
+ .callback = callback,
+ .callback_user_data = callback_user_data,
+ .ext_cancelled_id = g_signal_connect (cancellable,
+ "cancelled",
+ G_CALLBACK (_connect_cancelled_cb),
+ bzobj),
+ .timeout_id = g_timeout_add (timeout_msec,
+ _connect_timeout_cb,
+ bzobj),
+ };
+
+ bzobj->x_device_connect_bt_type = connection_bt_type;
+ bzobj->x_device.c_req_data = c_req_data;
+
+ return TRUE;
+}
+
+void
+nm_bluez_manager_disconnect (NMBluezManager *self,
+ const char *object_path)
+{
+ BzDBusObj *bzobj;
+
+ g_return_if_fail (NM_IS_BLUEZ_MANAGER (self));
+ g_return_if_fail (object_path);
+
+ bzobj = _bzobjs_get (self, object_path);
+ if (!bzobj)
+ return;
+
+ _connect_disconnect (self, bzobj, "disconnected by user");
+}
+
+/*****************************************************************************/
+
static NMDevice *
create_device (NMDeviceFactory *factory,
const char *iface,
@@ -334,8 +2790,8 @@ create_device (NMDeviceFactory *factory,
NMConnection *connection,
gboolean *out_ignore)
{
- g_warn_if_fail (plink->type == NM_LINK_TYPE_BNEP);
*out_ignore = TRUE;
+ g_return_val_if_fail (plink->type == NM_LINK_TYPE_BNEP, NULL);
return NULL;
}
@@ -360,7 +2816,25 @@ nm_bluez_manager_init (NMBluezManager *self)
{
NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+ priv->vtable_network_server = (NMBtVTableNetworkServer) {
+ .is_available = _network_server_vt_is_available,
+ .register_bridge = _network_server_vt_register_bridge,
+ .unregister_bridge = _network_server_vt_unregister_bridge,
+ };
+
+ c_list_init (&priv->network_server_lst_head);
+ c_list_init (&priv->process_change_lst_head);
+
+ priv->conn_data_heads = g_hash_table_new_full (_conn_data_head_hash, _conn_data_head_equal, g_free, NULL);
+ priv->conn_data_elems = g_hash_table_new_full (nm_pdirect_hash, nm_pdirect_equal, nm_g_slice_free_fcn (ConnDataElem), NULL);
+
+ priv->bzobjs = g_hash_table_new_full (nm_pstr_hash, nm_pstr_equal, (GDestroyNotify) _bz_dbus_obj_free, NULL);
+
+ priv->manager = g_object_ref (NM_MANAGER_GET);
priv->settings = g_object_ref (NM_SETTINGS_GET);
+ priv->dbus_connection = nm_g_object_ref (NM_MAIN_DBUS_CONNECTION_GET);
+
+ g_atomic_pointer_compare_and_exchange (&nm_bt_vtable_network_server, NULL, &priv->vtable_network_server);
}
static void
@@ -369,16 +2843,25 @@ dispose (GObject *object)
NMBluezManager *self = NM_BLUEZ_MANAGER (object);
NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
- if (priv->manager5) {
- g_signal_handlers_disconnect_by_data (priv->manager5, self);
- g_clear_object (&priv->manager5);
- }
+ /* FIXME(shutdown): we need a nm_device_factory_stop() hook to first unregister all
+ * BzDBusObj instances and do necessary cleanup actions (like disconnecting devices
+ * or deleting panu_connection). */
+
+ nm_assert (c_list_is_empty (&priv->network_server_lst_head));
+ nm_assert (c_list_is_empty (&priv->process_change_lst_head));
+ nm_assert (priv->process_change_idle_id == 0);
- cleanup_checking (self, TRUE);
+ g_atomic_pointer_compare_and_exchange (&nm_bt_vtable_network_server, &priv->vtable_network_server, NULL);
+
+ _cleanup_all (self);
G_OBJECT_CLASS (nm_bluez_manager_parent_class)->dispose (object);
g_clear_object (&priv->settings);
+ g_clear_object (&priv->manager);
+ g_clear_object (&priv->dbus_connection);
+
+ nm_clear_pointer (&priv->bzobjs, g_hash_table_destroy);
}
static void
@@ -387,10 +2870,10 @@ nm_bluez_manager_class_init (NMBluezManagerClass *klass)
GObjectClass *object_class = G_OBJECT_CLASS (klass);
NMDeviceFactoryClass *factory_class = NM_DEVICE_FACTORY_CLASS (klass);
- object_class->dispose = dispose;
+ object_class->dispose = dispose;
factory_class->get_supported_types = get_supported_types;
- factory_class->create_device = create_device;
- factory_class->match_connection = match_connection;
- factory_class->start = start;
+ factory_class->create_device = create_device;
+ factory_class->match_connection = match_connection;
+ factory_class->start = start;
}
diff --git a/src/devices/bluetooth/nm-bluez-manager.h b/src/devices/bluetooth/nm-bluez-manager.h
new file mode 100644
index 0000000000..85dbaa838a
--- /dev/null
+++ b/src/devices/bluetooth/nm-bluez-manager.h
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2009 - 2019 Red Hat, Inc.
+ */
+
+#ifndef __NM_BLUEZ_MANAGER_H__
+#define __NM_BLUEZ_MANAGER_H__
+
+#define NM_TYPE_BLUEZ_MANAGER (nm_bluez_manager_get_type ())
+#define NM_BLUEZ_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_BLUEZ_MANAGER, NMBluezManager))
+#define NM_BLUEZ_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_BLUEZ_MANAGER, NMBluezManagerClass))
+#define NM_IS_BLUEZ_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_BLUEZ_MANAGER))
+#define NM_IS_BLUEZ_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_BLUEZ_MANAGER))
+#define NM_BLUEZ_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_BLUEZ_MANAGER, NMBluezManagerClass))
+
+typedef struct _NMBluezManager NMBluezManager;
+typedef struct _NMBluezManagerClass NMBluezManagerClass;
+
+GType nm_bluez_manager_get_type (void);
+
+typedef void (*NMBluezManagerConnectCb) (NMBluezManager *self,
+ gboolean is_completed /* or else is early notification with DUN path */,
+ const char *device_name,
+ GError *error,
+ gpointer user_data);
+
+gboolean nm_bluez_manager_connect (NMBluezManager *self,
+ const char *object_path,
+ NMBluetoothCapabilities connection_bt_type,
+ int timeout_msec,
+ GCancellable *cancellable,
+ NMBluezManagerConnectCb callback,
+ gpointer callback_user_data,
+ GError **error);
+
+void nm_bluez_manager_disconnect (NMBluezManager *self,
+ const char *object_path);
+
+#endif /* __NM_BLUEZ_MANAGER_H__ */
diff --git a/src/devices/bluetooth/nm-bluez5-dun.c b/src/devices/bluetooth/nm-bluez5-dun.c
index 1338bcb811..3038e2635f 100644
--- a/src/devices/bluetooth/nm-bluez5-dun.c
+++ b/src/devices/bluetooth/nm-bluez5-dun.c
@@ -19,145 +19,407 @@
#include "nm-bt-error.h"
#include "NetworkManagerUtils.h"
+#define RFCOMM_FMT "/dev/rfcomm%d"
+
+/*****************************************************************************/
+
+typedef struct {
+ GCancellable *cancellable;
+ NMBluez5DunConnectCb callback;
+ gpointer callback_user_data;
+
+ sdp_session_t *sdp_session;
+
+ GError *rfcomm_sdp_search_error;
+
+ gint64 connect_open_tty_started_at;
+
+ gulong cancelled_id;
+
+ guint source_id;
+
+ guint8 sdp_session_try_count;
+} ConnectData;
+
struct _NMBluez5DunContext {
+ const char *dst_str;
+
+ ConnectData *cdat;
+
+ NMBluez5DunNotifyTtyHangupCb notify_tty_hangup_cb;
+ gpointer notify_tty_hangup_user_data;
+
+ char *rfcomm_tty_path;
+
+ int rfcomm_sock_fd;
+ int rfcomm_tty_fd;
+ int rfcomm_tty_no;
+ int rfcomm_channel;
+
+ guint rfcomm_tty_poll_id;
+
bdaddr_t src;
bdaddr_t dst;
- char *src_str;
- char *dst_str;
- int rfcomm_channel;
- int rfcomm_fd;
- int rfcomm_tty_fd;
- int rfcomm_id;
- NMBluez5DunFunc callback;
- gpointer user_data;
- sdp_session_t *sdp_session;
- guint sdp_watch_id;
+
+ char src_str[];
};
-static void
-dun_connect (NMBluez5DunContext *context)
+/*****************************************************************************/
+
+#define _NMLOG_DOMAIN LOGD_BT
+#define _NMLOG_PREFIX_NAME "bluez"
+#define _NMLOG(level, context, ...) \
+ G_STMT_START { \
+ if (nm_logging_enabled ((level), (_NMLOG_DOMAIN))) { \
+ const NMBluez5DunContext *const _context = (context); \
+ \
+ _nm_log ((level), (_NMLOG_DOMAIN), 0, NULL, NULL, \
+ "%s: DUN[%s] " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+ _NMLOG_PREFIX_NAME, \
+ _context->src_str \
+ _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+ } \
+ } G_STMT_END
+
+/*****************************************************************************/
+
+static void _context_invoke_callback_success (NMBluez5DunContext *context);
+static void _context_invoke_callback_fail_and_free (NMBluez5DunContext *context,
+ GError *error);
+static void _context_free (NMBluez5DunContext *context);
+static int _connect_open_tty (NMBluez5DunContext *context);
+static gboolean _connect_sdp_session_start (NMBluez5DunContext *context,
+ GError **error);
+
+/*****************************************************************************/
+
+NM_AUTO_DEFINE_FCN0 (NMBluez5DunContext *, _nm_auto_free_context, _context_free)
+#define nm_auto_free_context nm_auto(_nm_auto_free_context)
+
+/*****************************************************************************/
+
+const char *
+nm_bluez5_dun_context_get_adapter (const NMBluez5DunContext *context)
{
- struct sockaddr_rc sa;
- int devid, try = 30;
- char tty[100];
- const int ttylen = sizeof (tty) - 1;
- GError *error = NULL;
- int errsv;
+ return context->src_str;
+}
- struct rfcomm_dev_req req = {
- .flags = (1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP),
- .dev_id = -1,
- .channel = context->rfcomm_channel
- };
+const char *
+nm_bluez5_dun_context_get_remote (const NMBluez5DunContext *context)
+{
+ return context->dst_str;
+}
- context->rfcomm_fd = socket (AF_BLUETOOTH, SOCK_STREAM | SOCK_CLOEXEC, BTPROTO_RFCOMM);
- if (context->rfcomm_fd < 0) {
- errsv = errno;
- error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
- "Failed to create RFCOMM socket: (%d) %s",
- errsv, nm_strerror_native (errsv));
- goto done;
- }
+const char *
+nm_bluez5_dun_context_get_rfcomm_dev (const NMBluez5DunContext *context)
+{
+ return context->rfcomm_tty_path;
+}
- /* Connect to the remote device */
- sa.rc_family = AF_BLUETOOTH;
- sa.rc_channel = 0;
- memcpy (&sa.rc_bdaddr, &context->src, ETH_ALEN);
- if (bind (context->rfcomm_fd, (struct sockaddr *) &sa, sizeof(sa))) {
- errsv = errno;
- error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
- "Failed to bind socket: (%d) %s",
- errsv, nm_strerror_native (errsv));
- goto done;
+/*****************************************************************************/
+
+static gboolean
+_rfcomm_tty_poll_cb (GIOChannel *stream,
+ GIOCondition condition,
+ gpointer user_data)
+{
+ NMBluez5DunContext *context = user_data;
+
+ _LOGD (context, "receive %s%s%s signal on rfcomm file descriptor",
+ NM_FLAGS_HAS (condition, G_IO_ERR) ? "ERR" : "",
+ NM_FLAGS_ALL (condition, G_IO_HUP | G_IO_ERR) ? "," : "",
+ NM_FLAGS_HAS (condition, G_IO_HUP) ? "HUP" : "");
+
+ context->rfcomm_tty_poll_id = 0;
+ context->notify_tty_hangup_cb (context,
+ context->notify_tty_hangup_user_data);
+ return G_SOURCE_REMOVE;
+}
+
+static gboolean
+_connect_open_tty_retry_cb (gpointer user_data)
+{
+ NMBluez5DunContext *context = user_data;
+ int r;
+
+ r = _connect_open_tty (context);
+ if (r >= 0)
+ return G_SOURCE_REMOVE;
+
+ if (nm_utils_get_monotonic_timestamp_ns () > context->cdat->connect_open_tty_started_at + (30 * 100 * NM_UTILS_NS_PER_MSEC)) {
+ gs_free_error GError *error = NULL;
+
+ context->cdat->source_id = 0;
+ g_set_error (&error,
+ NM_BT_ERROR,
+ NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "give up waiting to open %s device: %s (%d)",
+ context->rfcomm_tty_path,
+ nm_strerror_native (r),
+ -r);
+ _context_invoke_callback_fail_and_free (context, error);
+ return G_SOURCE_REMOVE;
}
- sa.rc_channel = context->rfcomm_channel;
- memcpy (&sa.rc_bdaddr, &context->dst, ETH_ALEN);
- if (connect (context->rfcomm_fd, (struct sockaddr *) &sa, sizeof (sa)) ) {
- errsv = errno;
- error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
- "Failed to connect to remote device: (%d) %s",
- errsv, nm_strerror_native (errsv));
- goto done;
+ return G_SOURCE_CONTINUE;
+}
+
+static int
+_connect_open_tty (NMBluez5DunContext *context)
+{
+ nm_auto_unref_io_channel GIOChannel *io_channel = NULL;
+ int fd;
+ int errsv;
+
+ fd = open (context->rfcomm_tty_path, O_RDONLY | O_NOCTTY | O_CLOEXEC);
+ if (fd < 0) {
+ errsv = NM_ERRNO_NATIVE (errno);
+
+ if (context->cdat->source_id == 0) {
+ _LOGD (context, "failed opening tty "RFCOMM_FMT": %s (%d). Start polling...",
+ context->rfcomm_tty_no,
+ nm_strerror_native (errsv),
+ errsv);
+ context->cdat->connect_open_tty_started_at = nm_utils_get_monotonic_timestamp_ns ();
+ context->cdat->source_id = g_timeout_add (100,
+ _connect_open_tty_retry_cb,
+ context);
+ }
+ return -errsv;
}
- nm_log_dbg (LOGD_BT, "(%s): connected to %s on channel %d",
- context->src_str, context->dst_str, context->rfcomm_channel);
+ context->rfcomm_tty_fd = fd;
+
+ io_channel = g_io_channel_unix_new (context->rfcomm_tty_fd);
+ context->rfcomm_tty_poll_id = g_io_add_watch (io_channel,
+ G_IO_ERR | G_IO_HUP,
+ _rfcomm_tty_poll_cb,
+ context);
+
+ _context_invoke_callback_success (context);
+ return 0;
+}
+
+static void
+_connect_create_rfcomm (NMBluez5DunContext *context)
+{
+ gs_free_error GError *error = NULL;
+ struct rfcomm_dev_req req;
+ int devid;
+ int errsv;
+ int r;
+
+ _LOGD (context, "connected to %s on channel %d",
+ context->dst_str, context->rfcomm_channel);
/* Create an RFCOMM kernel device for the DUN channel */
+ memset (&req, 0, sizeof (req));
+ req.dev_id = -1;
+ req.flags = (1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP);
+ req.channel = context->rfcomm_channel;
memcpy (&req.src, &context->src, ETH_ALEN);
memcpy (&req.dst, &context->dst, ETH_ALEN);
- devid = ioctl (context->rfcomm_fd, RFCOMMCREATEDEV, &req);
+ devid = ioctl (context->rfcomm_sock_fd, RFCOMMCREATEDEV, &req);
if (devid < 0) {
- errsv = errno;
- error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
- "Failed to create rfcomm device: (%d) %s",
- errsv, nm_strerror_native (errsv));
- goto done;
+ errsv = NM_ERRNO_NATIVE (errno);
+ if (errsv == EBADFD) {
+ /* hm. We use a non-blocking socket to connect. Above getsockopt(SOL_SOCKET,SO_ERROR) indicated
+ * success, but still now we fail with EBADFD. I think that is a bug and we should get the
+ * failure during connect().
+ *
+ * Anyway, craft a less confusing error message than
+ * "failed to create rfcomm device: File descriptor in bad state (77)". */
+ g_set_error (&error,
+ NM_BT_ERROR,
+ NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "unknown failure to connect to DUN device");
+ } else {
+ g_set_error (&error,
+ NM_BT_ERROR,
+ NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "failed to create rfcomm device: %s (%d)",
+ nm_strerror_native (errsv), errsv);
+ }
+ _context_invoke_callback_fail_and_free (context, error);
+ return;
}
- context->rfcomm_id = devid;
- snprintf (tty, ttylen, "/dev/rfcomm%d", devid);
- while ((context->rfcomm_tty_fd = open (tty, O_RDONLY | O_NOCTTY | O_CLOEXEC)) < 0 && try--) {
- if (try) {
- g_usleep (100 * 1000);
- continue;
- }
+ context->rfcomm_tty_no = devid;
+ context->rfcomm_tty_path = g_strdup_printf (RFCOMM_FMT, devid);
+
+ r = _connect_open_tty (context);
+ if (r < 0) {
+ /* we created the rfcomm device, but cannot yet open it. That means, we are
+ * not yet fully connected. However, we notify the caller about "what we learned
+ * so far". Note that this happens synchronously.
+ *
+ * The purpose is that once we proceed synchrnously, modem-manager races with
+ * the detection of the modem. We want to notify the caller first about the
+ * device name. */
+ context->cdat->callback (NULL,
+ context->rfcomm_tty_path,
+ NULL,
+ context->cdat->callback_user_data);
+ }
+}
- error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
- "Failed to find rfcomm device: %s",
- tty);
- break;
+static gboolean
+_connect_socket_connect_cb (GIOChannel *stream,
+ GIOCondition condition,
+ gpointer user_data)
+{
+ NMBluez5DunContext *context = user_data;
+ gs_free GError *error = NULL;
+ int errsv = 0;
+ socklen_t slen = sizeof(errsv);
+ int r;
+
+ context->cdat->source_id = 0;
+
+ r = getsockopt (context->rfcomm_sock_fd, SOL_SOCKET, SO_ERROR, &errsv, &slen);
+
+ if (r < 0) {
+ errsv = errno;
+ g_set_error (&error,
+ NM_BT_ERROR,
+ NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "failed to complete connecting RFCOMM socket: %s (%d)",
+ nm_strerror_native (errsv), errsv);
+ _context_invoke_callback_fail_and_free (context, error);
+ return G_SOURCE_REMOVE;
}
-done:
- context->callback (context, tty, error, context->user_data);
+ if (errsv != 0) {
+ g_set_error (&error,
+ NM_BT_ERROR,
+ NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "failed to connect RFCOMM socket: %s (%d)",
+ nm_strerror_native (errsv), errsv);
+ _context_invoke_callback_fail_and_free (context, error);
+ return G_SOURCE_REMOVE;
+ }
+
+ _connect_create_rfcomm (context);
+ return G_SOURCE_REMOVE;
}
static void
-sdp_search_cleanup (NMBluez5DunContext *context)
+_connect_socket_connect (NMBluez5DunContext *context)
{
- if (context->sdp_session) {
- sdp_close (context->sdp_session);
- context->sdp_session = NULL;
+ gs_free_error GError *error = NULL;
+ struct sockaddr_rc sa;
+ int errsv;
+
+ context->rfcomm_sock_fd = socket (AF_BLUETOOTH, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, BTPROTO_RFCOMM);
+ if (context->rfcomm_sock_fd < 0) {
+ errsv = errno;
+ g_set_error (&error,
+ NM_BT_ERROR,
+ NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "failed to create RFCOMM socket: %s (%d)",
+ nm_strerror_native (errsv), errsv);
+ _context_invoke_callback_fail_and_free (context, error);
+ return;
+ }
+
+ /* Connect to the remote device */
+ memset (&sa, 0, sizeof (sa));
+ sa.rc_family = AF_BLUETOOTH;
+ sa.rc_channel = 0;
+ memcpy (&sa.rc_bdaddr, &context->src, ETH_ALEN);
+ if (bind (context->rfcomm_sock_fd,
+ (struct sockaddr *) &sa,
+ sizeof(sa)) != 0) {
+ errsv = errno;
+ g_set_error (&error,
+ NM_BT_ERROR,
+ NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "failed to bind socket: %s (%d)",
+ nm_strerror_native (errsv), errsv);
+ _context_invoke_callback_fail_and_free (context, error);
+ return;
+ }
+
+ memset (&sa, 0, sizeof (sa));
+ sa.rc_family = AF_BLUETOOTH;
+ sa.rc_channel = context->rfcomm_channel;
+ memcpy (&sa.rc_bdaddr, &context->dst, ETH_ALEN);
+ if (connect (context->rfcomm_sock_fd,
+ (struct sockaddr *) &sa,
+ sizeof (sa)) != 0) {
+ nm_auto_unref_io_channel GIOChannel *io_channel = NULL;
+
+ errsv = errno;
+ if (errsv != EINPROGRESS) {
+ g_set_error (&error,
+ NM_BT_ERROR,
+ NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "failed to connect to remote device: %s (%d)",
+ nm_strerror_native (errsv), errsv);
+ _context_invoke_callback_fail_and_free (context, error);
+ return;
+ }
+
+ _LOGD (context, "connecting to %s on channel %d...",
+ context->dst_str,
+ context->rfcomm_channel);
+
+ io_channel = g_io_channel_unix_new (context->rfcomm_sock_fd);
+ context->cdat->source_id = g_io_add_watch (io_channel,
+ G_IO_OUT,
+ _connect_socket_connect_cb,
+ context);
+ return;
}
- nm_clear_g_source (&context->sdp_watch_id);
+ _connect_create_rfcomm (context);
}
static void
-sdp_search_completed_cb (uint8_t type, uint16_t status, uint8_t *rsp, size_t size, void *user_data)
+_connect_sdp_search_cb (uint8_t type,
+ uint16_t status,
+ uint8_t *rsp,
+ size_t size,
+ void *user_data)
{
NMBluez5DunContext *context = user_data;
- int scanned, seqlen = 0, bytesleft = size;
+ int scanned;
+ int seqlen = 0;
+ int bytesleft = size;
uint8_t dataType;
int channel = -1;
- nm_log_dbg (LOGD_BT, "(%s -> %s): SDP search finished with type=%d status=%d",
- context->src_str, context->dst_str, status, type);
+ if ( context->cdat->rfcomm_sdp_search_error
+ || context->rfcomm_channel >= 0)
+ return;
+
+ _LOGD (context, "SDP search finished with type=%d status=%d",
+ status, type);
/* SDP response received */
- if (status || type != SDP_SVC_SEARCH_ATTR_RSP) {
- GError *error = g_error_new (NM_BT_ERROR,
- NM_BT_ERROR_DUN_CONNECT_FAILED,
- "Did not get a Service Discovery response");
- context->callback (context, NULL, error, context->user_data);
- goto done;
+ if ( status
+ || type != SDP_SVC_SEARCH_ATTR_RSP) {
+ g_set_error (&context->cdat->rfcomm_sdp_search_error,
+ NM_BT_ERROR,
+ NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "did not get a Service Discovery response");
+ return;
}
scanned = sdp_extract_seqtype (rsp, bytesleft, &dataType, &seqlen);
- nm_log_dbg (LOGD_BT, "(%s -> %s): SDP sequence type scanned=%d length=%d",
- context->src_str, context->dst_str, scanned, seqlen);
+ _LOGD (context, "SDP sequence type scanned=%d length=%d",
+ scanned, seqlen);
scanned = sdp_extract_seqtype (rsp, bytesleft, &dataType, &seqlen);
- if (!scanned || !seqlen) {
+ if ( !scanned
+ || !seqlen) {
/* Short read or unknown sequence type */
- GError *error = g_error_new (NM_BT_ERROR,
- NM_BT_ERROR_DUN_CONNECT_FAILED,
- "Improper Service Discovery response");
- context->callback (context, NULL, error, context->user_data);
- goto done;
+ g_set_error (&context->cdat->rfcomm_sdp_search_error,
+ NM_BT_ERROR,
+ NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "improper Service Discovery response");
+ return;
}
rsp += scanned;
@@ -181,90 +443,153 @@ sdp_search_completed_cb (uint8_t type, uint16_t status, uint8_t *rsp, size_t siz
channel = sdp_get_proto_port (protos, RFCOMM_UUID);
sdp_list_free (protos, NULL);
- nm_log_dbg (LOGD_BT, "(%s -> %s): SDP channel=%d",
- context->src_str, context->dst_str, channel);
+ _LOGD (context, "SDP channel=%d",
+ channel);
}
sdp_record_free (rec);
scanned += recsize;
rsp += recsize;
bytesleft -= recsize;
- } while ((scanned < (ssize_t) size) && (bytesleft > 0) && (channel < 0));
-
-done:
- if (channel != -1) {
- context->rfcomm_channel = channel;
- dun_connect (context);
+ } while ( scanned < (ssize_t) size
+ && bytesleft > 0
+ && channel < 0);
+
+ if (channel == -1) {
+ g_set_error (&context->cdat->rfcomm_sdp_search_error,
+ NM_BT_ERROR,
+ NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "did not receive rfcomm-channel");
+ return;
}
- sdp_search_cleanup (context);
+ context->rfcomm_channel = channel;
}
static gboolean
-sdp_search_process_cb (GIOChannel *channel, GIOCondition condition, gpointer user_data)
+_connect_sdp_search_io_cb (GIOChannel *io_channel,
+ GIOCondition condition,
+ gpointer user_data)
{
NMBluez5DunContext *context = user_data;
-
- nm_log_dbg (LOGD_BT, "(%s -> %s): SDP search progressed with condition=%d",
- context->src_str, context->dst_str, condition);
+ gs_free GError *error = NULL;
+ int errsv;
if (condition & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
- GError *error = g_error_new (NM_BT_ERROR,
- NM_BT_ERROR_DUN_CONNECT_FAILED,
- "Service Discovery interrupted");
- context->callback (context, NULL, error, context->user_data);
- sdp_search_cleanup (context);
- return FALSE;
+ _LOGD (context, "SDP search returned with invalid IO condition 0x%x",
+ (guint) condition);
+ error = g_error_new (NM_BT_ERROR,
+ NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "Service Discovery interrupted");
+ context->cdat->source_id = 0;
+ _context_invoke_callback_fail_and_free (context, error);
+ return G_SOURCE_REMOVE;
}
- if (sdp_process (context->sdp_session) < 0) {
- nm_log_dbg (LOGD_BT, "(%s -> %s): SDP search finished",
- context->src_str, context->dst_str);
+ if (sdp_process (context->cdat->sdp_session) == 0) {
+ _LOGD (context, "SDP search still not finished");
+ return G_SOURCE_CONTINUE;
+ }
- /* Search finished successfully. */
- return FALSE;
+ context->cdat->source_id = 0;
+
+ if ( context->rfcomm_channel < 0
+ && !context->cdat->rfcomm_sdp_search_error) {
+ errsv = sdp_get_error (context->cdat->sdp_session);
+ _LOGD (context, "SDP search failed: %s (%d)",
+ nm_strerror_native (errsv), errsv);
+ error = g_error_new (NM_BT_ERROR,
+ NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "Service Discovery failed with %s (%d)",
+ nm_strerror_native (errsv), errsv);
+ _context_invoke_callback_fail_and_free (context, error);
+ return G_SOURCE_REMOVE;
}
- /* Search progressed successfully. */
- return TRUE;
+ if (context->cdat->rfcomm_sdp_search_error) {
+ _LOGD (context, "SDP search failed to complete: %s", context->cdat->rfcomm_sdp_search_error->message);
+ _context_invoke_callback_fail_and_free (context, context->cdat->rfcomm_sdp_search_error);
+ return G_SOURCE_REMOVE;
+ }
+
+ nm_clear_pointer (&context->cdat->sdp_session, sdp_close);
+
+ _connect_socket_connect (context);
+
+ return G_SOURCE_REMOVE;
}
static gboolean
-sdp_connect_watch (GIOChannel *channel, GIOCondition condition, gpointer user_data)
+_connect_sdp_session_start_on_idle_cb (gpointer user_data)
{
NMBluez5DunContext *context = user_data;
- sdp_list_t *search, *attrs;
+ gs_free_error GError *error = NULL;
+
+ context->cdat->source_id = 0;
+
+ _LOGD (context, "retry starting sdp-session...");
+
+ if (!_connect_sdp_session_start (context, &error))
+ _context_invoke_callback_fail_and_free (context, error);
+
+ return G_SOURCE_REMOVE;
+}
+
+static gboolean
+_connect_sdp_io_cb (GIOChannel *io_channel,
+ GIOCondition condition,
+ gpointer user_data)
+{
+ NMBluez5DunContext *context = user_data;
+ sdp_list_t *search;
+ sdp_list_t *attrs;
uuid_t svclass;
uint16_t attr;
- int fd, fd_err = 0;
- int err;
+ int fd;
+ int errsv;
+ int fd_err = 0;
+ int r;
socklen_t len = sizeof (fd_err);
- GError *error = NULL;
+ gs_free_error GError *error = NULL;
+
+ context->cdat->source_id = 0;
- context->sdp_watch_id = 0;
+ fd = g_io_channel_unix_get_fd (io_channel);
+
+ _LOGD (context, "sdp-session ready to connect with fd=%d", fd);
- fd = g_io_channel_unix_get_fd (channel);
if (getsockopt (fd, SOL_SOCKET, SO_ERROR, &fd_err, &len) < 0) {
- err = errno;
- nm_log_dbg (LOGD_BT, "(%s -> %s): getsockopt error=%d",
- context->src_str, context->dst_str, err);
- } else {
- err = fd_err;
- nm_log_dbg (LOGD_BT, "(%s -> %s): SO_ERROR error=%d",
- context->src_str, context->dst_str, fd_err);
+ errsv = NM_ERRNO_NATIVE (errno);
+ error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "error for getsockopt on Service Discovery socket: %s (%d)",
+ nm_strerror_native (errsv), errsv);
+ goto done;
}
- if (err != 0) {
+ if (fd_err != 0) {
+ errsv = nm_errno_native (fd_err);
+
+ if ( NM_IN_SET (errsv, ECONNREFUSED, EHOSTDOWN)
+ && --context->cdat->sdp_session_try_count > 0) {
+ /* *sigh* */
+ _LOGD (context, "sdp-session failed with %s (%d). Retry in a bit", nm_strerror_native (errsv), errsv);
+ nm_clear_g_source (&context->cdat->source_id);
+ context->cdat->source_id = g_timeout_add (1000,
+ _connect_sdp_session_start_on_idle_cb,
+ context);
+ return G_SOURCE_REMOVE;
+ }
+
error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
- "Error on Service Discovery socket: (%d) %s",
- err, nm_strerror_native (err));
+ "error on Service Discovery socket: %s (%d)",
+ nm_strerror_native (errsv), errsv);
goto done;
}
- if (sdp_set_notify (context->sdp_session, sdp_search_completed_cb, context) < 0) {
+ if (sdp_set_notify (context->cdat->sdp_session, _connect_sdp_search_cb, context) < 0) {
/* Should not be reached, only can fail if we passed bad sdp_session. */
error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
- "Could not request Service Discovery notification");
+ "could not set Service Discovery notification");
goto done;
}
@@ -273,124 +598,261 @@ sdp_connect_watch (GIOChannel *channel, GIOCondition condition, gpointer user_da
attr = SDP_ATTR_PROTO_DESC_LIST;
attrs = sdp_list_append (NULL, &attr);
- if (!sdp_service_search_attr_async (context->sdp_session, search, SDP_ATTR_REQ_INDIVIDUAL, attrs)) {
- /* Set callback responsible for update the internal SDP transaction */
- context->sdp_watch_id = g_io_add_watch (channel,
- G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
- sdp_search_process_cb,
- context);
- } else {
- err = sdp_get_error (context->sdp_session);
- error = g_error_new (NM_BT_ERROR,
- NM_BT_ERROR_DUN_CONNECT_FAILED,
- "Error starting Service Discovery: (%d) %s",
- err, nm_strerror_native (err));
- }
+ r = sdp_service_search_attr_async (context->cdat->sdp_session,
+ search,
+ SDP_ATTR_REQ_INDIVIDUAL,
+ attrs);
sdp_list_free (attrs, NULL);
sdp_list_free (search, NULL);
-done:
- if (error) {
- context->callback (context, NULL, error, context->user_data);
- sdp_search_cleanup (context);
+ if (r < 0) {
+ errsv = nm_errno_native (sdp_get_error (context->cdat->sdp_session));
+ error = g_error_new (NM_BT_ERROR,
+ NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "error starting Service Discovery: %s (%d)",
+ nm_strerror_native (errsv), errsv);
+ goto done;
}
+ /* Set callback responsible for update the internal SDP transaction */
+ context->cdat->source_id = g_io_add_watch (io_channel,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ _connect_sdp_search_io_cb,
+ context);
+
+done:
+ if (error)
+ _context_invoke_callback_fail_and_free (context, error);
return G_SOURCE_REMOVE;
}
-NMBluez5DunContext *
-nm_bluez5_dun_new (const char *adapter,
- const char *remote)
+/*****************************************************************************/
+static void
+_connect_cancelled_cb (GCancellable *cancellable,
+ NMBluez5DunContext *context)
{
- NMBluez5DunContext *context;
-
- context = g_slice_new0 (NMBluez5DunContext);
- str2ba (adapter, &context->src);
- str2ba (remote, &context->dst);
- context->src_str = g_strdup (adapter);
- context->dst_str = g_strdup (remote);
- context->rfcomm_channel = -1;
- context->rfcomm_id = -1;
- context->rfcomm_fd = -1;
- return context;
+ gs_free_error GError *error = NULL;
+
+ if (!g_cancellable_set_error_if_cancelled (cancellable, &error))
+ g_return_if_reached ();
+
+ _context_invoke_callback_fail_and_free (context, error);
}
-void
-nm_bluez5_dun_connect (NMBluez5DunContext *context,
- NMBluez5DunFunc callback,
- gpointer user_data)
+static gboolean
+_connect_sdp_session_start (NMBluez5DunContext *context,
+ GError **error)
{
- GIOChannel *channel;
+ nm_auto_unref_io_channel GIOChannel *io_channel = NULL;
- context->callback = callback;
- context->user_data = user_data;
+ nm_assert (context->cdat);
- if (context->rfcomm_channel != -1) {
- nm_log_dbg (LOGD_BT, "(%s): channel number on device %s cached: %d",
- context->src_str, context->dst_str, context->rfcomm_channel);
- /* FIXME: don't invoke the callback synchronously. */
- dun_connect (context);
- return;
+ nm_clear_g_source (&context->cdat->source_id);
+ nm_clear_pointer (&context->cdat->sdp_session, sdp_close);
+
+ context->cdat->sdp_session = sdp_connect (&context->src, &context->dst, SDP_NON_BLOCKING);
+ if (!context->cdat->sdp_session) {
+ int errsv = nm_errno_native (errno);
+
+ g_set_error (error, NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "failed to connect to the SDP server: %s (%d)",
+ nm_strerror_native (errsv), errsv);
+ return FALSE;
}
- nm_log_dbg (LOGD_BT, "(%s): starting channel number discovery for device %s",
- context->src_str, context->dst_str);
+ io_channel = g_io_channel_unix_new (sdp_get_socket (context->cdat->sdp_session));
+ context->cdat->source_id = g_io_add_watch (io_channel,
+ G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ _connect_sdp_io_cb,
+ context);
+ return TRUE;
+}
- context->sdp_session = sdp_connect (&context->src, &context->dst, SDP_NON_BLOCKING);
- if (!context->sdp_session) {
- int err = errno;
- GError *error;
+/*****************************************************************************/
+
+gboolean
+nm_bluez5_dun_connect (const char *adapter,
+ const char *remote,
+ GCancellable *cancellable,
+ NMBluez5DunConnectCb callback,
+ gpointer callback_user_data,
+ NMBluez5DunNotifyTtyHangupCb notify_tty_hangup_cb,
+ gpointer notify_tty_hangup_user_data,
+ GError **error)
+{
+ nm_auto_free_context NMBluez5DunContext *context = NULL;
+ ConnectData *cdat;
+ gsize src_l;
+ gsize dst_l;
+
+ g_return_val_if_fail (adapter, FALSE);
+ g_return_val_if_fail (remote, FALSE);
+ g_return_val_if_fail (G_IS_CANCELLABLE (cancellable), FALSE);
+ g_return_val_if_fail (callback, FALSE);
+ g_return_val_if_fail (notify_tty_hangup_cb, FALSE);
+ g_return_val_if_fail (!error || !*error, FALSE);
+ nm_assert (!g_cancellable_is_cancelled (cancellable));
+
+ src_l = strlen (adapter) + 1;
+ dst_l = strlen (remote) + 1;
+
+ cdat = g_slice_new (ConnectData);
+ *cdat = (ConnectData) {
+ .callback = callback,
+ .callback_user_data = callback_user_data,
+ .cancellable = g_object_ref (cancellable),
+ .sdp_session_try_count = 5,
+ };
- error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
- "Failed to connect to the SDP server: (%d) %s",
- err, nm_strerror_native (err));
- /* FIXME: don't invoke the callback synchronously. */
- context->callback (context, NULL, error, context->user_data);
- return;
+ context = g_malloc (sizeof (NMBluez5DunContext) + src_l + dst_l);
+ *context = (NMBluez5DunContext) {
+ .cdat = cdat,
+ .notify_tty_hangup_cb = notify_tty_hangup_cb,
+ .notify_tty_hangup_user_data = notify_tty_hangup_user_data,
+ .rfcomm_tty_no = -1,
+ .rfcomm_sock_fd = -1,
+ .rfcomm_tty_fd = -1,
+ .rfcomm_channel = -1,
+ };
+ memcpy (&context->src_str[0], adapter, src_l);
+ context->dst_str = &context->src_str[src_l];
+ memcpy ((char *) context->dst_str, remote, dst_l);
+
+ if (str2ba (adapter, &context->src) < 0) {
+ g_set_error (error, NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "invalid source");
+ return FALSE;
}
- /* FIXME(shutdown): make connect cancellable. */
- channel = g_io_channel_unix_new (sdp_get_socket (context->sdp_session));
- context->sdp_watch_id = g_io_add_watch (channel,
- G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
- sdp_connect_watch,
- context);
- g_io_channel_unref (channel);
+ if (str2ba (remote, &context->dst) < 0) {
+ g_set_error (error, NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED,
+ "invalid remote");
+ return FALSE;
+ }
+
+ context->cdat->cancelled_id = g_signal_connect (context->cdat->cancellable,
+ "cancelled",
+ G_CALLBACK (_connect_cancelled_cb),
+ context);
+
+ if (!_connect_sdp_session_start (context, error))
+ return FALSE;
+
+ _LOGD (context, "starting channel number discovery for device %s",
+ context->dst_str);
+
+ g_steal_pointer (&context);
+ return TRUE;
}
-/* Only clean up connection-related stuff to allow reconnect */
+/*****************************************************************************/
+
void
-nm_bluez5_dun_cleanup (NMBluez5DunContext *context)
+nm_bluez5_dun_disconnect (NMBluez5DunContext *context)
{
- g_return_if_fail (context != NULL);
+ nm_assert (context);
+ nm_assert (!context->cdat);
- sdp_search_cleanup (context);
+ _LOGD (context, "disconnecting DUN connection");
- if (context->rfcomm_fd >= 0) {
- if (context->rfcomm_id >= 0) {
- struct rfcomm_dev_req req = { 0 };
+ _context_free (context);
+}
- req.dev_id = context->rfcomm_id;
- (void) ioctl (context->rfcomm_fd, RFCOMMRELEASEDEV, &req);
- context->rfcomm_id = -1;
- }
- nm_close (context->rfcomm_fd);
- context->rfcomm_fd = -1;
- }
+/*****************************************************************************/
- nm_close (context->rfcomm_tty_fd);
- context->rfcomm_tty_fd = -1;
+static void
+_context_cleanup_connect_data (NMBluez5DunContext *context)
+{
+ ConnectData *cdat;
+
+ cdat = g_steal_pointer (&context->cdat);
+ if (!cdat)
+ return;
+
+ nm_clear_g_signal_handler (cdat->cancellable, &cdat->cancelled_id);
+
+ nm_clear_g_source (&cdat->source_id);
+
+ nm_clear_pointer (&cdat->sdp_session, sdp_close);
+
+ g_clear_object (&cdat->cancellable);
+
+ g_clear_error (&cdat->rfcomm_sdp_search_error);
+
+ nm_g_slice_free (cdat);
}
-void
-nm_bluez5_dun_free (NMBluez5DunContext *context)
+static void
+_context_invoke_callback (NMBluez5DunContext *context,
+ GError *error)
+{
+ NMBluez5DunConnectCb callback;
+ gpointer callback_user_data;
+
+ nm_assert (context);
+ nm_assert (context->cdat);
+ nm_assert (context->cdat->callback);
+ nm_assert (error || context->rfcomm_tty_path);
+
+ if (!error)
+ _LOGD (context, "connected via \"%s\"", context->rfcomm_tty_path);
+ else if (nm_utils_error_is_cancelled (error, FALSE))
+ _LOGD (context, "cancelled");
+ else
+ _LOGD (context, "failed to connect: %s", error->message);
+
+ callback = context->cdat->callback;
+ callback_user_data = context->cdat->callback_user_data;
+
+ _context_cleanup_connect_data (context);
+
+ callback (error ? NULL : context,
+ error ? NULL : context->rfcomm_tty_path,
+ error,
+ callback_user_data);
+}
+
+static void
+_context_invoke_callback_success (NMBluez5DunContext *context)
{
- g_return_if_fail (context != NULL);
+ nm_assert (context->rfcomm_tty_path);
+ _context_invoke_callback (context, NULL);
+}
+
+static void
+_context_invoke_callback_fail_and_free (NMBluez5DunContext *context,
+ GError *error)
+{
+ nm_assert (error);
+ _context_invoke_callback (context, error);
+ _context_free (context);
+}
+
+static void
+_context_free (NMBluez5DunContext *context)
+{
+ nm_assert (context);
+
+ _context_cleanup_connect_data (context);
+
+ nm_clear_g_source (&context->rfcomm_tty_poll_id);
+
+ if (context->rfcomm_sock_fd >= 0) {
+ if (context->rfcomm_tty_no >= 0) {
+ struct rfcomm_dev_req req;
+
+ memset (&req, 0, sizeof (struct rfcomm_dev_req));
+ req.dev_id = context->rfcomm_tty_no;
+ context->rfcomm_tty_no = -1;
+ (void) ioctl (context->rfcomm_sock_fd, RFCOMMRELEASEDEV, &req);
+ }
+ nm_close (nm_steal_fd (&context->rfcomm_sock_fd));
+ }
- nm_bluez5_dun_cleanup (context);
- g_clear_pointer (&context->src_str, g_free);
- g_clear_pointer (&context->dst_str, g_free);
- g_slice_free (NMBluez5DunContext, context);
+ if (context->rfcomm_tty_fd >= 0)
+ nm_close (nm_steal_fd (&context->rfcomm_tty_fd));
+ nm_clear_g_free (&context->rfcomm_tty_path);
+ g_free (context);
}
diff --git a/src/devices/bluetooth/nm-bluez5-dun.h b/src/devices/bluetooth/nm-bluez5-dun.h
index d09046fa3d..6142eac26c 100644
--- a/src/devices/bluetooth/nm-bluez5-dun.h
+++ b/src/devices/bluetooth/nm-bluez5-dun.h
@@ -4,27 +4,36 @@
* Copyright (C) 2014 Red Hat, Inc.
*/
-#ifndef _NM_BLUEZ5_UTILS_H_
-#define _NM_BLUEZ5_UTILS_H_
+#ifndef __NM_BLUEZ5_DUN_H__
+#define __NM_BLUEZ5_DUN_H__
typedef struct _NMBluez5DunContext NMBluez5DunContext;
-typedef void (*NMBluez5DunFunc) (NMBluez5DunContext *context,
- const char *rfcomm_dev,
- GError *error,
- gpointer user_data);
+#if WITH_BLUEZ5_DUN
-NMBluez5DunContext *nm_bluez5_dun_new (const char *adapter,
- const char *remote);
+typedef void (*NMBluez5DunConnectCb) (NMBluez5DunContext *context,
+ const char *rfcomm_dev,
+ GError *error,
+ gpointer user_data);
-void nm_bluez5_dun_connect (NMBluez5DunContext *context,
- NMBluez5DunFunc callback,
- gpointer user_data);
+typedef void (*NMBluez5DunNotifyTtyHangupCb) (NMBluez5DunContext *context,
+ gpointer user_data);
-/* Clean up connection resources */
-void nm_bluez5_dun_cleanup (NMBluez5DunContext *context);
+gboolean nm_bluez5_dun_connect (const char *adapter,
+ const char *remote,
+ GCancellable *cancellable,
+ NMBluez5DunConnectCb callback,
+ gpointer callback_user_data,
+ NMBluez5DunNotifyTtyHangupCb notify_tty_hangup_cb,
+ gpointer notify_tty_hangup_user_data,
+ GError **error);
-/* Clean up and dispose all resources */
-void nm_bluez5_dun_free (NMBluez5DunContext *context);
+void nm_bluez5_dun_disconnect (NMBluez5DunContext *context);
-#endif /* _NM_BLUEZ5_UTILS_H_ */
+const char *nm_bluez5_dun_context_get_adapter (const NMBluez5DunContext *context);
+const char *nm_bluez5_dun_context_get_remote (const NMBluez5DunContext *context);
+const char *nm_bluez5_dun_context_get_rfcomm_dev (const NMBluez5DunContext *context);
+
+#endif /* WITH_BLUEZ5_DUN */
+
+#endif /* __NM_BLUEZ5_DUN_H__ */
diff --git a/src/devices/bluetooth/nm-bluez5-manager.c b/src/devices/bluetooth/nm-bluez5-manager.c
deleted file mode 100644
index 9ede4d709e..0000000000
--- a/src/devices/bluetooth/nm-bluez5-manager.c
+++ /dev/null
@@ -1,585 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/* NetworkManager -- Network link manager
- *
- * Copyright (C) 2007 - 2008 Novell, Inc.
- * Copyright (C) 2007 - 2017 Red Hat, Inc.
- * Copyright (C) 2013 Intel Corporation.
- */
-
-#include "nm-default.h"
-
-#include "nm-bluez5-manager.h"
-
-#include <signal.h>
-#include <stdlib.h>
-
-#include "nm-core-internal.h"
-
-#include "nm-std-aux/nm-dbus-compat.h"
-#include "c-list/src/c-list.h"
-#include "nm-bluez-device.h"
-#include "nm-bluez-common.h"
-#include "devices/nm-device-bridge.h"
-#include "settings/nm-settings.h"
-
-/*****************************************************************************/
-
-enum {
- BDADDR_ADDED,
- NETWORK_SERVER_ADDED,
- LAST_SIGNAL,
-};
-
-static guint signals[LAST_SIGNAL] = { 0 };
-
-typedef struct {
- NMSettings *settings;
-
- GDBusProxy *proxy;
-
- GHashTable *devices;
-
- CList network_servers;
-} NMBluez5ManagerPrivate;
-
-struct _NMBluez5Manager {
- GObject parent;
- NMBtVTableNetworkServer network_server_vtable;
- NMBluez5ManagerPrivate _priv;
-};
-
-struct _NMBluez5ManagerClass {
- GObjectClass parent;
-};
-
-G_DEFINE_TYPE (NMBluez5Manager, nm_bluez5_manager, G_TYPE_OBJECT)
-
-#define NM_BLUEZ5_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMBluez5Manager, NM_IS_BLUEZ5_MANAGER)
-
-#define NM_BLUEZ5_MANAGER_GET_NETWORK_SERVER_VTABLE(self) (&(self)->network_server_vtable)
-
-#define NETWORK_SERVER_VTABLE_GET_NM_BLUEZ5_MANAGER(vtable) \
- NM_BLUEZ5_MANAGER(((char *)(vtable)) - offsetof (struct _NMBluez5Manager, network_server_vtable))
-
-/*****************************************************************************/
-
-#define _NMLOG_DOMAIN LOGD_BT
-#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "bluez5", __VA_ARGS__)
-
-/*****************************************************************************/
-
-static void device_initialized (NMBluezDevice *device, gboolean success, NMBluez5Manager *self);
-static void device_usable (NMBluezDevice *device, GParamSpec *pspec, NMBluez5Manager *self);
-
-/*****************************************************************************/
-
-typedef struct {
- char *path;
- char *addr;
- NMDevice *device;
- CList lst_ns;
-} NetworkServer;
-
-static NetworkServer *
-_find_network_server (NMBluez5Manager *self, const char *path, NMDevice *device)
-{
- NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
- NetworkServer *network_server;
-
- nm_assert (path || NM_IS_DEVICE (device));
-
- c_list_for_each_entry (network_server, &priv->network_servers, lst_ns) {
- if (path && !nm_streq (network_server->path, path))
- continue;
- if (device && network_server->device != device)
- continue;
- return network_server;
- }
- return NULL;
-}
-
-static NetworkServer *
-_find_network_server_for_addr (NMBluez5Manager *self, const char *addr)
-{
- NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
- NetworkServer *network_server;
-
- c_list_for_each_entry (network_server, &priv->network_servers, lst_ns) {
- /* The address lookups need a server not assigned to a device
- * and tolerate an empty address as a wildcard for "any". */
- if ( !network_server->device
- && (!addr || nm_streq (network_server->addr, addr)))
- return network_server;
- }
- return NULL;
-}
-
-static void
-_network_server_unregister (NMBluez5Manager *self, NetworkServer *network_server)
-{
- NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
-
- if (!network_server->device) {
- /* Not connected. */
- return;
- }
-
- _LOGI ("NAP: unregistering %s from %s",
- nm_device_get_iface (network_server->device),
- network_server->addr);
-
- g_dbus_connection_call (g_dbus_proxy_get_connection (priv->proxy),
- NM_BLUEZ_SERVICE,
- network_server->path,
- NM_BLUEZ5_NETWORK_SERVER_INTERFACE,
- "Unregister",
- g_variant_new ("(s)", BLUETOOTH_CONNECT_NAP),
- NULL,
- G_DBUS_CALL_FLAGS_NONE,
- -1, NULL, NULL, NULL);
-
- g_clear_object (&network_server->device);
-}
-
-static void
-_network_server_free (NMBluez5Manager *self, NetworkServer *network_server)
-{
- _network_server_unregister (self, network_server);
- c_list_unlink_stale (&network_server->lst_ns);
- g_free (network_server->path);
- g_free (network_server->addr);
- g_slice_free (NetworkServer, network_server);
-}
-
-static gboolean
-network_server_is_available (const NMBtVTableNetworkServer *vtable,
- const char *addr)
-{
- NMBluez5Manager *self = NETWORK_SERVER_VTABLE_GET_NM_BLUEZ5_MANAGER (vtable);
-
- return !!_find_network_server_for_addr (self, addr);
-}
-
-static gboolean
-network_server_register_bridge (const NMBtVTableNetworkServer *vtable,
- const char *addr,
- NMDevice *device)
-{
- NMBluez5Manager *self = NETWORK_SERVER_VTABLE_GET_NM_BLUEZ5_MANAGER (vtable);
- NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
- NetworkServer *network_server = _find_network_server_for_addr (self, addr);
-
- nm_assert (NM_IS_DEVICE (device));
- nm_assert (!_find_network_server (self, NULL, device));
-
- if (!network_server) {
- /* The device checked that a network server is available, before
- * starting the activation, but for some reason it no longer is.
- * Indicate that the activation should not proceed. */
- _LOGI ("NAP: %s is not available for %s", addr, nm_device_get_iface (device));
- return FALSE;
- }
-
- _LOGI ("NAP: registering %s on %s", nm_device_get_iface (device), network_server->addr);
-
- g_dbus_connection_call (g_dbus_proxy_get_connection (priv->proxy),
- NM_BLUEZ_SERVICE,
- network_server->path,
- NM_BLUEZ5_NETWORK_SERVER_INTERFACE,
- "Register",
- g_variant_new ("(ss)", BLUETOOTH_CONNECT_NAP, nm_device_get_iface (device)),
- NULL,
- G_DBUS_CALL_FLAGS_NONE,
- -1, NULL, NULL, NULL);
-
- network_server->device = g_object_ref (device);
-
- return TRUE;
-}
-
-static gboolean
-network_server_unregister_bridge (const NMBtVTableNetworkServer *vtable,
- NMDevice *device)
-{
- NMBluez5Manager *self = NETWORK_SERVER_VTABLE_GET_NM_BLUEZ5_MANAGER (vtable);
- NetworkServer *network_server = _find_network_server (self, NULL, device);
-
- if (network_server)
- _network_server_unregister (self, network_server);
-
- return TRUE;
-}
-
-static void
-network_server_removed (GDBusProxy *proxy, const char *path, NMBluez5Manager *self)
-{
- NetworkServer *network_server;
-
- network_server = _find_network_server (self, path, NULL);
- if (!network_server)
- return;
-
- if (network_server->device) {
- nm_device_queue_state (network_server->device, NM_DEVICE_STATE_DISCONNECTED,
- NM_DEVICE_STATE_REASON_BT_FAILED);
- }
- _LOGI ("NAP: removed interface %s", network_server->addr);
- _network_server_free (self, network_server);
-}
-
-static void
-network_server_added (GDBusProxy *proxy, const char *path, const char *addr, NMBluez5Manager *self)
-{
- NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
- NetworkServer *network_server;
-
- /* If BlueZ messes up and announces a single network server twice,
- * make sure we get rid of the older instance first. */
- network_server_removed (proxy, path, self);
-
- network_server = g_slice_new0 (NetworkServer);
- network_server->path = g_strdup (path);
- network_server->addr = g_strdup (addr);
- c_list_link_before (&priv->network_servers, &network_server->lst_ns);
-
- _LOGI ("NAP: added interface %s", addr);
-
- g_signal_emit (self, signals[NETWORK_SERVER_ADDED], 0);
-}
-
-/*****************************************************************************/
-
-static void
-emit_bdaddr_added (NMBluez5Manager *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_bluez5_manager_query_devices (NMBluez5Manager *self)
-{
- NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
- NMBluezDevice *device;
- GHashTableIter iter;
-
- g_hash_table_iter_init (&iter, priv->devices);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer) &device)) {
- if (nm_bluez_device_get_usable (device))
- emit_bdaddr_added (self, device);
- }
-}
-
-static void
-remove_device (NMBluez5Manager *self, NMBluezDevice *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);
- if (nm_bluez_device_get_usable (device))
- g_signal_emit_by_name (device, NM_BLUEZ_DEVICE_REMOVED);
-}
-
-static void
-remove_all_devices (NMBluez5Manager *self)
-{
- GHashTableIter iter;
- NMBluezDevice *device;
- NMBluez5ManagerPrivate *priv = NM_BLUEZ5_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, NMBluez5Manager *self)
-{
- gboolean usable = nm_bluez_device_get_usable (device);
-
- _LOGD ("(%s): bluez device now %s",
- nm_bluez_device_get_path (device),
- usable ? "usable" : "unusable");
-
- if (usable) {
- _LOGD ("(%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_by_name (device, NM_BLUEZ_DEVICE_REMOVED);
-}
-
-static void
-device_initialized (NMBluezDevice *device, gboolean success, NMBluez5Manager *self)
-{
- NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
-
- _LOGD ("(%s): bluez device %s",
- nm_bluez_device_get_path (device),
- success ? "initialized" : "failed to initialize");
- if (!success)
- g_hash_table_remove (priv->devices, nm_bluez_device_get_path (device));
-}
-
-static void
-device_added (GDBusProxy *proxy, const char *path, NMBluez5Manager *self)
-{
- NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
- NMBluezDevice *device;
-
- device = nm_bluez_device_new (g_dbus_proxy_get_connection (proxy), path, priv->settings);
- g_signal_connect (device, NM_BLUEZ_DEVICE_INITIALIZED, G_CALLBACK (device_initialized), self);
- g_signal_connect (device, "notify::" NM_BLUEZ_DEVICE_USABLE, G_CALLBACK (device_usable), self);
- g_hash_table_insert (priv->devices, (gpointer) nm_bluez_device_get_path (device), device);
-
- _LOGD ("(%s): new bluez device found", path);
-}
-
-static void
-device_removed (GDBusProxy *proxy, const char *path, NMBluez5Manager *self)
-{
- NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
- NMBluezDevice *device;
-
- _LOGD ("(%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_BLUEZ5_MANAGER (self), device);
- g_object_unref (device);
- }
-}
-
-static void
-object_manager_interfaces_added (GDBusProxy *proxy,
- const char *path,
- GVariant *dict,
- NMBluez5Manager *self)
-{
- if (g_variant_lookup (dict, NM_BLUEZ5_DEVICE_INTERFACE, "a{sv}", NULL))
- device_added (proxy, path, self);
- if (g_variant_lookup (dict, NM_BLUEZ5_NETWORK_SERVER_INTERFACE, "a{sv}", NULL)) {
- gs_unref_variant GVariant *adapter = g_variant_lookup_value (dict, NM_BLUEZ5_ADAPTER_INTERFACE, G_VARIANT_TYPE_DICTIONARY);
- const char *address;
-
- if ( adapter
- && g_variant_lookup (adapter, "Address", "&s", &address))
- network_server_added (proxy, path, address, self);
- }
-}
-
-static void
-object_manager_interfaces_removed (GDBusProxy *proxy,
- const char *path,
- const char **ifaces,
- NMBluez5Manager *self)
-{
- if (ifaces && g_strv_contains (ifaces, NM_BLUEZ5_DEVICE_INTERFACE))
- device_removed (proxy, path, self);
- if (ifaces && g_strv_contains (ifaces, NM_BLUEZ5_NETWORK_SERVER_INTERFACE))
- network_server_removed (proxy, path, self);
-}
-
-static void
-get_managed_objects_cb (GDBusProxy *proxy,
- GAsyncResult *res,
- NMBluez5Manager *self)
-{
- gs_unref_variant GVariant *variant0 = NULL;
- GVariant *variant, *ifaces;
- GVariantIter i;
- GError *error = NULL;
- const char *path;
-
- variant = _nm_dbus_proxy_call_finish (proxy, res,
- G_VARIANT_TYPE ("(a{oa{sa{sv}}})"),
- &error);
- if (!variant) {
- if (g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD))
- _LOGW ("Couldn't get managed objects: not running Bluez5?");
- else {
- g_dbus_error_strip_remote_error (error);
- _LOGW ("Couldn't get managed objects: %s", error->message);
- }
- g_clear_error (&error);
- return;
- }
- variant0 = g_variant_get_child_value (variant, 0);
- g_variant_iter_init (&i, variant0);
- while ((g_variant_iter_next (&i, "{&o*}", &path, &ifaces))) {
- object_manager_interfaces_added (proxy, path, ifaces, self);
- g_variant_unref (ifaces);
- }
-
- g_variant_unref (variant);
-}
-
-static void name_owner_changed_cb (GObject *object, GParamSpec *pspec, gpointer user_data);
-
-static void
-on_proxy_acquired (GObject *object,
- GAsyncResult *res,
- NMBluez5Manager *self)
-{
- NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
- GError *error = NULL;
-
- priv->proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
-
- if (!priv->proxy) {
- _LOGW ("Couldn't acquire object manager proxy: %s", error->message);
- g_clear_error (&error);
- return;
- }
-
- g_signal_connect (priv->proxy, "notify::g-name-owner",
- G_CALLBACK (name_owner_changed_cb), 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);
-
- _nm_dbus_signal_connect (priv->proxy, "InterfacesAdded", G_VARIANT_TYPE ("(oa{sa{sv}})"),
- G_CALLBACK (object_manager_interfaces_added), self);
- _nm_dbus_signal_connect (priv->proxy, "InterfacesRemoved", G_VARIANT_TYPE ("(oas)"),
- G_CALLBACK (object_manager_interfaces_removed), self);
-}
-
-static void
-bluez_connect (NMBluez5Manager *self)
-{
- NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
-
- g_return_if_fail (priv->proxy == NULL);
-
- g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
- G_DBUS_PROXY_FLAGS_NONE,
- NULL,
- NM_BLUEZ_SERVICE,
- NM_BLUEZ_MANAGER_PATH,
- DBUS_INTERFACE_OBJECT_MANAGER,
- NULL,
- (GAsyncReadyCallback) on_proxy_acquired,
- self);
-}
-
-static void
-name_owner_changed_cb (GObject *object,
- GParamSpec *pspec,
- gpointer user_data)
-{
- NMBluez5Manager *self = NM_BLUEZ5_MANAGER (user_data);
- NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
- char *owner;
-
- if (priv->devices) {
- owner = g_dbus_proxy_get_name_owner (priv->proxy);
- if (!owner)
- remove_all_devices (self);
- g_free (owner);
- }
-}
-
-/*****************************************************************************/
-
-static void
-nm_bluez5_manager_init (NMBluez5Manager *self)
-{
- NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
- NMBtVTableNetworkServer *network_server_vtable = NM_BLUEZ5_MANAGER_GET_NETWORK_SERVER_VTABLE (self);
-
- bluez_connect (self);
-
- priv->devices = g_hash_table_new_full (nm_str_hash, g_str_equal,
- NULL, g_object_unref);
-
- c_list_init (&priv->network_servers);
-
- nm_assert (!nm_bt_vtable_network_server);
- network_server_vtable->is_available = network_server_is_available;
- network_server_vtable->register_bridge = network_server_register_bridge;
- network_server_vtable->unregister_bridge = network_server_unregister_bridge;
- nm_bt_vtable_network_server = network_server_vtable;
-}
-
-NMBluez5Manager *
-nm_bluez5_manager_new (NMSettings *settings)
-{
- NMBluez5Manager *instance = NULL;
-
- g_return_val_if_fail (NM_IS_SETTINGS (settings), NULL);
-
- instance = g_object_new (NM_TYPE_BLUEZ5_MANAGER, NULL);
- NM_BLUEZ5_MANAGER_GET_PRIVATE (instance)->settings = g_object_ref (settings);
- return instance;
-}
-
-static void
-dispose (GObject *object)
-{
- NMBluez5Manager *self = NM_BLUEZ5_MANAGER (object);
- NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
- CList *iter, *safe;
-
- c_list_for_each_safe (iter, safe, &priv->network_servers)
- _network_server_free (self, c_list_entry (iter, NetworkServer, lst_ns));
-
- if (priv->proxy) {
- g_signal_handlers_disconnect_by_func (priv->proxy, G_CALLBACK (name_owner_changed_cb), self);
- g_clear_object (&priv->proxy);
- }
-
- g_hash_table_remove_all (priv->devices);
-
- G_OBJECT_CLASS (nm_bluez5_manager_parent_class)->dispose (object);
-}
-
-static void
-finalize (GObject *object)
-{
- NMBluez5Manager *self = NM_BLUEZ5_MANAGER (object);
- NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
-
- g_hash_table_destroy (priv->devices);
-
- G_OBJECT_CLASS (nm_bluez5_manager_parent_class)->finalize (object);
-
- g_object_unref (priv->settings);
-}
-
-static void
-nm_bluez5_manager_class_init (NMBluez5ManagerClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- object_class->dispose = dispose;
- object_class->finalize = finalize;
-
- signals[BDADDR_ADDED] =
- g_signal_new (NM_BLUEZ_MANAGER_BDADDR_ADDED,
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_FIRST,
- 0, NULL, NULL, NULL,
- G_TYPE_NONE, 5, G_TYPE_OBJECT, G_TYPE_STRING,
- G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT);
-
- signals[NETWORK_SERVER_ADDED] =
- g_signal_new (NM_BLUEZ_MANAGER_NETWORK_SERVER_ADDED,
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_FIRST,
- 0, NULL, NULL, NULL,
- G_TYPE_NONE, 0);
-}
diff --git a/src/devices/bluetooth/nm-bluez5-manager.h b/src/devices/bluetooth/nm-bluez5-manager.h
deleted file mode 100644
index a3f81a8ce9..0000000000
--- a/src/devices/bluetooth/nm-bluez5-manager.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/* NetworkManager -- Network link manager
- *
- * Copyright (C) 2007 - 2008 Novell, Inc.
- * Copyright (C) 2007 - 2013 Red Hat, Inc.
- */
-
-#ifndef __NETWORKMANAGER_BLUEZ5_MANAGER_H__
-#define __NETWORKMANAGER_BLUEZ5_MANAGER_H__
-
-#define NM_TYPE_BLUEZ5_MANAGER (nm_bluez5_manager_get_type ())
-#define NM_BLUEZ5_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_BLUEZ5_MANAGER, NMBluez5Manager))
-#define NM_BLUEZ5_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_BLUEZ5_MANAGER, NMBluez5ManagerClass))
-#define NM_IS_BLUEZ5_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_BLUEZ5_MANAGER))
-#define NM_IS_BLUEZ5_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_BLUEZ5_MANAGER))
-#define NM_BLUEZ5_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_BLUEZ5_MANAGER, NMBluez5ManagerClass))
-
-typedef struct _NMBluez5Manager NMBluez5Manager;
-typedef struct _NMBluez5ManagerClass NMBluez5ManagerClass;
-
-GType nm_bluez5_manager_get_type (void);
-
-NMBluez5Manager *nm_bluez5_manager_new (NMSettings *settings);
-
-void nm_bluez5_manager_query_devices (NMBluez5Manager *manager);
-
-#endif /* __NETWORKMANAGER_BLUEZ5_MANAGER_H__ */
diff --git a/src/devices/bluetooth/nm-device-bt.c b/src/devices/bluetooth/nm-device-bt.c
index e904fd44ea..61a7759e11 100644
--- a/src/devices/bluetooth/nm-device-bt.c
+++ b/src/devices/bluetooth/nm-device-bt.c
@@ -10,8 +10,9 @@
#include <stdio.h>
+#include "nm-core-internal.h"
#include "nm-bluez-common.h"
-#include "nm-bluez-device.h"
+#include "nm-bluez-manager.h"
#include "devices/nm-device-private.h"
#include "ppp/nm-ppp-manager.h"
#include "nm-setting-connection.h"
@@ -35,10 +36,12 @@ _LOG_DECLARE_SELF(NMDeviceBt);
/*****************************************************************************/
-NM_GOBJECT_PROPERTIES_DEFINE_BASE (
- PROP_BT_NAME,
+NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceBt,
+ PROP_BT_BDADDR,
+ PROP_BT_BZ_MGR,
PROP_BT_CAPABILITIES,
- PROP_BT_DEVICE,
+ PROP_BT_DBUS_PATH,
+ PROP_BT_NAME,
);
enum {
@@ -51,24 +54,38 @@ static guint signals[LAST_SIGNAL] = { 0 };
typedef struct {
NMModemManager *modem_manager;
- gboolean mm_running;
+ NMBluezManager *bz_mgr;
- NMBluezDevice *bt_device;
+ char *dbus_path;
char *bdaddr;
char *name;
- guint32 capabilities;
- gboolean connected;
- gboolean have_iface;
+ char *connect_rfcomm_iface;
+
+ GSList *connect_modem_candidates;
- char *rfcomm_iface;
NMModem *modem;
- guint timeout_id;
- GCancellable *cancellable;
+ GCancellable *connect_bz_cancellable;
+
+ gulong connect_watch_link_id;
+
+ guint connect_watch_link_idle_id;
+
+ guint connect_wait_modem_id;
+
+ NMBluetoothCapabilities capabilities:6;
+
+ NMBluetoothCapabilities connect_bt_type:6; /* BT type of the current connection */
+
+ NMDeviceStageState stage1_bt_state:3;
+ NMDeviceStageState stage1_modem_prepare_state:3;
+
+ bool is_connected:1;
+
+ bool mm_running:1;
- guint32 bt_type; /* BT type of the current connection */
} NMDeviceBtPrivate;
struct _NMDeviceBt {
@@ -82,42 +99,65 @@ struct _NMDeviceBtClass {
G_DEFINE_TYPE (NMDeviceBt, nm_device_bt, NM_TYPE_DEVICE)
-#define NM_DEVICE_BT_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDeviceBt, NM_IS_DEVICE_BT)
-
-/*****************************************************************************/
-
-static gboolean modem_stage1 (NMDeviceBt *self, NMModem *modem, NMDeviceStateReason *out_failure_reason);
+#define NM_DEVICE_BT_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDeviceBt, NM_IS_DEVICE_BT, NMDevice)
/*****************************************************************************/
-guint32 nm_device_bt_get_capabilities (NMDeviceBt *self)
+NMBluetoothCapabilities nm_device_bt_get_capabilities (NMDeviceBt *self)
{
g_return_val_if_fail (NM_IS_DEVICE_BT (self), NM_BT_CAPABILITY_NONE);
return NM_DEVICE_BT_GET_PRIVATE (self)->capabilities;
}
-static guint32
+static NMBluetoothCapabilities
get_connection_bt_type (NMConnection *connection)
{
NMSettingBluetooth *s_bt;
const char *bt_type;
s_bt = nm_connection_get_setting_bluetooth (connection);
- if (!s_bt)
- return NM_BT_CAPABILITY_NONE;
- bt_type = nm_setting_bluetooth_get_connection_type (s_bt);
- g_assert (bt_type);
-
- if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN))
- return NM_BT_CAPABILITY_DUN;
- else if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU))
- return NM_BT_CAPABILITY_NAP;
+ if (s_bt) {
+ bt_type = nm_setting_bluetooth_get_connection_type (s_bt);
+ if (bt_type) {
+ if (nm_streq (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN))
+ return NM_BT_CAPABILITY_DUN;
+ else if (nm_streq (bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU))
+ return NM_BT_CAPABILITY_NAP;
+ }
+ }
return NM_BT_CAPABILITY_NONE;
}
+static gboolean
+get_connection_bt_type_check (NMDeviceBt *self,
+ NMConnection *connection,
+ NMBluetoothCapabilities *out_bt_type,
+ GError **error)
+{
+ NMBluetoothCapabilities bt_type;
+
+ bt_type = get_connection_bt_type (connection);
+
+ NM_SET_OUT (out_bt_type, bt_type);
+
+ if (bt_type == NM_BT_CAPABILITY_NONE) {
+ nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+ "profile is not a PANU/DUN bluetooth type");
+ return FALSE;
+ }
+
+ if (!NM_FLAGS_ALL (NM_DEVICE_BT_GET_PRIVATE (self)->capabilities, bt_type)) {
+ nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+ "device does not support bluetooth type");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
static NMDeviceCapabilities
get_generic_capabilities (NMDevice *device)
{
@@ -129,17 +169,24 @@ can_auto_connect (NMDevice *device,
NMSettingsConnection *sett_conn,
char **specific_object)
{
- NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device);
- guint32 bt_type;
+ NMDeviceBt *self = NM_DEVICE_BT (device);
+ NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
+ NMBluetoothCapabilities bt_type;
nm_assert (!specific_object || !*specific_object);
if (!NM_DEVICE_CLASS (nm_device_bt_parent_class)->can_auto_connect (device, sett_conn, NULL))
return FALSE;
+ if (!get_connection_bt_type_check (self,
+ nm_settings_connection_get_connection (sett_conn),
+ &bt_type,
+ NULL))
+ return FALSE;
+
/* Can't auto-activate a DUN connection without ModemManager */
- bt_type = get_connection_bt_type (nm_settings_connection_get_connection (sett_conn));
- if (bt_type == NM_BT_CAPABILITY_DUN && priv->mm_running == FALSE)
+ if ( bt_type == NM_BT_CAPABILITY_DUN
+ && priv->mm_running == FALSE)
return FALSE;
return TRUE;
@@ -148,20 +195,16 @@ can_auto_connect (NMDevice *device,
static gboolean
check_connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
{
- NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device);
+ NMDeviceBt *self = NM_DEVICE_BT (device);
+ NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
NMSettingBluetooth *s_bt;
const char *bdaddr;
- guint32 bt_type;
if (!NM_DEVICE_CLASS (nm_device_bt_parent_class)->check_connection_compatible (device, connection, error))
return FALSE;
- bt_type = get_connection_bt_type (connection);
- if (!NM_FLAGS_ALL (priv->capabilities, bt_type)) {
- nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
- "device does not support bluetooth type of profile");
+ if (!get_connection_bt_type_check (self, connection, NULL, error))
return FALSE;
- }
s_bt = nm_connection_get_setting_bluetooth (connection);
@@ -187,18 +230,15 @@ check_connection_available (NMDevice *device,
const char *specific_object,
GError **error)
{
- NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device);
- guint32 bt_type;
+ NMDeviceBt *self = NM_DEVICE_BT (device);
+ NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
+ NMBluetoothCapabilities bt_type;
- bt_type = get_connection_bt_type (connection);
- if (!(bt_type & priv->capabilities)) {
- nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
- "device does not support bluetooth type");
+ if (!get_connection_bt_type_check (self, connection, &bt_type, error))
return FALSE;
- }
- /* DUN connections aren't available without ModemManager */
- if (bt_type == NM_BT_CAPABILITY_DUN && priv->mm_running == FALSE) {
+ if ( bt_type == NM_BT_CAPABILITY_DUN
+ && !priv->mm_running) {
nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
"ModemManager missing for DUN profile");
return FALSE;
@@ -214,11 +254,12 @@ complete_connection (NMDevice *device,
NMConnection *const*existing_connections,
GError **error)
{
- NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device);
+ NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
NMSettingBluetooth *s_bt;
const char *setting_bdaddr;
const char *ctype;
- gboolean is_dun = FALSE, is_pan = FALSE;
+ gboolean is_dun = FALSE;
+ gboolean is_pan = FALSE;
NMSettingGsm *s_gsm;
NMSettingCdma *s_cdma;
NMSettingSerial *s_serial;
@@ -261,7 +302,10 @@ complete_connection (NMDevice *device,
}
/* PAN can't use any DUN-related settings */
- if (s_gsm || s_cdma || s_serial || s_ppp) {
+ if ( s_gsm
+ || s_cdma
+ || s_serial
+ || s_ppp) {
g_set_error_literal (error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_SETTING,
@@ -291,7 +335,8 @@ complete_connection (NMDevice *device,
}
/* Need at least a GSM or a CDMA setting */
- if (!s_gsm && !s_cdma) {
+ if ( !s_gsm
+ && !s_cdma) {
g_set_error_literal (error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_SETTING,
@@ -428,20 +473,19 @@ static void
modem_auth_result (NMModem *modem, GError *error, gpointer user_data)
{
NMDevice *device = NM_DEVICE (user_data);
- NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device);
+ NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
+
+ g_return_if_fail (nm_device_get_state (device) == NM_DEVICE_STATE_NEED_AUTH);
if (error) {
nm_device_state_changed (device,
NM_DEVICE_STATE_FAILED,
NM_DEVICE_STATE_REASON_NO_SECRETS);
- } else {
- NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE;
-
- /* Otherwise, on success for GSM/CDMA secrets we need to schedule modem stage1 again */
- g_return_if_fail (nm_device_get_state (device) == NM_DEVICE_STATE_NEED_AUTH);
- if (!modem_stage1 (NM_DEVICE_BT (device), priv->modem, &failure_reason))
- nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, failure_reason);
+ return;
}
+
+ priv->stage1_modem_prepare_state = NM_DEVICE_STAGE_STATE_INIT;
+ nm_device_activate_schedule_stage1_device_prepare (device);
}
static void
@@ -450,29 +494,33 @@ modem_prepare_result (NMModem *modem,
guint i_reason,
gpointer user_data)
{
- NMDeviceBt *self = NM_DEVICE_BT (user_data);
- NMDevice *device = NM_DEVICE (self);
+ NMDeviceBt *self = user_data;
+ NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
NMDeviceStateReason reason = i_reason;
NMDeviceState state;
- state = nm_device_get_state (device);
- g_return_if_fail (state == NM_DEVICE_STATE_CONFIG || state == NM_DEVICE_STATE_NEED_AUTH);
+ state = nm_device_get_state (NM_DEVICE (self));
- if (success) {
- nm_modem_act_stage2_config (modem);
+ g_return_if_fail (NM_IN_SET (state, NM_DEVICE_STATE_PREPARE,
+ NM_DEVICE_STATE_NEED_AUTH));
- nm_device_activate_schedule_stage3_ip_config_start (device);
- } else {
+ nm_assert (priv->stage1_modem_prepare_state == NM_DEVICE_STAGE_STATE_PENDING);
+
+ if (!success) {
if (nm_device_state_reason_check (reason) == NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT) {
/* If the connect failed because the SIM PIN was wrong don't allow
* the device to be auto-activated anymore, which would risk locking
* the SIM if the incorrect PIN continues to be used.
*/
- nm_device_autoconnect_blocked_set (device, NM_DEVICE_AUTOCONNECT_BLOCKED_WRONG_PIN);
+ nm_device_autoconnect_blocked_set (NM_DEVICE (self), NM_DEVICE_AUTOCONNECT_BLOCKED_WRONG_PIN);
}
- nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason);
+ nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, reason);
+ return;
}
+
+ priv->stage1_modem_prepare_state = NM_DEVICE_STAGE_STATE_COMPLETED;
+ nm_device_activate_schedule_stage1_device_prepare (NM_DEVICE (self));
}
static void
@@ -481,7 +529,7 @@ device_state_changed (NMDevice *device,
NMDeviceState old_state,
NMDeviceStateReason reason)
{
- NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device);
+ NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
if (priv->modem)
nm_modem_device_state_changed (priv->modem, new_state, old_state);
@@ -490,7 +538,8 @@ device_state_changed (NMDevice *device,
* since the device could be both DUN and NAP capable and thus may not
* change state (which rechecks available connections) when MM comes and goes.
*/
- if (priv->mm_running && (priv->capabilities & NM_BT_CAPABILITY_DUN))
+ if ( priv->mm_running
+ && NM_FLAGS_HAS (priv->capabilities, NM_BT_CAPABILITY_DUN))
nm_device_recheck_available_connections (device);
}
@@ -512,8 +561,10 @@ modem_ip4_config_result (NMModem *modem,
nm_device_ip_method_failed (device,
AF_INET,
NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
- } else
- nm_device_activate_schedule_ip_config_result (device, AF_INET, NM_IP_CONFIG_CAST (config));
+ return;
+ }
+
+ nm_device_activate_schedule_ip_config_result (device, AF_INET, NM_IP_CONFIG_CAST (config));
}
static void
@@ -529,29 +580,6 @@ ip_ifindex_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data)
}
}
-static gboolean
-modem_stage1 (NMDeviceBt *self, NMModem *modem, NMDeviceStateReason *out_failure_reason)
-{
- NMActRequest *req;
- NMActStageReturn ret;
-
- req = nm_device_get_act_request (NM_DEVICE (self));
- g_return_val_if_fail (req, FALSE);
-
- ret = nm_modem_act_stage1_prepare (modem, req, out_failure_reason);
- switch (ret) {
- case NM_ACT_STAGE_RETURN_POSTPONE:
- case NM_ACT_STAGE_RETURN_SUCCESS:
- /* Success, wait for the 'prepare-result' signal */
- return TRUE;
- case NM_ACT_STAGE_RETURN_FAILURE:
- default:
- break;
- }
-
- return FALSE;
-}
-
/*****************************************************************************/
static void
@@ -576,11 +604,13 @@ modem_state_cb (NMModem *modem,
NMDevice *device = NM_DEVICE (user_data);
NMDeviceState dev_state = nm_device_get_state (device);
- if (new_state <= NM_MODEM_STATE_DISABLING && old_state > NM_MODEM_STATE_DISABLING) {
+ if ( new_state <= NM_MODEM_STATE_DISABLING
+ && old_state > NM_MODEM_STATE_DISABLING) {
/* Will be called whenever something external to NM disables the
* modem directly through ModemManager.
*/
- if (nm_device_is_activating (device) || dev_state == NM_DEVICE_STATE_ACTIVATED) {
+ if ( nm_device_is_activating (device)
+ || dev_state == NM_DEVICE_STATE_ACTIVATED) {
nm_device_state_changed (device,
NM_DEVICE_STATE_DISCONNECTED,
NM_DEVICE_STATE_REASON_USER_REQUESTED);
@@ -588,13 +618,15 @@ modem_state_cb (NMModem *modem,
}
}
- if (new_state < NM_MODEM_STATE_CONNECTING &&
- old_state >= NM_MODEM_STATE_CONNECTING &&
- dev_state >= NM_DEVICE_STATE_NEED_AUTH &&
- dev_state <= NM_DEVICE_STATE_ACTIVATED) {
+ if ( new_state < NM_MODEM_STATE_CONNECTING
+ && old_state >= NM_MODEM_STATE_CONNECTING
+ && dev_state >= NM_DEVICE_STATE_NEED_AUTH
+ && dev_state <= NM_DEVICE_STATE_ACTIVATED) {
/* Fail the device if the modem disconnects unexpectedly while the
* device is activating/activated. */
- nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER);
+ nm_device_state_changed (device,
+ NM_DEVICE_STATE_FAILED,
+ NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER);
return;
}
}
@@ -605,67 +637,56 @@ modem_removed_cb (NMModem *modem, gpointer user_data)
NMDeviceBt *self = NM_DEVICE_BT (user_data);
NMDeviceState state;
- /* Fail the device if the modem was removed while active */
state = nm_device_get_state (NM_DEVICE (self));
- if ( state == NM_DEVICE_STATE_ACTIVATED
- || nm_device_is_activating (NM_DEVICE (self))) {
+ if ( nm_device_is_activating (NM_DEVICE (self))
+ || state == NM_DEVICE_STATE_ACTIVATED) {
nm_device_state_changed (NM_DEVICE (self),
NM_DEVICE_STATE_FAILED,
NM_DEVICE_STATE_REASON_BT_FAILED);
- } else
- modem_cleanup (self);
+ return;
+ }
+
+ modem_cleanup (self);
}
static gboolean
-component_added (NMDevice *device, GObject *component)
+modem_try_claim (NMDeviceBt *self,
+ NMModem *modem)
{
- NMDeviceBt *self = NM_DEVICE_BT (device);
NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
- NMModem *modem;
+ gs_free char *rfcomm_base_name = NULL;
NMDeviceState state;
- NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE;
- if ( !component
- || !NM_IS_MODEM (component))
+ if (priv->modem) {
+ if (priv->modem == modem)
+ return TRUE;
return FALSE;
-
- modem = NM_MODEM (component);
+ }
if (nm_modem_is_claimed (modem))
return FALSE;
- if (!priv->rfcomm_iface)
+ if (!priv->connect_rfcomm_iface)
return FALSE;
- {
- gs_free char *base = NULL;
-
- base = g_path_get_basename (priv->rfcomm_iface);
- if (!nm_streq (base, nm_modem_get_control_port (modem)))
- return FALSE;
- }
-
- /* Got the modem */
- nm_clear_g_source (&priv->timeout_id);
- nm_clear_g_cancellable (&priv->cancellable);
+ rfcomm_base_name = g_path_get_basename (priv->connect_rfcomm_iface);
+ if (!nm_streq0 (rfcomm_base_name, nm_modem_get_control_port (modem)))
+ return FALSE;
- /* Can only accept the modem in stage2, but since the interface matched
+ /* Can only accept the modem in stage1, but since the interface matched
* what we were expecting, don't let anything else claim the modem either.
*/
state = nm_device_get_state (NM_DEVICE (self));
- if (state != NM_DEVICE_STATE_CONFIG) {
- _LOGW (LOGD_BT | LOGD_MB,
+ if (state != NM_DEVICE_STATE_PREPARE) {
+ _LOGD (LOGD_BT | LOGD_MB,
"modem found but device not in correct state (%d)",
nm_device_get_state (NM_DEVICE (self)));
- return TRUE;
+ return FALSE;
}
- _LOGI (LOGD_BT | LOGD_MB,
- "Activation: (bluetooth) Stage 2 of 5 (Device Configure) modem found.");
-
- modem_cleanup (self);
-
priv->modem = nm_modem_claim (modem);
+ priv->stage1_modem_prepare_state = NM_DEVICE_STAGE_STATE_INIT;
+
g_signal_connect (modem, NM_MODEM_PPP_STATS, G_CALLBACK (ppp_stats), self);
g_signal_connect (modem, NM_MODEM_PPP_FAILED, G_CALLBACK (ppp_failed), self);
g_signal_connect (modem, NM_MODEM_PREPARE_RESULT, G_CALLBACK (modem_prepare_result), self);
@@ -674,92 +695,187 @@ component_added (NMDevice *device, GObject *component)
g_signal_connect (modem, NM_MODEM_AUTH_RESULT, G_CALLBACK (modem_auth_result), self);
g_signal_connect (modem, NM_MODEM_STATE_CHANGED, G_CALLBACK (modem_state_cb), self);
g_signal_connect (modem, NM_MODEM_REMOVED, G_CALLBACK (modem_removed_cb), self);
-
g_signal_connect (modem, "notify::" NM_MODEM_IP_IFINDEX, G_CALLBACK (ip_ifindex_changed_cb), self);
- /* Kick off the modem connection */
- if (!modem_stage1 (self, modem, &failure_reason))
- nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, failure_reason);
+ _LOGD (LOGD_BT | LOGD_MB,
+ "modem found");
return TRUE;
}
-static gboolean
-modem_find_timeout (gpointer user_data)
+static void
+mm_modem_added_cb (NMModemManager *manager,
+ NMModem *modem,
+ gpointer user_data)
+{
+ NMDeviceBt *self = user_data;
+ NMDeviceBtPrivate *priv;
+
+ if (!modem_try_claim (user_data, modem))
+ return;
+
+ priv = NM_DEVICE_BT_GET_PRIVATE (self);
+
+ if (priv->stage1_bt_state == NM_DEVICE_STAGE_STATE_COMPLETED)
+ nm_device_activate_schedule_stage1_device_prepare (NM_DEVICE (self));
+}
+
+/*****************************************************************************/
+
+void
+_nm_device_bt_notify_set_connected (NMDeviceBt *self,
+ gboolean connected)
{
- NMDeviceBt *self = NM_DEVICE_BT (user_data);
NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
- priv->timeout_id = 0;
- nm_clear_g_cancellable (&priv->cancellable);
+ connected = !!connected;
+ if (priv->is_connected == connected)
+ return;
+
+ priv->is_connected = connected;
+
+ if ( connected
+ || priv->stage1_bt_state != NM_DEVICE_STAGE_STATE_COMPLETED
+ || nm_device_get_state (NM_DEVICE (self)) > NM_DEVICE_STATE_ACTIVATED) {
+ _LOGT (LOGD_BT, "set-connected: %d", connected);
+ return;
+ }
+ _LOGT (LOGD_BT, "set-connected: %d (disconnecting device...)", connected);
nm_device_state_changed (NM_DEVICE (self),
NM_DEVICE_STATE_FAILED,
- NM_DEVICE_STATE_REASON_MODEM_NOT_FOUND);
- return FALSE;
+ NM_DEVICE_STATE_REASON_CARRIER);
}
-static void
-check_connect_continue (NMDeviceBt *self)
+static gboolean
+connect_watch_link_idle_cb (gpointer user_data)
{
- NMDevice *device = NM_DEVICE (self);
+ NMDeviceBt *self = user_data;
NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
- gboolean pan = (priv->bt_type == NM_BT_CAPABILITY_NAP);
- gboolean dun = (priv->bt_type == NM_BT_CAPABILITY_DUN);
+ int ifindex;
- if (!priv->connected || !priv->have_iface)
- return;
+ priv->connect_watch_link_idle_id = 0;
- _LOGI (LOGD_BT,
- "Activation: (bluetooth) Stage 2 of 5 (Device Configure) successful. Will connect via %s.",
- dun ? "DUN" : (pan ? "PAN" : "unknown"));
+ if (nm_device_get_state (NM_DEVICE (self)) <= NM_DEVICE_STATE_ACTIVATED) {
+ ifindex = nm_device_get_ip_ifindex (NM_DEVICE (self));
+ if ( ifindex > 0
+ && !nm_platform_link_get (nm_device_get_platform (NM_DEVICE (self)), ifindex)) {
+ _LOGT (LOGD_BT, "device disappeared");
+ nm_device_state_changed (NM_DEVICE (self),
+ NM_DEVICE_STATE_FAILED,
+ NM_DEVICE_STATE_REASON_BT_FAILED);
+ }
+ }
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+connect_watch_link_cb (NMPlatform *platform,
+ int obj_type_i,
+ int ifindex,
+ NMPlatformLink *info,
+ int change_type_i,
+ NMDevice *self)
+{
+ const NMPlatformSignalChangeType change_type = change_type_i;
+ NMDeviceBtPrivate *priv;
+
+ /* bluez doesn't notify us when the connection disconnects.
+ * Neither does NMManager (or NMDevice) tell us when the ip-ifindex goes away.
+ * This is horrible, and should be improved. For now, watch the link ourself... */
+
+ if (NM_IN_SET (change_type, NM_PLATFORM_SIGNAL_CHANGED,
+ NM_PLATFORM_SIGNAL_REMOVED)) {
+ priv = NM_DEVICE_BT_GET_PRIVATE (self);
+ if (priv->connect_watch_link_idle_id == 0)
+ priv->connect_watch_link_idle_id = g_idle_add (connect_watch_link_idle_cb, self);
+ }
+}
+
+static gboolean
+connect_wait_modem_timeout (gpointer user_data)
+{
+ NMDeviceBt *self = NM_DEVICE_BT (user_data);
+ NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
- nm_clear_g_source (&priv->timeout_id);
- nm_clear_g_cancellable (&priv->cancellable);
+ /* since this timeout is longer than the connect timeout, we must have already
+ * hit the connect-timeout first or being connected. */
+ nm_assert (priv->stage1_bt_state == NM_DEVICE_STAGE_STATE_COMPLETED);
- if (pan) {
- /* Bluez says we're connected now. Start IP config. */
- nm_device_activate_schedule_stage3_ip_config_start (device);
- } else if (dun) {
- /* Wait for ModemManager to find the modem */
- priv->timeout_id = g_timeout_add_seconds (30, modem_find_timeout, self);
+ priv->connect_wait_modem_id = 0;
+ nm_clear_g_cancellable (&priv->connect_bz_cancellable);
- _LOGI (LOGD_BT | LOGD_MB,
- "Activation: (bluetooth) Stage 2 of 5 (Device Configure) waiting for modem to appear.");
- } else
- g_assert_not_reached ();
+ if (priv->modem)
+ _LOGD (LOGD_BT, "timeout connecting modem for DUN connection");
+ else
+ _LOGD (LOGD_BT, "timeout finding modem for DUN connection");
+
+ nm_device_state_changed (NM_DEVICE (self),
+ NM_DEVICE_STATE_FAILED,
+ NM_DEVICE_STATE_REASON_MODEM_NOT_FOUND);
+ return G_SOURCE_REMOVE;
}
static void
-bluez_connect_cb (NMBluezDevice *bt_device,
- const char *device_name,
- GError *error,
- gpointer user_data)
+connect_bz_cb (NMBluezManager *bz_mgr,
+ gboolean is_complete,
+ const char *device_name,
+ GError *error,
+ gpointer user_data)
{
- gs_unref_object NMDeviceBt *self = user_data;
- NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
+ NMDeviceBt *self;
+ NMDeviceBtPrivate *priv;
+ char sbuf[100];
if (nm_utils_error_is_cancelled (error, FALSE))
return;
- nm_clear_g_source (&priv->timeout_id);
- g_clear_object (&priv->cancellable);
+ self = user_data;
+ priv = NM_DEVICE_BT_GET_PRIVATE (self);
+
+ nm_assert (nm_device_is_activating (NM_DEVICE (self)));
+ nm_assert (NM_IN_SET ((NMBluetoothCapabilities) priv->connect_bt_type, NM_BT_CAPABILITY_DUN,
+ NM_BT_CAPABILITY_NAP));
+
+ if (!is_complete) {
+ nm_assert (priv->connect_bt_type == NM_BT_CAPABILITY_DUN);
+ nm_assert (device_name);
+ nm_assert (!error);
- if (!nm_device_is_activating (NM_DEVICE (self)))
+ if (!nm_streq0 (priv->connect_rfcomm_iface, device_name)) {
+ nm_assert (!priv->connect_rfcomm_iface);
+ _LOGD (LOGD_BT, "DUN is still connecting but got serial port \"%s\" to claim modem", device_name);
+ g_free (priv->connect_rfcomm_iface);
+ priv->connect_rfcomm_iface = g_strdup (device_name);
+ }
return;
+ }
+
+ g_clear_object (&priv->connect_bz_cancellable);
if (!device_name) {
- _LOGW (LOGD_BT, "Error connecting with bluez: %s", error->message);
+ _LOGW (LOGD_BT, "%s connect request failed: %s",
+ nm_bluetooth_capability_to_string (priv->connect_bt_type, sbuf, sizeof (sbuf)),
+ error->message);
nm_device_state_changed (NM_DEVICE (self),
NM_DEVICE_STATE_FAILED,
NM_DEVICE_STATE_REASON_BT_FAILED);
return;
}
- if (priv->bt_type == NM_BT_CAPABILITY_DUN) {
- g_free (priv->rfcomm_iface);
- priv->rfcomm_iface = g_strdup (device_name);
- } else if (priv->bt_type == NM_BT_CAPABILITY_NAP) {
+ _LOGD (LOGD_BT, "%s connect request successful (%s)",
+ nm_bluetooth_capability_to_string (priv->connect_bt_type, sbuf, sizeof (sbuf)),
+ device_name);
+
+ if (priv->connect_bt_type == NM_BT_CAPABILITY_DUN) {
+ if (!nm_streq0 (priv->connect_rfcomm_iface, device_name)) {
+ nm_assert_not_reached ();
+ g_free (priv->connect_rfcomm_iface);
+ priv->connect_rfcomm_iface = g_strdup (device_name);
+ }
+ } else {
+ nm_assert (priv->connect_bt_type == NM_BT_CAPABILITY_NAP);
if (!nm_device_set_ip_iface (NM_DEVICE (self), device_name)) {
_LOGW (LOGD_BT, "Error connecting with bluez: cannot find device %s", device_name);
nm_device_state_changed (NM_DEVICE (self),
@@ -767,107 +883,121 @@ bluez_connect_cb (NMBluezDevice *bt_device,
NM_DEVICE_STATE_REASON_BT_FAILED);
return;
}
+ priv->connect_watch_link_id = g_signal_connect (nm_device_get_platform (NM_DEVICE (self)),
+ NM_PLATFORM_SIGNAL_LINK_CHANGED,
+ G_CALLBACK (connect_watch_link_cb),
+ self);
}
- _LOGD (LOGD_BT, "connect request successful");
-
- /* Stage 3 gets scheduled when Bluez says we're connected */
- priv->have_iface = TRUE;
- check_connect_continue (self);
-}
-
-static void
-bluez_connected_changed (NMBluezDevice *bt_device,
- GParamSpec *pspec,
- NMDevice *device)
-{
- NMDeviceBt *self = NM_DEVICE_BT (device);
- NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
- gboolean connected;
- NMDeviceState state;
-
- state = nm_device_get_state (device);
- connected = nm_bluez_device_get_connected (bt_device);
- if (connected) {
- if (state == NM_DEVICE_STATE_CONFIG) {
- _LOGD (LOGD_BT, "connected to the device");
-
- priv->connected = TRUE;
- check_connect_continue (self);
- }
- } else {
- gboolean fail = FALSE;
-
- /* Bluez says we're disconnected from the device. Suck. */
-
- if (nm_device_is_activating (device)) {
- _LOGI (LOGD_BT, "Activation: (bluetooth) bluetooth link disconnected.");
- fail = TRUE;
- } else if (state == NM_DEVICE_STATE_ACTIVATED) {
- _LOGI (LOGD_BT, "bluetooth link disconnected.");
- fail = TRUE;
- }
-
- if (fail) {
- nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_CARRIER);
- priv->connected = FALSE;
- }
+ if (!priv->is_connected) {
+ /* we got the callback from NMBluezManager with succes. We actually should be
+ * connected and this line shouldn't be reached. */
+ nm_assert_not_reached ();
+ _LOGE (LOGD_BT, "bluetooth is unexpectedly not in connected state");
+ nm_device_state_changed (NM_DEVICE (self),
+ NM_DEVICE_STATE_FAILED,
+ NM_DEVICE_STATE_REASON_BT_FAILED);
+ return;
}
-}
-static gboolean
-bt_connect_timeout (gpointer user_data)
-{
- NMDeviceBt *self = NM_DEVICE_BT (user_data);
- NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
-
- _LOGD (LOGD_BT, "initial connection timed out");
-
- priv->timeout_id = 0;
- nm_clear_g_cancellable (&priv->cancellable);
-
- nm_device_state_changed (NM_DEVICE (self),
- NM_DEVICE_STATE_FAILED,
- NM_DEVICE_STATE_REASON_BT_FAILED);
- return FALSE;
+ priv->stage1_bt_state = NM_DEVICE_STAGE_STATE_COMPLETED;
+ nm_device_activate_schedule_stage1_device_prepare (NM_DEVICE (self));
}
static NMActStageReturn
-act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
+act_stage1_prepare (NMDevice *device,
+ NMDeviceStateReason *out_failure_reason)
{
NMDeviceBt *self = NM_DEVICE_BT (device);
NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
+ gs_free_error GError *error = NULL;
NMConnection *connection;
connection = nm_device_get_applied_connection (device);
g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE);
- priv->bt_type = get_connection_bt_type (connection);
- if (priv->bt_type == NM_BT_CAPABILITY_NONE) {
- // FIXME: set a reason code
+ priv->connect_bt_type = get_connection_bt_type (connection);
+ if (priv->connect_bt_type == NM_BT_CAPABILITY_NONE) {
+ NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_BT_FAILED);
return NM_ACT_STAGE_RETURN_FAILURE;
}
- if (priv->bt_type == NM_BT_CAPABILITY_DUN && !priv->mm_running) {
+ if ( priv->connect_bt_type == NM_BT_CAPABILITY_DUN
+ && !priv->mm_running) {
NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE);
return NM_ACT_STAGE_RETURN_FAILURE;
}
- _LOGD (LOGD_BT, "requesting connection to the device");
+ if (priv->stage1_bt_state == NM_DEVICE_STAGE_STATE_PENDING)
+ return NM_ACT_STAGE_RETURN_POSTPONE;
+ else if (priv->stage1_bt_state == NM_DEVICE_STAGE_STATE_INIT) {
+ gs_unref_object GCancellable *cancellable = NULL;
+ char sbuf[100];
+
+ _LOGD (LOGD_BT, "connecting to %s bluetooth device",
+ nm_bluetooth_capability_to_string (priv->connect_bt_type, sbuf, sizeof (sbuf)));
+
+ cancellable = g_cancellable_new ();
+
+ if (!nm_bluez_manager_connect (priv->bz_mgr,
+ priv->dbus_path,
+ priv->connect_bt_type,
+ 30000,
+ cancellable,
+ connect_bz_cb,
+ self,
+ &error)) {
+ _LOGD (LOGD_BT, "cannot connect to bluetooth device: %s", error->message);
+ *out_failure_reason = NM_DEVICE_STATE_REASON_BT_FAILED;
+ return NM_ACT_STAGE_RETURN_FAILURE;
+ }
+
+ priv->connect_bz_cancellable = g_steal_pointer (&cancellable);
+ priv->stage1_bt_state = NM_DEVICE_STAGE_STATE_PENDING;
+ return NM_ACT_STAGE_RETURN_POSTPONE;
+ }
+
+ if (priv->connect_bt_type == NM_BT_CAPABILITY_DUN) {
+ if (!priv->modem) {
+ gs_free NMModem **modems = NULL;
+ guint i, n;
+
+ if (priv->connect_wait_modem_id == 0)
+ priv->connect_wait_modem_id = g_timeout_add_seconds (30, connect_wait_modem_timeout, self);
+
+ modems = nm_modem_manager_get_modems (priv->modem_manager, &n);
+ for (i = 0; i < n; i++) {
+ if (modem_try_claim (self, modems[i]))
+ break;
+ }
+ if (!priv->modem)
+ return NM_ACT_STAGE_RETURN_POSTPONE;
+ }
+
+ if (priv->stage1_modem_prepare_state == NM_DEVICE_STAGE_STATE_PENDING)
+ return NM_ACT_STAGE_RETURN_POSTPONE;
+ if (priv->stage1_modem_prepare_state == NM_DEVICE_STAGE_STATE_INIT) {
+ priv->stage1_modem_prepare_state = NM_DEVICE_STAGE_STATE_PENDING;
+ return nm_modem_act_stage1_prepare (priv->modem,
+ nm_device_get_act_request (NM_DEVICE (self)),
+ out_failure_reason);
+ }
+ }
- nm_clear_g_source (&priv->timeout_id);
- nm_clear_g_cancellable (&priv->cancellable);
+ return NM_ACT_STAGE_RETURN_SUCCESS;
+}
- priv->timeout_id = g_timeout_add_seconds (30, bt_connect_timeout, device);
- priv->cancellable = g_cancellable_new ();
+static NMActStageReturn
+act_stage2_config (NMDevice *device,
+ NMDeviceStateReason *out_failure_reason)
+{
+ NMDeviceBt *self = NM_DEVICE_BT (device);
+ NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
- nm_bluez_device_connect_async (priv->bt_device,
- priv->bt_type & (NM_BT_CAPABILITY_DUN | NM_BT_CAPABILITY_NAP),
- priv->cancellable,
- bluez_connect_cb,
- g_object_ref (self));
+ if (priv->connect_bt_type == NM_BT_CAPABILITY_DUN)
+ nm_modem_act_stage2_config (priv->modem);
- return NM_ACT_STAGE_RETURN_POSTPONE;
+ return NM_ACT_STAGE_RETURN_SUCCESS;
}
static NMActStageReturn
@@ -876,11 +1006,11 @@ act_stage3_ip_config_start (NMDevice *device,
gpointer *out_config,
NMDeviceStateReason *out_failure_reason)
{
- NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device);
+ NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
nm_assert_addr_family (addr_family);
- if (priv->bt_type == NM_BT_CAPABILITY_DUN) {
+ if (priv->connect_bt_type == NM_BT_CAPABILITY_DUN) {
if (addr_family == AF_INET) {
return nm_modem_stage3_ip4_config_start (priv->modem,
device,
@@ -899,15 +1029,17 @@ act_stage3_ip_config_start (NMDevice *device,
static void
deactivate (NMDevice *device)
{
- NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device);
+ NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device);
- priv->have_iface = FALSE;
- priv->connected = FALSE;
+ nm_clear_g_signal_handler (nm_device_get_platform (device), &priv->connect_watch_link_id);
+ nm_clear_g_source (&priv->connect_watch_link_idle_id);
+ priv->stage1_bt_state = NM_DEVICE_STAGE_STATE_INIT;
+ nm_clear_g_source (&priv->connect_wait_modem_id);
+ nm_clear_g_cancellable (&priv->connect_bz_cancellable);
- nm_clear_g_source (&priv->timeout_id);
- nm_clear_g_cancellable (&priv->cancellable);
+ priv->stage1_bt_state = NM_DEVICE_STAGE_STATE_INIT;
- if (priv->bt_type == NM_BT_CAPABILITY_DUN) {
+ if (priv->connect_bt_type == NM_BT_CAPABILITY_DUN) {
if (priv->modem) {
nm_modem_deactivate (priv->modem, device);
@@ -921,22 +1053,53 @@ deactivate (NMDevice *device)
}
}
- if (priv->bt_type != NM_BT_CAPABILITY_NONE)
- nm_bluez_device_disconnect (priv->bt_device);
-
- priv->bt_type = NM_BT_CAPABILITY_NONE;
+ if (priv->connect_bt_type != NM_BT_CAPABILITY_NONE) {
+ priv->connect_bt_type = NM_BT_CAPABILITY_NONE;
+ nm_bluez_manager_disconnect (priv->bz_mgr, priv->dbus_path);
+ }
- g_free (priv->rfcomm_iface);
- priv->rfcomm_iface = NULL;
+ nm_clear_g_free (&priv->connect_rfcomm_iface);
if (NM_DEVICE_CLASS (nm_device_bt_parent_class)->deactivate)
NM_DEVICE_CLASS (nm_device_bt_parent_class)->deactivate (device);
}
-static void
-bluez_device_removed (NMBluezDevice *bdev, gpointer user_data)
+void
+_nm_device_bt_notify_removed (NMDeviceBt *self)
{
- g_signal_emit_by_name (NM_DEVICE_BT (user_data), NM_DEVICE_REMOVED);
+ g_signal_emit_by_name (self, NM_DEVICE_REMOVED);
+}
+
+/*****************************************************************************/
+
+gboolean
+_nm_device_bt_for_same_device (NMDeviceBt *self,
+ const char *dbus_path,
+ const char *bdaddr,
+ const char *name,
+ NMBluetoothCapabilities capabilities)
+{
+ NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
+
+ return nm_streq (priv->dbus_path, dbus_path)
+ && nm_streq (priv->bdaddr, bdaddr)
+ && capabilities == priv->capabilities
+ && (!name || nm_streq (priv->name, name));
+}
+
+void
+_nm_device_bt_notify_set_name (NMDeviceBt *self, const char *name)
+{
+ NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
+
+ nm_assert (name);
+
+ if (!nm_streq (priv->name, name)) {
+ _LOGT (LOGD_BT, "set-name: %s", name);
+ g_free (priv->name);
+ priv->name = g_strdup (name);
+ _notify (self, PROP_BT_NAME);
+ }
}
/*****************************************************************************/
@@ -997,9 +1160,6 @@ 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;
@@ -1013,19 +1173,32 @@ set_property (GObject *object, guint prop_id,
NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) object);
switch (prop_id) {
+ case PROP_BT_BZ_MGR:
+ /* construct-only */
+ priv->bz_mgr = g_object_ref (g_value_get_pointer (value));
+ nm_assert (NM_IS_BLUEZ_MANAGER (priv->bz_mgr));
+ break;
+ case PROP_BT_DBUS_PATH:
+ /* construct-only */
+ priv->dbus_path = g_value_dup_string (value);
+ nm_assert (priv->dbus_path);
+ break;
+ case PROP_BT_BDADDR:
+ /* construct-only */
+ priv->bdaddr = g_value_dup_string (value);
+ nm_assert (priv->bdaddr);
+ break;
case PROP_BT_NAME:
/* construct-only */
priv->name = g_value_dup_string (value);
+ nm_assert (priv->name);
break;
case PROP_BT_CAPABILITIES:
/* construct-only */
priv->capabilities = g_value_get_uint (value);
- break;
- case PROP_BT_DEVICE:
- /* construct-only */
- priv->bt_device = g_value_dup_object (value);
- if (!priv->bt_device)
- g_return_if_reached ();
+ nm_assert (NM_IN_SET ((NMBluetoothCapabilities) priv->capabilities, NM_BT_CAPABILITY_DUN,
+ NM_BT_CAPABILITY_NAP,
+ NM_BT_CAPABILITY_DUN | NM_BT_CAPABILITY_NAP));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -1045,7 +1218,6 @@ constructed (GObject *object)
{
NMDeviceBt *self = NM_DEVICE_BT (object);
NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
- const char *my_hwaddr;
G_OBJECT_CLASS (nm_device_bt_parent_class)->constructed (object);
@@ -1054,74 +1226,69 @@ constructed (GObject *object)
nm_modem_manager_name_owner_ref (priv->modem_manager);
g_signal_connect (priv->modem_manager,
+ NM_MODEM_MANAGER_MODEM_ADDED,
+ G_CALLBACK (mm_modem_added_cb),
+ self);
+
+ g_signal_connect (priv->modem_manager,
"notify::"NM_MODEM_MANAGER_NAME_OWNER,
G_CALLBACK (mm_name_owner_changed_cb),
self);
- if (priv->bt_device) {
- /* Watch for BT device property changes */
- g_signal_connect (priv->bt_device, "notify::" NM_BLUEZ_DEVICE_CONNECTED,
- G_CALLBACK (bluez_connected_changed),
- object);
- g_signal_connect (priv->bt_device, NM_BLUEZ_DEVICE_REMOVED,
- G_CALLBACK (bluez_device_removed), object);
- }
-
- my_hwaddr = nm_device_get_hw_address (NM_DEVICE (object));
- if (my_hwaddr)
- priv->bdaddr = g_strdup (my_hwaddr);
- else
- g_warn_if_reached ();
-
set_mm_running (self);
}
-NMDevice *
-nm_device_bt_new (NMBluezDevice *bt_device,
- const char *udi,
+NMDeviceBt *
+nm_device_bt_new (NMBluezManager *bz_mgr,
+ const char *dbus_path,
const char *bdaddr,
const char *name,
- guint32 capabilities)
+ NMBluetoothCapabilities capabilities)
{
- g_return_val_if_fail (udi != NULL, NULL);
- g_return_val_if_fail (bdaddr != NULL, NULL);
- g_return_val_if_fail (name != NULL, NULL);
+ g_return_val_if_fail (NM_IS_BLUEZ_MANAGER (bz_mgr), NULL);
+ g_return_val_if_fail (dbus_path, NULL);
+ g_return_val_if_fail (bdaddr, NULL);
+ g_return_val_if_fail (name, 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_PERM_HW_ADDRESS, bdaddr,
- NM_DEVICE_BT_DEVICE, bt_device,
- NM_DEVICE_BT_NAME, name,
- NM_DEVICE_BT_CAPABILITIES, capabilities,
- NM_DEVICE_TYPE_DESC, "Bluetooth",
- NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_BT,
- NULL);
+
+ return g_object_new (NM_TYPE_DEVICE_BT,
+ NM_DEVICE_UDI, dbus_path,
+ NM_DEVICE_IFACE, bdaddr,
+ NM_DEVICE_DRIVER, "bluez",
+ NM_DEVICE_PERM_HW_ADDRESS, bdaddr,
+ NM_DEVICE_BT_BDADDR, bdaddr,
+ NM_DEVICE_BT_BZ_MGR, bz_mgr,
+ NM_DEVICE_BT_CAPABILITIES, (guint) capabilities,
+ NM_DEVICE_BT_DBUS_PATH, dbus_path,
+ NM_DEVICE_BT_NAME, name,
+ NM_DEVICE_TYPE_DESC, "Bluetooth",
+ NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_BT,
+ NULL);
}
static void
dispose (GObject *object)
{
- NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) object);
+ NMDeviceBt *self = NM_DEVICE_BT (object);
+ NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
- nm_clear_g_source (&priv->timeout_id);
- nm_clear_g_cancellable (&priv->cancellable);
+ nm_clear_g_signal_handler (nm_device_get_platform (NM_DEVICE (self)), &priv->connect_watch_link_id);
+ nm_clear_g_source (&priv->connect_watch_link_idle_id);
- g_signal_handlers_disconnect_matched (priv->bt_device, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, object);
+ nm_clear_g_source (&priv->connect_wait_modem_id);
+ nm_clear_g_cancellable (&priv->connect_bz_cancellable);
if (priv->modem_manager) {
- g_signal_handlers_disconnect_by_func (priv->modem_manager, G_CALLBACK (mm_name_owner_changed_cb), object);
+ g_signal_handlers_disconnect_by_func (priv->modem_manager, G_CALLBACK (mm_name_owner_changed_cb), self);
nm_modem_manager_name_owner_unref (priv->modem_manager);
g_clear_object (&priv->modem_manager);
}
- modem_cleanup (NM_DEVICE_BT (object));
- g_clear_object (&priv->bt_device);
+ modem_cleanup (self);
G_OBJECT_CLASS (nm_device_bt_parent_class)->dispose (object);
+
+ g_clear_object (&priv->bz_mgr);
}
static void
@@ -1129,7 +1296,8 @@ finalize (GObject *object)
{
NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) object);
- g_free (priv->rfcomm_iface);
+ g_free (priv->connect_rfcomm_iface);
+ g_free (priv->dbus_path);
g_free (priv->name);
g_free (priv->bdaddr);
@@ -1171,17 +1339,34 @@ nm_device_bt_class_init (NMDeviceBtClass *klass)
device_class->get_generic_capabilities = get_generic_capabilities;
device_class->can_auto_connect = can_auto_connect;
device_class->deactivate = deactivate;
+ device_class->act_stage1_prepare = act_stage1_prepare;
device_class->act_stage2_config = act_stage2_config;
device_class->act_stage3_ip_config_start = act_stage3_ip_config_start;
device_class->check_connection_compatible = check_connection_compatible;
device_class->check_connection_available = check_connection_available;
device_class->complete_connection = complete_connection;
device_class->is_available = is_available;
- device_class->component_added = component_added;
device_class->get_configured_mtu = nm_modem_get_configured_mtu;
device_class->state_changed = device_state_changed;
+ obj_properties[PROP_BT_BZ_MGR] =
+ g_param_spec_pointer (NM_DEVICE_BT_BZ_MGR, "", "",
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ obj_properties[PROP_BT_BDADDR] =
+ g_param_spec_string (NM_DEVICE_BT_BDADDR, "", "",
+ NULL,
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ obj_properties[PROP_BT_DBUS_PATH] =
+ g_param_spec_string (NM_DEVICE_BT_DBUS_PATH, "", "",
+ NULL,
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
obj_properties[PROP_BT_NAME] =
g_param_spec_string (NM_DEVICE_BT_NAME, "", "",
NULL,
@@ -1194,12 +1379,6 @@ nm_device_bt_class_init (NMDeviceBtClass *klass)
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
- obj_properties[PROP_BT_DEVICE] =
- g_param_spec_object (NM_DEVICE_BT_DEVICE, "", "",
- NM_TYPE_BLUEZ_DEVICE,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_STATIC_STRINGS);
-
g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
signals[PPP_STATS] =
diff --git a/src/devices/bluetooth/nm-device-bt.h b/src/devices/bluetooth/nm-device-bt.h
index 2a0eb1de2c..d889aefefb 100644
--- a/src/devices/bluetooth/nm-device-bt.h
+++ b/src/devices/bluetooth/nm-device-bt.h
@@ -8,7 +8,6 @@
#define __NETWORKMANAGER_DEVICE_BT_H__
#include "devices/nm-device.h"
-#include "nm-bluez-device.h"
#define NM_TYPE_DEVICE_BT (nm_device_bt_get_type ())
#define NM_DEVICE_BT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_BT, NMDeviceBt))
@@ -17,9 +16,11 @@
#define NM_IS_DEVICE_BT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_BT))
#define NM_DEVICE_BT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_BT, NMDeviceBtClass))
-#define NM_DEVICE_BT_NAME "name"
+#define NM_DEVICE_BT_BDADDR "bt-bdaddr"
+#define NM_DEVICE_BT_BZ_MGR "bt-bz-mgr"
#define NM_DEVICE_BT_CAPABILITIES "bt-capabilities"
-#define NM_DEVICE_BT_DEVICE "bt-device"
+#define NM_DEVICE_BT_DBUS_PATH "bt-dbus-path"
+#define NM_DEVICE_BT_NAME "bt-name"
#define NM_DEVICE_BT_PPP_STATS "ppp-stats"
@@ -28,13 +29,21 @@ typedef struct _NMDeviceBtClass NMDeviceBtClass;
GType nm_device_bt_get_type (void);
-NMDevice *nm_device_bt_new (NMBluezDevice *bt_device,
- const char *udi,
- const char *bdaddr,
- const char *name,
- guint32 capabilities);
+struct _NMBluezManager;
-guint32 nm_device_bt_get_capabilities (NMDeviceBt *device);
+NMDeviceBt *nm_device_bt_new (struct _NMBluezManager *bz_mgr,
+ const char *dbus_path,
+ const char *bdaddr,
+ const char *name,
+ NMBluetoothCapabilities capabilities);
+
+gboolean _nm_device_bt_for_same_device (NMDeviceBt *device,
+ const char *dbus_path,
+ const char *bdaddr,
+ const char *name,
+ NMBluetoothCapabilities capabilities);
+
+NMBluetoothCapabilities nm_device_bt_get_capabilities (NMDeviceBt *device);
struct _NMModem;
@@ -42,4 +51,11 @@ gboolean nm_device_bt_modem_added (NMDeviceBt *device,
struct _NMModem *modem,
const char *driver);
+void _nm_device_bt_notify_removed (NMDeviceBt *self);
+
+void _nm_device_bt_notify_set_name (NMDeviceBt *self, const char *name);
+
+void _nm_device_bt_notify_set_connected (NMDeviceBt *self,
+ gboolean connected);
+
#endif /* __NETWORKMANAGER_DEVICE_BT_H__ */
diff --git a/src/devices/bluetooth/tests/nm-bt-test.c b/src/devices/bluetooth/tests/nm-bt-test.c
index 42f3f2e553..02cfd2288f 100644
--- a/src/devices/bluetooth/tests/nm-bt-test.c
+++ b/src/devices/bluetooth/tests/nm-bt-test.c
@@ -2,35 +2,219 @@
#include "nm-default.h"
+#include <glib-unix.h>
+
#include "devices/bluetooth/nm-bluez5-dun.h"
#include "nm-test-utils-core.h"
/*****************************************************************************/
+#define _NMLOG_DOMAIN LOGD_BT
+#define _NMLOG(level, ...) \
+ nm_log ((level), _NMLOG_DOMAIN, \
+ NULL, NULL, \
+ "bt%s%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
+ NM_PRINT_FMT_QUOTED (gl.argv_cmd, "[", gl.argv_cmd, "]", "") \
+ _NM_UTILS_MACRO_REST (__VA_ARGS__))
+
+/*****************************************************************************/
+
+struct {
+ int argc;
+ const char *const*argv;
+ const char *argv_cmd;
+ GMainLoop *loop;
+} gl;
+
+typedef struct _MainCmdInfo {
+ const char *name;
+ int (*main_func) (const struct _MainCmdInfo *main_cmd_info);
+} MainCmdInfo;
+
+/*****************************************************************************/
+
+#if WITH_BLUEZ5_DUN
+
+typedef struct {
+ NMBluez5DunContext *dun_context;
+ GCancellable *cancellable;
+ guint timeout_id;
+ guint sig_term_id;
+ guint sig_int_id;
+} DunConnectData;
+
+static void
+_dun_connect_cb (NMBluez5DunContext *context,
+ const char *rfcomm_dev,
+ GError *error,
+ gpointer user_data)
+{
+ DunConnectData *dun_connect_data = user_data;
+
+ g_assert (dun_connect_data);
+ g_assert (!dun_connect_data->dun_context);
+ g_assert ((!!error) != (!!rfcomm_dev));
+
+ if (rfcomm_dev && !context) {
+ _LOGI ("dun-connect notifies path \"%s\". Wait longer...", rfcomm_dev);
+ return;
+ }
+
+ if (rfcomm_dev) {
+ g_assert (context);
+ _LOGI ("dun-connect completed with path \"%s\"", rfcomm_dev);
+ } else {
+ g_assert (!context);
+ _LOGI ("dun-connect failed with error: %s", error->message);
+ }
+
+ dun_connect_data->dun_context = context;
+
+ g_main_loop_quit (gl.loop);
+}
+
+static void
+_dun_notify_tty_hangup_cb (NMBluez5DunContext *context,
+ gpointer user_data)
+{
+ _LOGI ("dun-connect: notified TTY hangup");
+}
+
+static gboolean
+_timeout_cb (gpointer user_data)
+{
+ DunConnectData *dun_connect_data = user_data;
+
+ _LOGI ("timeout");
+ dun_connect_data->timeout_id = 0;
+ if (dun_connect_data->cancellable)
+ g_cancellable_cancel (dun_connect_data->cancellable);
+ return G_SOURCE_REMOVE;
+}
+
+static gboolean
+_sig_xxx_cb (DunConnectData *dun_connect_data, int sigid)
+{
+ _LOGI ("signal %s received", sigid == SIGTERM ? "SIGTERM" : "SIGINT");
+ g_main_loop_quit (gl.loop);
+ return G_SOURCE_CONTINUE;
+}
+
+static gboolean
+_sig_term_cb (gpointer user_data)
+{
+ return _sig_xxx_cb (user_data, SIGTERM);
+}
+
+static gboolean
+_sig_int_cb (gpointer user_data)
+{
+ return _sig_xxx_cb (user_data, SIGINT);
+}
+#endif
+
+static int
+do_dun_connect (const MainCmdInfo *main_cmd_info)
+{
+#if WITH_BLUEZ5_DUN
+ gs_unref_object GCancellable *cancellable = NULL;
+ gs_free_error GError *error = NULL;
+ const char *adapter;
+ const char *remote;
+ DunConnectData dun_connect_data = { };
+
+ if (gl.argc < 4) {
+ _LOGE ("missing arguments \"adapter\" and \"remote\"");
+ return -1;
+ }
+
+ adapter = gl.argv[2];
+ remote = gl.argv[3];
+
+ cancellable = g_cancellable_new ();
+ dun_connect_data.cancellable = cancellable;
+
+ if (!nm_bluez5_dun_connect (adapter,
+ remote,
+ cancellable,
+ _dun_connect_cb,
+ &dun_connect_data,
+ _dun_notify_tty_hangup_cb,
+ &dun_connect_data,
+ &error)) {
+ _LOGE ("connect failed to start: %s", error->message);
+ return -1;
+ }
+
+ dun_connect_data.timeout_id = g_timeout_add (60000, _timeout_cb, &dun_connect_data);
+
+ g_main_loop_run (gl.loop);
+
+ nm_clear_g_source (&dun_connect_data.timeout_id);
+
+ if (dun_connect_data.dun_context) {
+
+ dun_connect_data.sig_term_id = g_unix_signal_add (SIGTERM, _sig_term_cb, &dun_connect_data);
+ dun_connect_data.sig_int_id = g_unix_signal_add (SIGINT, _sig_int_cb, &dun_connect_data);
+
+ g_main_loop_run (gl.loop);
+
+ nm_clear_g_source (&dun_connect_data.sig_term_id);
+ nm_clear_g_source (&dun_connect_data.sig_int_id);
+
+ nm_bluez5_dun_disconnect (g_steal_pointer (&dun_connect_data.dun_context));
+ }
+
+ return 0;
+#else
+ _LOGE ("compiled without bluetooth DUN support");
+ return 1;
+#endif
+}
+
+/*****************************************************************************/
+
NMTST_DEFINE ();
int
main (int argc, char **argv)
{
- NMBluez5DunContext *dun_context;
- GMainLoop *loop;
+ static const MainCmdInfo main_cmd_infos[] = {
+ { .name = "dun-connect", .main_func = do_dun_connect, },
+ };
+ int exit_code = 0;
+ guint i;
if (!g_getenv ("G_MESSAGES_DEBUG"))
g_setenv ("G_MESSAGES_DEBUG", "all", TRUE);
nmtst_init_with_logging (&argc, &argv, "DEBUG", "ALL");
- nm_log_info (LOGD_BT, "bluetooth test util start");
+ nm_logging_init (NULL, TRUE);
+
+ gl.argv = (const char *const*) argv;
+ gl.argc = argc;
+ gl.loop = g_main_loop_new (NULL, FALSE);
- dun_context = nm_bluez5_dun_new ("aa:bb:cc:dd:ee:ff",
- "aa:bb:cc:dd:ee:fa");
+ _LOGI ("bluetooth test util start");
- loop = g_main_loop_new (NULL, FALSE);
+ gl.argv_cmd = argc >= 2 ? argv[1] : NULL;
- g_main_loop_unref (loop);
+ for (i = 0; i < G_N_ELEMENTS (main_cmd_infos); i++) {
+ if (nm_streq0 (main_cmd_infos[i].name, gl.argv_cmd)) {
+ _LOGD ("start \"%s\"", gl.argv_cmd);
+ exit_code = main_cmd_infos[i].main_func (&main_cmd_infos[i]);
+ _LOGD ("completed with %d", exit_code);
+ break;
+ }
+ }
+ if (gl.argv_cmd && i >= G_N_ELEMENTS (main_cmd_infos)) {
+ nm_log_err (LOGD_BT, "invalid command \"%s\"", gl.argv_cmd);
+ exit_code = -1;
+ }
- nm_bluez5_dun_free (dun_context);
+ nm_clear_pointer (&gl.loop, g_main_loop_unref);
- return EXIT_SUCCESS;
+ return exit_code;
}
diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c
index 66dc6dc809..aadaa7faee 100644
--- a/src/devices/nm-device-bridge.c
+++ b/src/devices/nm-device-bridge.c
@@ -23,7 +23,9 @@ _LOG_DECLARE_SELF(NMDeviceBridge);
struct _NMDeviceBridge {
NMDevice parent;
+ GCancellable *bt_cancellable;
bool vlan_configured:1;
+ bool bt_registered:1;
};
struct _NMDeviceBridgeClass {
@@ -51,6 +53,7 @@ check_connection_available (NMDevice *device,
const char *specific_object,
GError **error)
{
+ NMDeviceBridge *self = NM_DEVICE_BRIDGE (device);
NMSettingBluetooth *s_bt;
if (!NM_DEVICE_CLASS (nm_device_bridge_parent_class)->check_connection_available (device, connection, flags, specific_object, error))
@@ -67,13 +70,18 @@ check_connection_available (NMDevice *device,
}
bdaddr = nm_setting_bluetooth_get_bdaddr (s_bt);
- if (!nm_bt_vtable_network_server->is_available (nm_bt_vtable_network_server, bdaddr)) {
+ if (!nm_bt_vtable_network_server->is_available (nm_bt_vtable_network_server,
+ bdaddr,
+ ( self->bt_cancellable
+ || self->bt_registered)
+ ? device
+ : NULL)) {
if (bdaddr)
nm_utils_error_set (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
- "not suitable NAP device \"%s\" available", bdaddr);
+ "no suitable NAP device \"%s\" available", bdaddr);
else
nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
- "not suitable NAP device available");
+ "no suitable NAP device available");
return FALSE;
}
}
@@ -505,9 +513,53 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
return NM_ACT_STAGE_RETURN_SUCCESS;
}
+static void
+_bt_register_bridge_cb (GError *error,
+ gpointer user_data)
+{
+ NMDeviceBridge *self;
+
+ if (nm_utils_error_is_cancelled (error, FALSE))
+ return;
+
+ self = user_data;
+
+ g_clear_object (&self->bt_cancellable);
+
+ if (error) {
+ _LOGD (LOGD_DEVICE, "bluetooth NAP server failed to register bridge: %s", error->message);
+ nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED,
+ NM_DEVICE_STATE_REASON_BT_FAILED);
+ return;
+ }
+
+ nm_device_activate_schedule_stage3_ip_config_start (NM_DEVICE (self));
+}
+
+void
+_nm_device_bridge_notify_unregister_bt_nap (NMDevice *device,
+ const char *reason)
+{
+ NMDeviceBridge *self = NM_DEVICE_BRIDGE (device);
+
+ _LOGD (LOGD_DEVICE, "bluetooth NAP server unregistered from bridge: %s%s",
+ reason,
+ self->bt_registered ? "" : " (was no longer registered)");
+
+ nm_clear_g_cancellable (&self->bt_cancellable);
+
+ if (self->bt_registered) {
+ self->bt_registered = FALSE;
+ nm_device_state_changed (device,
+ NM_DEVICE_STATE_FAILED,
+ NM_DEVICE_STATE_REASON_BT_FAILED);
+ }
+}
+
static NMActStageReturn
act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
{
+ NMDeviceBridge *self = NM_DEVICE_BRIDGE (device);
NMConnection *connection;
NMSettingBluetooth *s_bt;
@@ -515,14 +567,32 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
s_bt = _nm_connection_get_setting_bluetooth_for_nap (connection);
if (s_bt) {
- if ( !nm_bt_vtable_network_server
- || !nm_bt_vtable_network_server->register_bridge (nm_bt_vtable_network_server,
- nm_setting_bluetooth_get_bdaddr (s_bt),
- device)) {
- /* The HCI we could use is no longer present. */
- *out_failure_reason = NM_DEVICE_STATE_REASON_REMOVED;
+ gs_free_error GError *error = NULL;
+
+ if (!nm_bt_vtable_network_server) {
+ _LOGD (LOGD_DEVICE, "bluetooth NAP server failed because bluetooth plugin not available");
+ *out_failure_reason = NM_DEVICE_STATE_REASON_BT_FAILED;
+ return NM_ACT_STAGE_RETURN_FAILURE;
+ }
+
+ if (self->bt_cancellable)
+ return NM_ACT_STAGE_RETURN_POSTPONE;
+
+ self->bt_cancellable = g_cancellable_new ();
+ if (!nm_bt_vtable_network_server->register_bridge (nm_bt_vtable_network_server,
+ nm_setting_bluetooth_get_bdaddr (s_bt),
+ device,
+ self->bt_cancellable,
+ _bt_register_bridge_cb,
+ device,
+ &error)) {
+ _LOGD (LOGD_DEVICE, "bluetooth NAP server failed to register bridge: %s", error->message);
+ *out_failure_reason = NM_DEVICE_STATE_REASON_BT_FAILED;
return NM_ACT_STAGE_RETURN_FAILURE;
}
+
+ self->bt_registered = TRUE;
+ return NM_ACT_STAGE_RETURN_POSTPONE;
}
return NM_ACT_STAGE_RETURN_SUCCESS;
@@ -533,11 +603,15 @@ deactivate (NMDevice *device)
{
NMDeviceBridge *self = NM_DEVICE_BRIDGE (device);
+ _LOGD (LOGD_DEVICE, "deactivate bridge%s",
+ self->bt_registered ? " (registered as NAP bluetooth device)" : "");
+
self->vlan_configured = FALSE;
- if (nm_bt_vtable_network_server) {
- /* always call unregister. It does nothing if the device
- * isn't registered as a hotspot bridge. */
+ nm_clear_g_cancellable (&self->bt_cancellable);
+
+ if (self->bt_registered) {
+ self->bt_registered = FALSE;
nm_bt_vtable_network_server->unregister_bridge (nm_bt_vtable_network_server,
device);
}
diff --git a/src/devices/nm-device-bridge.h b/src/devices/nm-device-bridge.h
index 6830a84a13..1caf0f1f01 100644
--- a/src/devices/nm-device-bridge.h
+++ b/src/devices/nm-device-bridge.h
@@ -23,4 +23,7 @@ GType nm_device_bridge_get_type (void);
extern const NMBtVTableNetworkServer *nm_bt_vtable_network_server;
+void _nm_device_bridge_notify_unregister_bt_nap (NMDevice *device,
+ const char *reason);
+
#endif /* __NETWORKMANAGER_DEVICE_BRIDGE_H__ */
diff --git a/src/devices/nm-device-factory.c b/src/devices/nm-device-factory.c
index bf4d490d52..6633c35fb0 100644
--- a/src/devices/nm-device-factory.c
+++ b/src/devices/nm-device-factory.c
@@ -23,7 +23,6 @@
enum {
DEVICE_ADDED,
- COMPONENT_ADDED,
LAST_SIGNAL
};
@@ -33,17 +32,6 @@ G_DEFINE_ABSTRACT_TYPE (NMDeviceFactory, nm_device_factory, G_TYPE_OBJECT)
/*****************************************************************************/
-gboolean
-nm_device_factory_emit_component_added (NMDeviceFactory *factory, GObject *component)
-{
- gboolean consumed = FALSE;
-
- g_return_val_if_fail (NM_IS_DEVICE_FACTORY (factory), FALSE);
-
- g_signal_emit (factory, signals[COMPONENT_ADDED], 0, component, &consumed);
- return consumed;
-}
-
static void
nm_device_factory_get_supported_types (NMDeviceFactory *factory,
const NMLinkType **out_link_types,
@@ -182,16 +170,8 @@ nm_device_factory_class_init (NMDeviceFactoryClass *klass)
signals[DEVICE_ADDED] = g_signal_new (NM_DEVICE_FACTORY_DEVICE_ADDED,
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
- G_STRUCT_OFFSET (NMDeviceFactoryClass, device_added),
- NULL, NULL, NULL,
+ 0, NULL, NULL, NULL,
G_TYPE_NONE, 1, NM_TYPE_DEVICE);
-
- signals[COMPONENT_ADDED] = g_signal_new (NM_DEVICE_FACTORY_COMPONENT_ADDED,
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (NMDeviceFactoryClass, component_added),
- g_signal_accumulator_true_handled, NULL, NULL,
- G_TYPE_BOOLEAN, 1, G_TYPE_OBJECT);
}
/*****************************************************************************/
diff --git a/src/devices/nm-device-factory.h b/src/devices/nm-device-factory.h
index ac3bb1979c..11e83e9508 100644
--- a/src/devices/nm-device-factory.h
+++ b/src/devices/nm-device-factory.h
@@ -23,7 +23,6 @@
#define NM_IS_DEVICE_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_FACTORY))
#define NM_DEVICE_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_FACTORY, NMDeviceFactoryClass))
-#define NM_DEVICE_FACTORY_COMPONENT_ADDED "component-added"
#define NM_DEVICE_FACTORY_DEVICE_ADDED "device-added"
typedef struct {
@@ -118,36 +117,6 @@ typedef struct {
NMConnection *connection,
gboolean *out_ignore);
- /* Signals */
-
- /**
- * device_added:
- * @factory: the #NMDeviceFactory
- * @device: the new #NMDevice subclass
- *
- * The factory emits this signal if it finds a new device by itself.
- */
- void (*device_added) (NMDeviceFactory *factory, NMDevice *device);
-
- /**
- * component_added:
- * @factory: the #NMDeviceFactory
- * @component: a new component which existing devices may wish to claim
- *
- * The factory emits this signal when an appearance of some component
- * native to it could be interesting to some of the already existing devices.
- * The devices then indicate if they took interest in claiming the component.
- *
- * For example, the WWAN factory may indicate that a new modem is available,
- * which an existing Bluetooth device may wish to claim. It emits a signal
- * passing the modem instance around to see if any device claims it.
- * If no device claims the component, the plugin is allowed to create a new
- * #NMDevice instance for that component and emit the "device-added" signal.
- *
- * Returns: %TRUE if the component was claimed by a device, %FALSE if not
- */
- gboolean (*component_added) (NMDeviceFactory *factory, GObject *component);
-
} NMDeviceFactoryClass;
GType nm_device_factory_get_type (void);
@@ -189,10 +158,6 @@ NMDevice * nm_device_factory_create_device (NMDeviceFactory *factory,
gboolean *out_ignore,
GError **error);
-/* For use by implementations */
-gboolean nm_device_factory_emit_component_added (NMDeviceFactory *factory,
- GObject *component);
-
#define NM_DEVICE_FACTORY_DECLARE_LINK_TYPES(...) \
{ static NMLinkType const _link_types_declared[] = { __VA_ARGS__, NM_LINK_TYPE_NONE }; _link_types = _link_types_declared; }
#define NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES(...) \
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index a30f588b27..bcbf5c27cd 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -4785,42 +4785,24 @@ nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error)
return TRUE;
}
-/**
- * nm_device_notify_component_added():
- * @self: the #NMDevice
- * @component: the component being added by a plugin
- *
- * Called by the manager to notify the device that a new component has
- * been found. The device implementation should return %TRUE if it
- * wishes to claim the component, or %FALSE if it cannot.
- *
- * Returns: %TRUE to claim the component, %FALSE if the component cannot be
- * claimed.
- */
-gboolean
-nm_device_notify_component_added (NMDevice *self, GObject *component)
+void
+nm_device_notify_availability_maybe_changed (NMDevice *self)
{
- NMDeviceClass *klass;
NMDevicePrivate *priv;
- g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+ g_return_if_fail (NM_IS_DEVICE (self));
priv = NM_DEVICE_GET_PRIVATE (self);
- klass = NM_DEVICE_GET_CLASS (self);
-
- if (priv->state == NM_DEVICE_STATE_DISCONNECTED) {
- /* A device could have stayed disconnected because it would
- * want to register with a network server that now become
- * available. */
- nm_device_recheck_available_connections (self);
- if (g_hash_table_size (priv->available_connections) > 0)
- nm_device_emit_recheck_auto_activate (self);
- }
- if (klass->component_added)
- return klass->component_added (self, component);
+ if (priv->state != NM_DEVICE_STATE_DISCONNECTED)
+ return;
- return FALSE;
+ /* A device could have stayed disconnected because it would
+ * want to register with a network server that now become
+ * available. */
+ nm_device_recheck_available_connections (self);
+ if (g_hash_table_size (priv->available_connections) > 0)
+ nm_device_emit_recheck_auto_activate (self);
}
/**
diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
index 72c269c653..eaeac620d2 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -416,23 +416,6 @@ typedef struct _NMDeviceClass {
int new_ifindex,
NMDevice *new_parent);
- /**
- * component_added:
- * @self: the #NMDevice
- * @component: the component (device, modem, etc) which was added
- *
- * Notifies @self that a new component that a device might be interested
- * in was detected by some device factory. It may include an object of
- * %GObject subclass to help the devices decide whether it claims that
- * particular object itself and the emitting factory should not.
- *
- * Returns: %TRUE if the component was claimed exclusively and no further
- * devices should be notified of the new component. %FALSE to indicate
- * that the component was not exclusively claimed and other devices should
- * be notified.
- */
- gboolean (* component_added) (NMDevice *self, GObject *component);
-
gboolean (* owns_iface) (NMDevice *self, const char *iface);
NMConnection * (* new_default_connection) (NMDevice *self);
@@ -809,7 +792,7 @@ gboolean nm_device_check_connection_available (NMDevice *device,
const char *specific_object,
GError **error);
-gboolean nm_device_notify_component_added (NMDevice *device, GObject *component);
+void nm_device_notify_availability_maybe_changed (NMDevice *self);
gboolean nm_device_owns_iface (NMDevice *device, const char *iface);
@@ -869,12 +852,22 @@ void nm_device_check_connectivity_cancel (NMDeviceConnectivityHandle *handle);
NMConnectivityState nm_device_get_connectivity_state (NMDevice *self, int addr_family);
typedef struct _NMBtVTableNetworkServer NMBtVTableNetworkServer;
+
+typedef void (*NMBtVTableRegisterCallback) (GError *error,
+ gpointer user_data);
+
struct _NMBtVTableNetworkServer {
gboolean (*is_available) (const NMBtVTableNetworkServer *vtable,
- const char *addr);
+ const char *addr,
+ NMDevice *device_accept_busy);
+
gboolean (*register_bridge) (const NMBtVTableNetworkServer *vtable,
const char *addr,
- NMDevice *device);
+ NMDevice *device,
+ GCancellable *cancellable,
+ NMBtVTableRegisterCallback callback,
+ gpointer callback_user_data,
+ GError **error);
gboolean (*unregister_bridge) (const NMBtVTableNetworkServer *vtable,
NMDevice *device);
};
diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c
index 32490da1a6..e6174ccc49 100644
--- a/src/devices/wwan/nm-modem.c
+++ b/src/devices/wwan/nm-modem.c
@@ -1029,6 +1029,8 @@ nm_modem_act_stage1_prepare (NMModem *self,
NMSecretAgentGetSecretsFlags flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION;
NMConnection *connection;
+ g_return_val_if_fail (NM_IS_ACT_REQUEST (req), NM_ACT_STAGE_RETURN_FAILURE);
+
if (priv->act_request)
g_object_unref (priv->act_request);
priv->act_request = g_object_ref (req);
@@ -1066,8 +1068,11 @@ nm_modem_act_stage1_prepare (NMModem *self,
void
nm_modem_act_stage2_config (NMModem *self)
{
- NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
+ NMModemPrivate *priv;
+ g_return_if_fail (NM_IS_MODEM (self));
+
+ priv = NM_MODEM_GET_PRIVATE (self);
/* Clear secrets tries counter since secrets were successfully used
* already if we get here.
*/
diff --git a/src/devices/wwan/nm-wwan-factory.c b/src/devices/wwan/nm-wwan-factory.c
index 7a93b47c7b..c81a8175bd 100644
--- a/src/devices/wwan/nm-wwan-factory.c
+++ b/src/devices/wwan/nm-wwan-factory.c
@@ -67,10 +67,6 @@ modem_added_cb (NMModemManager *manager,
gs_unref_object NMDevice *device = NULL;
const char *driver;
- /* Do nothing if the modem was consumed by some other plugin */
- if (nm_device_factory_emit_component_added (NM_DEVICE_FACTORY (self), G_OBJECT (modem)))
- return;
-
if (nm_modem_is_claimed (modem))
return;
@@ -80,9 +76,10 @@ modem_added_cb (NMModemManager *manager,
* it. The rfcomm port (and thus the modem) gets created automatically
* by the Bluetooth code during the connection process.
*/
- if (driver && strstr (driver, "bluetooth")) {
- nm_log_info (LOGD_MB, "ignoring modem '%s' (no associated Bluetooth device)",
- nm_modem_get_control_port (modem));
+ if ( driver
+ && strstr (driver, "bluetooth")) {
+ nm_log_dbg (LOGD_MB, "WWAN factory ignores bluetooth modem '%s' which should be handled by bluetooth plugin",
+ nm_modem_get_control_port (modem));
return;
}
diff --git a/src/nm-manager.c b/src/nm-manager.c
index 6a01426753..b21d39a015 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -3188,22 +3188,6 @@ factory_device_added_cb (NMDeviceFactory *factory,
}
}
-static gboolean
-factory_component_added_cb (NMDeviceFactory *factory,
- GObject *component,
- gpointer user_data)
-{
- NMManager *self = user_data;
- NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
- NMDevice *device;
-
- c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
- if (nm_device_notify_component_added (device, component))
- return TRUE;
- }
- return FALSE;
-}
-
static void
_register_device_factory (NMDeviceFactory *factory, gpointer user_data)
{
@@ -3213,10 +3197,18 @@ _register_device_factory (NMDeviceFactory *factory, gpointer user_data)
NM_DEVICE_FACTORY_DEVICE_ADDED,
G_CALLBACK (factory_device_added_cb),
self);
- g_signal_connect (factory,
- NM_DEVICE_FACTORY_COMPONENT_ADDED,
- G_CALLBACK (factory_component_added_cb),
- self);
+}
+
+/*****************************************************************************/
+
+void
+nm_manager_notify_device_availibility_maybe_changed (NMManager *self)
+{
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+ NMDevice *device;
+
+ c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst)
+ nm_device_notify_availability_maybe_changed (device);
}
/*****************************************************************************/
diff --git a/src/nm-manager.h b/src/nm-manager.h
index bedd004e81..187ce72863 100644
--- a/src/nm-manager.h
+++ b/src/nm-manager.h
@@ -191,4 +191,6 @@ void nm_manager_dbus_set_property_handle (NMDBusObject *obj,
NMMetered nm_manager_get_metered (NMManager *self);
+void nm_manager_notify_device_availibility_maybe_changed (NMManager *self);
+
#endif /* __NETWORKMANAGER_MANAGER_H__ */