summary refs log tree commit diff
path: root/src/devices/bluetooth
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2017-11-07 00:14:39 +0100
committerMichael Biebl <biebl@debian.org>2017-11-07 00:14:39 +0100
commit90e8691111889a7b5f3c812f5a41f15a8a058913 (patch)
treef101a879eca27c34a9bfa5f3da52266b22539a36 /src/devices/bluetooth
parentbdb6eeb0670658255c2a4c3c501c0a27fa8cfe55 (diff)
New upstream version 1.9.90 upstream/1.9.90
Diffstat (limited to 'src/devices/bluetooth')
-rw-r--r--src/devices/bluetooth/nm-bluez-common.h26
-rw-r--r--src/devices/bluetooth/nm-bluez-device.c58
-rw-r--r--src/devices/bluetooth/nm-bluez-device.h3
-rw-r--r--src/devices/bluetooth/nm-bluez-manager.c37
-rw-r--r--src/devices/bluetooth/nm-bluez4-adapter.c131
-rw-r--r--src/devices/bluetooth/nm-bluez4-adapter.h6
-rw-r--r--src/devices/bluetooth/nm-bluez4-manager.c154
-rw-r--r--src/devices/bluetooth/nm-bluez5-dun.c4
-rw-r--r--src/devices/bluetooth/nm-bluez5-manager.c299
-rw-r--r--src/devices/bluetooth/nm-device-bt.c96
-rw-r--r--src/devices/bluetooth/nm-device-bt.h6
11 files changed, 591 insertions, 229 deletions
diff --git a/src/devices/bluetooth/nm-bluez-common.h b/src/devices/bluetooth/nm-bluez-common.h
index 6e97c3f5..d72bea81 100644
--- a/src/devices/bluetooth/nm-bluez-common.h
+++ b/src/devices/bluetooth/nm-bluez-common.h
@@ -15,7 +15,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #ifndef __NETWORKMANAGER_BLUEZ_COMMON_H__
@@ -24,21 +24,23 @@
 #define BLUETOOTH_CONNECT_DUN "dun"
 #define BLUETOOTH_CONNECT_NAP "nap"
 
-#define BLUEZ_SERVICE           "org.bluez"
+#define NM_BLUEZ_SERVICE           "org.bluez"
 
-#define BLUEZ_MANAGER_PATH      "/"
-#define OBJECT_MANAGER_INTERFACE "org.freedesktop.DBus.ObjectManager"
+#define NM_BLUEZ_MANAGER_PATH      "/"
+#define NM_OBJECT_MANAGER_INTERFACE "org.freedesktop.DBus.ObjectManager"
 
-#define BLUEZ5_ADAPTER_INTERFACE "org.bluez.Adapter1"
-#define BLUEZ5_DEVICE_INTERFACE  "org.bluez.Device1"
-#define BLUEZ5_NETWORK_INTERFACE "org.bluez.Network1"
+#define NM_BLUEZ5_ADAPTER_INTERFACE "org.bluez.Adapter1"
+#define NM_BLUEZ5_DEVICE_INTERFACE  "org.bluez.Device1"
+#define NM_BLUEZ5_NETWORK_INTERFACE "org.bluez.Network1"
+#define NM_BLUEZ5_NETWORK_SERVER_INTERFACE "org.bluez.NetworkServer1"
 
-#define BLUEZ4_MANAGER_INTERFACE "org.bluez.Manager"
-#define BLUEZ4_ADAPTER_INTERFACE "org.bluez.Adapter"
-#define BLUEZ4_DEVICE_INTERFACE  "org.bluez.Device"
-#define BLUEZ4_SERIAL_INTERFACE  "org.bluez.Serial"
-#define BLUEZ4_NETWORK_INTERFACE "org.bluez.Network"
+#define NM_BLUEZ4_MANAGER_INTERFACE "org.bluez.Manager"
+#define NM_BLUEZ4_ADAPTER_INTERFACE "org.bluez.Adapter"
+#define NM_BLUEZ4_DEVICE_INTERFACE  "org.bluez.Device"
+#define NM_BLUEZ4_SERIAL_INTERFACE  "org.bluez.Serial"
+#define NM_BLUEZ4_NETWORK_INTERFACE "org.bluez.Network"
 
 #define NM_BLUEZ_MANAGER_BDADDR_ADDED "bdaddr-added"
+#define NM_BLUEZ_MANAGER_NETWORK_SERVER_ADDED "network-server-added"
 
 #endif  /* NM_BLUEZ_COMMON_H */
diff --git a/src/devices/bluetooth/nm-bluez-device.c b/src/devices/bluetooth/nm-bluez-device.c
index 41ef74ca..182527d9 100644
--- a/src/devices/bluetooth/nm-bluez-device.c
+++ b/src/devices/bluetooth/nm-bluez-device.c
@@ -84,6 +84,7 @@ typedef struct {
 	char *name;
 	guint32 capabilities;
 	gboolean connected;
+	gboolean paired;
 
 	char *b4_iface;
 #if WITH_BLUEZ5_DUN
@@ -278,10 +279,11 @@ check_emit_usable (NMBluezDevice *self)
 	/* 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->bluez_version == 4) ||
-	               (priv->bluez_version == 5 && priv->adapter5 && priv->adapter_powered) ) &&
-	              priv->dbus_connection && priv->address && priv->adapter_address);
+	new_usable = (   priv->initialized && priv->capabilities
+	              && priv->name && priv->paired
+	              && (   (priv->bluez_version == 4)
+	                  || (priv->bluez_version == 5 && priv->adapter5 && priv->adapter_powered))
+	              && priv->dbus_connection && priv->address && priv->adapter_address);
 
 	if (!new_usable)
 		goto END;
@@ -343,6 +345,10 @@ connection_compatible (NMBluezDevice *self, NMConnection *connection)
 		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;
@@ -465,7 +471,7 @@ nm_bluez_device_disconnect (NMBluezDevice *self)
 			if (!priv->b4_iface)
 				goto out;
 			args = g_variant_new ("(s)", priv->b4_iface),
-			dbus_iface = BLUEZ4_SERIAL_INTERFACE;
+			dbus_iface = NM_BLUEZ4_SERIAL_INTERFACE;
 		} else if (priv->bluez_version == 5) {
 #if WITH_BLUEZ5_DUN
 			nm_bluez5_dun_cleanup (priv->b5_dun_context);
@@ -475,16 +481,16 @@ nm_bluez_device_disconnect (NMBluezDevice *self)
 		}
 	} else if (priv->connection_bt_type == NM_BT_CAPABILITY_NAP) {
 		if (priv->bluez_version == 4)
-			dbus_iface = BLUEZ4_NETWORK_INTERFACE;
+			dbus_iface = NM_BLUEZ4_NETWORK_INTERFACE;
 		else if (priv->bluez_version == 5)
-			dbus_iface = BLUEZ5_NETWORK_INTERFACE;
+			dbus_iface = NM_BLUEZ5_NETWORK_INTERFACE;
 		else
 			g_assert_not_reached ();
 	} else
 		g_assert_not_reached ();
 
 	g_dbus_connection_call (priv->dbus_connection,
-	                        BLUEZ_SERVICE,
+	                        NM_BLUEZ_SERVICE,
 	                        priv->path,
 	                        dbus_iface,
 	                        "Disconnect",
@@ -577,13 +583,13 @@ nm_bluez_device_connect_async (NMBluezDevice *self,
 	if (connection_bt_type == NM_BT_CAPABILITY_NAP) {
 		connect_type = BLUETOOTH_CONNECT_NAP;
 		if (priv->bluez_version == 4)
-			dbus_iface = BLUEZ4_NETWORK_INTERFACE;
+			dbus_iface = NM_BLUEZ4_NETWORK_INTERFACE;
 		else if (priv->bluez_version == 5)
-			dbus_iface = BLUEZ5_NETWORK_INTERFACE;
+			dbus_iface = NM_BLUEZ5_NETWORK_INTERFACE;
 	} else if (connection_bt_type == NM_BT_CAPABILITY_DUN) {
 		connect_type = BLUETOOTH_CONNECT_DUN;
 		if (priv->bluez_version == 4)
-			dbus_iface = BLUEZ4_SERIAL_INTERFACE;
+			dbus_iface = NM_BLUEZ4_SERIAL_INTERFACE;
 		else if (priv->bluez_version == 5) {
 #if WITH_BLUEZ5_DUN
 			if (priv->b5_dun_context == NULL)
@@ -602,7 +608,7 @@ nm_bluez_device_connect_async (NMBluezDevice *self,
 		g_assert_not_reached ();
 
 	g_dbus_connection_call (priv->dbus_connection,
-	                        BLUEZ_SERVICE,
+	                        NM_BLUEZ_SERVICE,
 	                        priv->path,
 	                        dbus_iface,
 	                        "Connect",
@@ -795,6 +801,17 @@ _take_variant_property_connected (NMBluezDevice *self, GVariant *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,
@@ -864,6 +881,8 @@ _take_one_variant_property (NMBluezDevice *self, const char *property, GVariant
 			_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"))
@@ -963,6 +982,7 @@ query_properties (NMBluezDevice *self)
 		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));
@@ -972,9 +992,9 @@ query_properties (NMBluezDevice *self)
 			g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
 			                          G_DBUS_PROXY_FLAGS_NONE,
 			                          NULL,
-			                          BLUEZ_SERVICE,
+			                          NM_BLUEZ_SERVICE,
 			                          g_variant_get_string (v, NULL),
-			                          BLUEZ5_ADAPTER_INTERFACE,
+			                          NM_BLUEZ5_ADAPTER_INTERFACE,
 			                          NULL,
 			                          (GAsyncReadyCallback) adapter5_on_acquired,
 			                          g_object_ref (self));
@@ -1134,17 +1154,17 @@ nm_bluez_device_new (const char *path,
 
 	switch (priv->bluez_version) {
 	case 4:
-		interface_name = BLUEZ4_DEVICE_INTERFACE;
+		interface_name = NM_BLUEZ4_DEVICE_INTERFACE;
 		break;
 	case 5:
-		interface_name = BLUEZ5_DEVICE_INTERFACE;
+		interface_name = NM_BLUEZ5_DEVICE_INTERFACE;
 		break;
 	}
 
 	g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
 	                          G_DBUS_PROXY_FLAGS_NONE,
 	                          NULL,
-	                          BLUEZ_SERVICE,
+	                          NM_BLUEZ_SERVICE,
 	                          priv->path,
 	                          interface_name,
 	                          NULL,
@@ -1199,7 +1219,7 @@ dispose (GObject *object)
 	if (to_delete) {
 		nm_log_dbg (LOGD_BT, "bluez[%s] removing Bluetooth connection for NAP device: '%s' (%s)", priv->path,
 		            nm_connection_get_id (to_delete), nm_connection_get_uuid (to_delete));
-		nm_settings_connection_delete (NM_SETTINGS_CONNECTION (to_delete), NULL, NULL);
+		nm_settings_connection_delete (NM_SETTINGS_CONNECTION (to_delete), NULL);
 		g_object_unref (to_delete);
 	}
 
@@ -1274,7 +1294,7 @@ nm_bluez_device_class_init (NMBluezDeviceClass *config_class)
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
-	signals[INITIALIZED] = g_signal_new ("initialized",
+	signals[INITIALIZED] = g_signal_new (NM_BLUEZ_DEVICE_INITIALIZED,
 	                                     G_OBJECT_CLASS_TYPE (object_class),
 	                                     G_SIGNAL_RUN_LAST,
 	                                     0,
diff --git a/src/devices/bluetooth/nm-bluez-device.h b/src/devices/bluetooth/nm-bluez-device.h
index e56d5d24..f8a1872f 100644
--- a/src/devices/bluetooth/nm-bluez-device.h
+++ b/src/devices/bluetooth/nm-bluez-device.h
@@ -39,6 +39,7 @@
 #define NM_BLUEZ_DEVICE_CONNECTED    "connected"
 
 /* Signals */
+#define NM_BLUEZ_DEVICE_INITIALIZED  "initialized"
 #define NM_BLUEZ_DEVICE_REMOVED      "removed"
 
 typedef struct _NMBluezDevice NMBluezDevice;
@@ -61,8 +62,6 @@ const char *nm_bluez_device_get_address (NMBluezDevice *self);
 
 const char *nm_bluez_device_get_name (NMBluezDevice *self);
 
-guint32 nm_bluez_device_get_class (NMBluezDevice *self);
-
 guint32 nm_bluez_device_get_capabilities (NMBluezDevice *self);
 
 gboolean nm_bluez_device_get_connected (NMBluezDevice *self);
diff --git a/src/devices/bluetooth/nm-bluez-manager.c b/src/devices/bluetooth/nm-bluez-manager.c
index 2f0afa16..96e80245 100644
--- a/src/devices/bluetooth/nm-bluez-manager.c
+++ b/src/devices/bluetooth/nm-bluez-manager.c
@@ -26,6 +26,7 @@
 #include <gmodule.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-bluez4-manager.h"
@@ -146,7 +147,7 @@ cleanup_checking (NMBluezManager *self, gboolean do_unwatch_name)
 
 
 static void
-manager_bdaddr_added_cb (NMBluez4Manager *bluez_mgr,
+manager_bdaddr_added_cb (GObject *manager,
                          NMBluezDevice *bt_device,
                          const char *bdaddr,
                          const char *name,
@@ -180,6 +181,13 @@ manager_bdaddr_added_cb (NMBluez4Manager *bluez_mgr,
 }
 
 static void
+manager_network_server_added_cb (GObject *manager,
+                                 gpointer user_data)
+{
+	nm_device_factory_emit_component_added (NM_DEVICE_FACTORY (user_data), NULL);
+}
+
+static void
 setup_version_number (NMBluezManager *self, int bluez_version)
 {
 	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
@@ -228,6 +236,10 @@ setup_bluez5 (NMBluezManager *self)
 	                  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);
 
 	nm_bluez5_manager_query_devices (manager);
 }
@@ -264,7 +276,7 @@ check_bluez_and_try_setup_final_step (NMBluezManager *self, int bluez_version, c
 		cleanup_checking (self, FALSE);
 		if (!priv->watch_name_id) {
 			priv->watch_name_id = g_bus_watch_name (G_BUS_TYPE_SYSTEM,
-			                                        BLUEZ_SERVICE,
+			                                        NM_BLUEZ_SERVICE,
 			                                        G_BUS_NAME_WATCHER_FLAGS_NONE,
 			                                        watch_name_on_appeared,
 			                                        NULL,
@@ -317,7 +329,7 @@ check_bluez_and_try_setup_do_introspect (GObject *source_object,
 	/* might not be the best approach to detect the version, but it's good enough in practice. */
 	if (strstr (xml_data, "org.freedesktop.DBus.ObjectManager"))
 		bluez_version = 5;
-	else if (strstr (xml_data, BLUEZ4_MANAGER_INTERFACE))
+	else if (strstr (xml_data, NM_BLUEZ4_MANAGER_INTERFACE))
 		bluez_version = 4;
 	else
 		reason = "unexpected introspect result";
@@ -380,7 +392,7 @@ check_bluez_and_try_setup (NMBluezManager *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,
-	                          BLUEZ_SERVICE,
+	                          NM_BLUEZ_SERVICE,
 	                          "/",
 	                          DBUS_INTERFACE_INTROSPECTABLE,
 	                          priv->async_cancellable,
@@ -406,6 +418,20 @@ create_device (NMDeviceFactory *factory,
 	return NULL;
 }
 
+static gboolean
+match_connection (NMDeviceFactory *factory,
+                  NMConnection *connection)
+{
+	const char *type = nm_connection_get_connection_type (connection);
+
+	nm_assert (nm_streq (type, NM_SETTING_BLUETOOTH_SETTING_NAME));
+
+	if (_nm_connection_get_setting_bluetooth_for_nap (connection))
+		return FALSE;    /* handled by the bridge factory */
+
+	return TRUE;
+}
+
 /*****************************************************************************/
 
 static void
@@ -427,7 +453,7 @@ dispose (GObject *object)
 		g_clear_object (&priv->manager4);
 	}
 	if (priv->manager5) {
-		g_signal_handlers_disconnect_by_func (priv->manager5, manager_bdaddr_added_cb, self);
+		g_signal_handlers_disconnect_by_data (priv->manager5, self);
 		g_clear_object (&priv->manager5);
 	}
 
@@ -450,5 +476,6 @@ nm_bluez_manager_class_init (NMBluezManagerClass *klass)
 
 	factory_class->get_supported_types = get_supported_types;
 	factory_class->create_device = create_device;
+	factory_class->match_connection = match_connection;
 	factory_class->start = start;
 }
diff --git a/src/devices/bluetooth/nm-bluez4-adapter.c b/src/devices/bluetooth/nm-bluez4-adapter.c
index c0c1be30..0f19f998 100644
--- a/src/devices/bluetooth/nm-bluez4-adapter.c
+++ b/src/devices/bluetooth/nm-bluez4-adapter.c
@@ -25,6 +25,7 @@
 #include <string.h>
 
 #include "nm-dbus-interface.h"
+#include "nm-utils/nm-hash-utils.h"
 #include "nm-bluez-device.h"
 #include "nm-bluez-common.h"
 #include "nm-core-internal.h"
@@ -49,6 +50,7 @@ static guint signals[LAST_SIGNAL] = { 0 };
 typedef struct {
 	char *path;
 	GDBusProxy *proxy;
+	GCancellable *proxy_cancellable;
 	gboolean initialized;
 
 	char *address;
@@ -73,6 +75,11 @@ G_DEFINE_TYPE (NMBluez4Adapter, nm_bluez4_adapter, G_TYPE_OBJECT)
 
 /*****************************************************************************/
 
+#define _NMLOG_DOMAIN      LOGD_BT
+#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "bluez4-adapter", __VA_ARGS__)
+
+/*****************************************************************************/
+
 static void device_do_remove (NMBluez4Adapter *self, NMBluezDevice *device);
 
 /*****************************************************************************/
@@ -119,8 +126,8 @@ nm_bluez4_adapter_get_devices (NMBluez4Adapter *self)
 static void
 emit_device_removed (NMBluez4Adapter *self, NMBluezDevice *device)
 {
-	nm_log_dbg (LOGD_BT, "(%s): bluez device now unusable",
-	            nm_bluez_device_get_path (device));
+	_LOGD ("(%s): bluez device now unusable",
+	       nm_bluez_device_get_path (device));
 	g_signal_emit (self, signals[DEVICE_REMOVED], 0, device);
 }
 
@@ -130,9 +137,9 @@ device_usable (NMBluezDevice *device, GParamSpec *pspec, gpointer user_data)
 	NMBluez4Adapter *self = NM_BLUEZ4_ADAPTER (user_data);
 
 	if (nm_bluez_device_get_usable (device)) {
-		nm_log_dbg (LOGD_BT, "(%s): bluez device now usable (device address is %s)",
-		            nm_bluez_device_get_path (device),
-		            nm_bluez_device_get_address (device));
+		_LOGD ("(%s): bluez device now usable (device address is %s)",
+		       nm_bluez_device_get_path (device),
+		       nm_bluez_device_get_address (device));
 		g_signal_emit (self, signals[DEVICE_ADDED], 0, device);
 	} else
 		emit_device_removed (self, device);
@@ -143,9 +150,9 @@ device_initialized (NMBluezDevice *device, gboolean success, gpointer user_data)
 {
 	NMBluez4Adapter *self = NM_BLUEZ4_ADAPTER (user_data);
 
-	nm_log_dbg (LOGD_BT, "(%s): bluez device %s",
-	            nm_bluez_device_get_path (device),
-	            success ? "initialized" : "failed to initialize");
+	_LOGD ("(%s): bluez device %s",
+	       nm_bluez_device_get_path (device),
+	       success ? "initialized" : "failed to initialize");
 	if (!success)
 		device_do_remove (self, device);
 }
@@ -174,11 +181,11 @@ device_created (GDBusProxy *proxy, const char *path, gpointer user_data)
 	NMBluezDevice *device;
 
 	device = nm_bluez_device_new (path, priv->address, priv->settings, 4);
-	g_signal_connect (device, "initialized", G_CALLBACK (device_initialized), self);
-	g_signal_connect (device, "notify::usable", G_CALLBACK (device_usable), self);
+	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);
 
-	nm_log_dbg (LOGD_BT, "(%s): new bluez device found", path);
+	_LOGD ("(%s): new bluez device found", path);
 }
 
 static void
@@ -188,7 +195,7 @@ device_removed (GDBusProxy *proxy, const char *path, gpointer user_data)
 	NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self);
 	NMBluezDevice *device;
 
-	nm_log_dbg (LOGD_BT, "(%s): bluez device removed", path);
+	_LOGD ("(%s): bluez device removed", path);
 
 	device = g_hash_table_lookup (priv->devices, path);
 	if (device)
@@ -198,19 +205,28 @@ device_removed (GDBusProxy *proxy, const char *path, gpointer user_data)
 static void
 get_properties_cb (GObject *proxy, GAsyncResult *result, gpointer user_data)
 {
-	NMBluez4Adapter *self = NM_BLUEZ4_ADAPTER (user_data);
-	NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self);
-	GError *err = NULL;
+	NMBluez4Adapter *self;
+	NMBluez4AdapterPrivate *priv;
+	gs_free_error GError *error = NULL;
 	GVariant *ret, *properties;
 	char **devices;
 	int i;
 
 	ret = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), result,
-	                                  G_VARIANT_TYPE ("(a{sv})"), &err);
+	                                  G_VARIANT_TYPE ("(a{sv})"), &error);
+
+	if (   !ret
+	    && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
+
+	self = NM_BLUEZ4_ADAPTER (user_data);
+	priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self);
+
+	g_clear_object (&priv->proxy_cancellable);
+
 	if (!ret) {
-		g_dbus_error_strip_remote_error (err);
-		nm_log_warn (LOGD_BT, "bluez error getting adapter properties: %s", err->message);
-		g_error_free (err);
+		g_dbus_error_strip_remote_error (error);
+		_LOGW ("bluez error getting adapter properties: %s", error->message);
 		goto done;
 	}
 
@@ -233,15 +249,43 @@ done:
 }
 
 static void
-query_properties (NMBluez4Adapter *self)
+_proxy_new_cb (GObject *source_object,
+               GAsyncResult *result,
+               gpointer user_data)
 {
-	NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self);
+	NMBluez4Adapter *self;
+	NMBluez4AdapterPrivate *priv;
+	gs_free_error GError *error = NULL;
+	GDBusProxy *proxy;
+
+	proxy = g_dbus_proxy_new_for_bus_finish (result, &error);
+	if (   !proxy
+	    && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
+
+	self = user_data;
+	priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self);
+
+	if (!proxy) {
+		_LOGW ("bluez error creating D-Bus proxy: %s", error->message);
+		g_clear_object (&priv->proxy_cancellable);
+		g_signal_emit (self, signals[INITIALIZED], 0, priv->initialized);
+		return;
+	}
+
+	priv->proxy = proxy;
+
+	_nm_dbus_signal_connect (priv->proxy, "DeviceCreated", G_VARIANT_TYPE ("(o)"),
+	                         G_CALLBACK (device_created), self);
+	_nm_dbus_signal_connect (priv->proxy, "DeviceRemoved", G_VARIANT_TYPE ("(o)"),
+	                         G_CALLBACK (device_removed), self);
 
 	g_dbus_proxy_call (priv->proxy, "GetProperties",
 	                   NULL,
 	                   G_DBUS_CALL_FLAGS_NONE, -1,
-	                   NULL,
-	                   get_properties_cb, self);
+	                   priv->proxy_cancellable,
+	                   get_properties_cb,
+	                   self);
 }
 
 /*****************************************************************************/
@@ -297,7 +341,7 @@ nm_bluez4_adapter_init (NMBluez4Adapter *self)
 {
 	NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self);
 
-	priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal,
+	priv->devices = g_hash_table_new_full (nm_str_hash, g_str_equal,
 	                                       NULL, NULL);
 }
 
@@ -316,19 +360,17 @@ nm_bluez4_adapter_new (const char *path, NMSettings *settings)
 
 	priv->settings = g_object_ref (settings);
 
-	priv->proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
-	                                             G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
-	                                             NULL,
-	                                             BLUEZ_SERVICE,
-	                                             priv->path,
-	                                             BLUEZ4_ADAPTER_INTERFACE,
-	                                             NULL, NULL);
-	_nm_dbus_signal_connect (priv->proxy, "DeviceCreated", G_VARIANT_TYPE ("(o)"),
-	                         G_CALLBACK (device_created), self);
-	_nm_dbus_signal_connect (priv->proxy, "DeviceRemoved", G_VARIANT_TYPE ("(o)"),
-	                         G_CALLBACK (device_removed), self);
+	priv->proxy_cancellable = g_cancellable_new ();
 
-	query_properties (self);
+	g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+	                          G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
+	                          NULL,
+	                          NM_BLUEZ_SERVICE,
+	                          priv->path,
+	                          NM_BLUEZ4_ADAPTER_INTERFACE,
+	                          priv->proxy_cancellable,
+	                          _proxy_new_cb,
+	                          self);
 	return self;
 }
 
@@ -339,21 +381,28 @@ dispose (GObject *object)
 	NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self);
 	NMBluezDevice *device;
 
+	nm_clear_g_cancellable (&priv->proxy_cancellable);
+
 	while ((device = g_hash_table_find (priv->devices, _find_all, NULL)))
 		device_do_remove (self, device);
 
+	if (priv->proxy) {
+		g_signal_handlers_disconnect_by_data (priv->proxy, self);
+		g_clear_object (&priv->proxy);
+	}
+
 	G_OBJECT_CLASS (nm_bluez4_adapter_parent_class)->dispose (object);
 }
 
 static void
 finalize (GObject *object)
 {
-	NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE ((NMBluez4Adapter *) object);
+	NMBluez4Adapter *self = NM_BLUEZ4_ADAPTER (object);
+	NMBluez4AdapterPrivate *priv = NM_BLUEZ4_ADAPTER_GET_PRIVATE (self);
 
 	g_hash_table_destroy (priv->devices);
 	g_free (priv->address);
 	g_free (priv->path);
-	g_object_unref (priv->proxy);
 
 	G_OBJECT_CLASS (nm_bluez4_adapter_parent_class)->finalize (object);
 
@@ -384,7 +433,7 @@ nm_bluez4_adapter_class_init (NMBluez4AdapterClass *config_class)
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
-	signals[INITIALIZED] = g_signal_new ("initialized",
+	signals[INITIALIZED] = g_signal_new (NM_BLUEZ4_ADAPTER_INITIALIZED,
 	                                     G_OBJECT_CLASS_TYPE (object_class),
 	                                     G_SIGNAL_RUN_LAST,
 	                                     0,
@@ -392,7 +441,7 @@ nm_bluez4_adapter_class_init (NMBluez4AdapterClass *config_class)
 	                                     g_cclosure_marshal_VOID__BOOLEAN,
 	                                     G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
 
-	signals[DEVICE_ADDED] = g_signal_new ("device-added",
+	signals[DEVICE_ADDED] = g_signal_new (NM_BLUEZ4_ADAPTER_DEVICE_ADDED,
 	                                      G_OBJECT_CLASS_TYPE (object_class),
 	                                      G_SIGNAL_RUN_LAST,
 	                                      0,
@@ -400,7 +449,7 @@ nm_bluez4_adapter_class_init (NMBluez4AdapterClass *config_class)
 	                                      g_cclosure_marshal_VOID__OBJECT,
 	                                      G_TYPE_NONE, 1, G_TYPE_OBJECT);
 
-	signals[DEVICE_REMOVED] = g_signal_new ("device-removed",
+	signals[DEVICE_REMOVED] = g_signal_new (NM_BLUEZ4_ADAPTER_DEVICE_REMOVED,
 	                                        G_OBJECT_CLASS_TYPE (object_class),
 	                                        G_SIGNAL_RUN_LAST,
 	                                        0,
diff --git a/src/devices/bluetooth/nm-bluez4-adapter.h b/src/devices/bluetooth/nm-bluez4-adapter.h
index e240ec2c..0aa4ff91 100644
--- a/src/devices/bluetooth/nm-bluez4-adapter.h
+++ b/src/devices/bluetooth/nm-bluez4-adapter.h
@@ -30,9 +30,15 @@
 #define NM_IS_BLUEZ4_ADAPTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_BLUEZ4_ADAPTER))
 #define NM_BLUEZ4_ADAPTER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_BLUEZ4_ADAPTER, NMBluez4AdapterClass))
 
+/* Properties */
 #define NM_BLUEZ4_ADAPTER_PATH    "path"
 #define NM_BLUEZ4_ADAPTER_ADDRESS "address"
 
+/* Signals */
+#define NM_BLUEZ4_ADAPTER_INITIALIZED      "initialized"
+#define NM_BLUEZ4_ADAPTER_DEVICE_ADDED     "device-added"
+#define NM_BLUEZ4_ADAPTER_DEVICE_REMOVED   "device-removed"
+
 typedef struct _NMBluez4Adapter NMBluez4Adapter;
 typedef struct _NMBluez4AdapterClass NMBluez4AdapterClass;
 
diff --git a/src/devices/bluetooth/nm-bluez4-manager.c b/src/devices/bluetooth/nm-bluez4-manager.c
index a9079a2f..1fe02f18 100644
--- a/src/devices/bluetooth/nm-bluez4-manager.c
+++ b/src/devices/bluetooth/nm-bluez4-manager.c
@@ -47,6 +47,7 @@ typedef struct {
 	NMSettings *settings;
 
 	GDBusProxy *proxy;
+	GCancellable *proxy_cancellable;
 
 	NMBluez4Adapter *adapter;
 } NMBluez4ManagerPrivate;
@@ -66,6 +67,11 @@ G_DEFINE_TYPE (NMBluez4Manager, nm_bluez4_manager, G_TYPE_OBJECT)
 
 /*****************************************************************************/
 
+#define _NMLOG_DOMAIN      LOGD_BT
+#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "bluez4-manager", __VA_ARGS__)
+
+/*****************************************************************************/
+
 static void
 emit_bdaddr_added (NMBluez4Manager *self, NMBluezDevice *device)
 {
@@ -119,8 +125,10 @@ adapter_initialized (NMBluez4Adapter *adapter, gboolean success, gpointer user_d
 			emit_bdaddr_added (self, NM_BLUEZ_DEVICE (iter->data));
 		g_slist_free (devices);
 
-		g_signal_connect (adapter, "device-added", G_CALLBACK (device_added), self);
-		g_signal_connect (adapter, "device-removed", G_CALLBACK (device_removed), self);
+		g_signal_connect (adapter, NM_BLUEZ4_ADAPTER_DEVICE_ADDED,
+		                  G_CALLBACK (device_added), self);
+		g_signal_connect (adapter, NM_BLUEZ4_ADAPTER_DEVICE_REMOVED,
+		                  G_CALLBACK (device_removed), self);
 	} else {
 		g_object_unref (priv->adapter);
 		priv->adapter = NULL;
@@ -169,49 +177,70 @@ default_adapter_changed (GDBusProxy *proxy, const char *path, NMBluez4Manager *s
 	/* Add the new default adapter */
 	if (path) {
 		priv->adapter = nm_bluez4_adapter_new (path, priv->settings);
-		g_signal_connect (priv->adapter, "initialized", G_CALLBACK (adapter_initialized), self);
+		g_signal_connect (priv->adapter, NM_BLUEZ4_ADAPTER_INITIALIZED,
+		                  G_CALLBACK (adapter_initialized), self);
 	}
 }
 
 static void
 default_adapter_cb (GObject *proxy, GAsyncResult *result, gpointer user_data)
 {
-	NMBluez4Manager *self = NM_BLUEZ4_MANAGER (user_data);
-	NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
-	GVariant *ret;
-	GError *err = NULL;
+	NMBluez4Manager *self;
+	NMBluez4ManagerPrivate *priv;
+	gs_unref_variant GVariant *ret = NULL;
+	gs_free_error GError *error = NULL;
+	const char *default_adapter;
 
 	ret = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), result,
-	                                  G_VARIANT_TYPE ("(o)"), &err);
-	if (ret) {
-		const char *default_adapter;
+	                                  G_VARIANT_TYPE ("(o)"), &error);
+	if (   !ret
+	    && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
 
-		g_variant_get (ret, "(&o)", &default_adapter);
-		default_adapter_changed (priv->proxy, default_adapter, self);
-		g_variant_unref (ret);
-	} else {
+	self = NM_BLUEZ4_MANAGER (user_data);
+	priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
+
+	g_clear_object (&priv->proxy_cancellable);
+
+	if (!ret) {
 		/* Ignore "No such adapter" errors; just means bluetooth isn't active */
-		if (   !_nm_dbus_error_has_name (err, "org.bluez.Error.NoSuchAdapter")
-		    && !_nm_dbus_error_has_name (err, "org.freedesktop.systemd1.LoadFailed")
-		    && !g_error_matches (err, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)) {
-			g_dbus_error_strip_remote_error (err);
-			nm_log_warn (LOGD_BT, "bluez error getting default adapter: %s",
-			             err->message);
+		if (   !_nm_dbus_error_has_name (error, "org.bluez.Error.NoSuchAdapter")
+		    && !_nm_dbus_error_has_name (error, "org.freedesktop.systemd1.LoadFailed")
+		    && !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)) {
+			g_dbus_error_strip_remote_error (error);
+			_LOGW ("bluez error getting default adapter: %s",
+			       error->message);
 		}
-		g_error_free (err);
+		return;
 	}
+
+	g_variant_get (ret, "(&o)", &default_adapter);
+	default_adapter_changed (priv->proxy, default_adapter, self);
 }
 
 static void
-query_default_adapter (NMBluez4Manager *self)
+name_owner_changed (NMBluez4Manager *self)
 {
 	NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
+	gs_free char *owner = NULL;
+
+	nm_clear_g_cancellable (&priv->proxy_cancellable);
+
+	owner = g_dbus_proxy_get_name_owner (priv->proxy);
+	if (!owner) {
+		/* Throwing away the adapter removes all devices too */
+		g_clear_object (&priv->adapter);
+		return;
+	}
+
+	priv->proxy_cancellable = g_cancellable_new ();
 
 	g_dbus_proxy_call (priv->proxy, "DefaultAdapter",
 	                   NULL,
 	                   G_DBUS_CALL_FLAGS_NONE, -1,
-	                   NULL,
-	                   default_adapter_cb, self);
+	                   priv->proxy_cancellable,
+	                   default_adapter_cb,
+	                   self);
 }
 
 static void
@@ -219,34 +248,35 @@ name_owner_changed_cb (GObject *object,
                        GParamSpec *pspec,
                        gpointer user_data)
 {
-	NMBluez4Manager *self = NM_BLUEZ4_MANAGER (user_data);
-	NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
-	char *owner;
-
-	owner = g_dbus_proxy_get_name_owner (priv->proxy);
-	if (owner) {
-		query_default_adapter (self);
-		g_free (owner);
-	} else {
-		/* Throwing away the adapter removes all devices too */
-		g_clear_object (&priv->adapter);
-	}
+	name_owner_changed (user_data);
 }
 
-/*****************************************************************************/
-
 static void
-nm_bluez4_manager_init (NMBluez4Manager *self)
+_proxy_new_cb (GObject *source_object,
+               GAsyncResult *result,
+               gpointer user_data)
 {
-	NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
+	NMBluez4Manager *self;
+	NMBluez4ManagerPrivate *priv;
+	gs_free_error GError *error = NULL;
+	GDBusProxy *proxy;
+
+	proxy = g_dbus_proxy_new_for_bus_finish (result, &error);
+	if (   !proxy
+	    && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
+
+	self = user_data;
+	priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
+
+	if (!proxy) {
+		_LOGW ("bluez error creating D-Bus proxy: %s", error->message);
+		g_clear_object (&priv->proxy_cancellable);
+		return;
+	}
+
+	priv->proxy = proxy;
 
-	priv->proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
-	                                             G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
-	                                             NULL,
-	                                             BLUEZ_SERVICE,
-	                                             BLUEZ_MANAGER_PATH,
-	                                             BLUEZ4_MANAGER_INTERFACE,
-	                                             NULL, NULL);
 	_nm_dbus_signal_connect (priv->proxy, "AdapterRemoved", G_VARIANT_TYPE ("(o)"),
 	                         G_CALLBACK (adapter_removed), self);
 	_nm_dbus_signal_connect (priv->proxy, "DefaultAdapterChanged", G_VARIANT_TYPE ("(o)"),
@@ -254,7 +284,27 @@ nm_bluez4_manager_init (NMBluez4Manager *self)
 	g_signal_connect (priv->proxy, "notify::g-name-owner",
 	                  G_CALLBACK (name_owner_changed_cb), self);
 
-	query_default_adapter (self);
+	name_owner_changed (self);
+}
+
+/*****************************************************************************/
+
+static void
+nm_bluez4_manager_init (NMBluez4Manager *self)
+{
+	NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
+
+	priv->proxy_cancellable = g_cancellable_new ();
+
+	g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+	                          G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
+	                          NULL,
+	                          NM_BLUEZ_SERVICE,
+	                          NM_BLUEZ_MANAGER_PATH,
+	                          NM_BLUEZ4_MANAGER_INTERFACE,
+	                          priv->proxy_cancellable,
+	                          _proxy_new_cb,
+	                          self);
 }
 
 NMBluez4Manager *
@@ -275,7 +325,13 @@ dispose (GObject *object)
 	NMBluez4Manager *self = NM_BLUEZ4_MANAGER (object);
 	NMBluez4ManagerPrivate *priv = NM_BLUEZ4_MANAGER_GET_PRIVATE (self);
 
-	g_clear_object (&priv->proxy);
+	nm_clear_g_cancellable (&priv->proxy_cancellable);
+
+	if (priv->proxy) {
+		g_signal_handlers_disconnect_by_data (priv->proxy, self);
+		g_clear_object (&priv->proxy);
+	}
+
 	g_clear_object (&priv->adapter);
 
 	G_OBJECT_CLASS (nm_bluez4_manager_parent_class)->dispose (object);
diff --git a/src/devices/bluetooth/nm-bluez5-dun.c b/src/devices/bluetooth/nm-bluez5-dun.c
index aba3a0dd..ca09b276 100644
--- a/src/devices/bluetooth/nm-bluez5-dun.c
+++ b/src/devices/bluetooth/nm-bluez5-dun.c
@@ -386,11 +386,11 @@ nm_bluez5_dun_cleanup (NMBluez5DunContext *context)
 			ioctl (context->rfcomm_fd, RFCOMMRELEASEDEV, &req);
 			context->rfcomm_id = -1;
 		}
-		close (context->rfcomm_fd);
+		nm_close (context->rfcomm_fd);
 		context->rfcomm_fd = -1;
 	}
 
-	close (context->rfcomm_tty_fd);
+	nm_close (context->rfcomm_tty_fd);
 	context->rfcomm_tty_fd = -1;
 }
 
diff --git a/src/devices/bluetooth/nm-bluez5-manager.c b/src/devices/bluetooth/nm-bluez5-manager.c
index 88759301..8c93f2a5 100644
--- a/src/devices/bluetooth/nm-bluez5-manager.c
+++ b/src/devices/bluetooth/nm-bluez5-manager.c
@@ -16,7 +16,7 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
  * Copyright (C) 2007 - 2008 Novell, Inc.
- * Copyright (C) 2007 - 2013 Red Hat, Inc.
+ * Copyright (C) 2007 - 2017 Red Hat, Inc.
  * Copyright (C) 2013 Intel Corporation.
  */
 
@@ -30,14 +30,17 @@
 
 #include "nm-core-internal.h"
 
+#include "nm-utils/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,
 };
 
@@ -49,10 +52,13 @@ typedef struct {
 	GDBusProxy *proxy;
 
 	GHashTable *devices;
+
+	CList network_servers;
 } NMBluez5ManagerPrivate;
 
 struct _NMBluez5Manager {
 	GObject parent;
+	NMBtVTableNetworkServer network_server_vtable;
 	NMBluez5ManagerPrivate _priv;
 };
 
@@ -64,6 +70,15 @@ 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);
@@ -71,6 +86,182 @@ static void device_usable (NMBluezDevice *device, GParamSpec *pspec, NMBluez5Man
 
 /*****************************************************************************/
 
+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 (&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 gchar *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 gchar *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)
 {
@@ -125,14 +316,14 @@ device_usable (NMBluezDevice *device, GParamSpec *pspec, NMBluez5Manager *self)
 {
 	gboolean usable = nm_bluez_device_get_usable (device);
 
-	nm_log_dbg (LOGD_BT, "(%s): bluez device now %s",
-	            nm_bluez_device_get_path (device),
-	            usable ? "usable" : "unusable");
+	_LOGD ("(%s): bluez device now %s",
+	       nm_bluez_device_get_path (device),
+	       usable ? "usable" : "unusable");
 
 	if (usable) {
-		nm_log_dbg (LOGD_BT, "(%s): bluez device address %s",
-				    nm_bluez_device_get_path (device),
-				    nm_bluez_device_get_address (device));
+		_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);
@@ -143,9 +334,9 @@ device_initialized (NMBluezDevice *device, gboolean success, NMBluez5Manager *se
 {
 	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
 
-	nm_log_dbg (LOGD_BT, "(%s): bluez device %s",
-	            nm_bluez_device_get_path (device),
-	            success ? "initialized" : "failed to initialize");
+	_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));
 }
@@ -157,11 +348,11 @@ device_added (GDBusProxy *proxy, const gchar *path, NMBluez5Manager *self)
 	NMBluezDevice *device;
 
 	device = nm_bluez_device_new (path, NULL, priv->settings, 5);
-	g_signal_connect (device, "initialized", G_CALLBACK (device_initialized), self);
-	g_signal_connect (device, "notify::usable", G_CALLBACK (device_usable), self);
+	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);
 
-	nm_log_dbg (LOGD_BT, "(%s): new bluez device found", path);
+	_LOGD ("(%s): new bluez device found", path);
 }
 
 static void
@@ -170,7 +361,7 @@ device_removed (GDBusProxy *proxy, const gchar *path, NMBluez5Manager *self)
 	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
 	NMBluezDevice *device;
 
-	nm_log_dbg (LOGD_BT, "(%s): bluez device removed", path);
+	_LOGD ("(%s): bluez device removed", path);
 
 	device = g_hash_table_lookup (priv->devices, path);
 	if (device) {
@@ -186,8 +377,16 @@ object_manager_interfaces_added (GDBusProxy      *proxy,
                                  GVariant        *dict,
                                  NMBluez5Manager *self)
 {
-	if (g_variant_lookup (dict, BLUEZ5_DEVICE_INTERFACE, "a{sv}", NULL))
+	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
@@ -196,8 +395,10 @@ object_manager_interfaces_removed (GDBusProxy       *proxy,
                                    const char      **ifaces,
                                    NMBluez5Manager  *self)
 {
-	if (ifaces && g_strv_contains (ifaces, BLUEZ5_DEVICE_INTERFACE))
+	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
@@ -215,20 +416,17 @@ get_managed_objects_cb (GDBusProxy *proxy,
 	                                      &error);
 	if (!variant) {
 		if (g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD))
-			nm_log_warn (LOGD_BT, "Couldn't get managed objects: not running Bluez5?");
+			_LOGW ("Couldn't get managed objects: not running Bluez5?");
 		else {
 			g_dbus_error_strip_remote_error (error);
-			nm_log_warn (LOGD_BT, "Couldn't get managed objects: %s", error->message);
+			_LOGW ("Couldn't get managed objects: %s", error->message);
 		}
 		g_clear_error (&error);
 		return;
 	}
 	g_variant_iter_init (&i, g_variant_get_child_value (variant, 0));
 	while ((g_variant_iter_next (&i, "{&o*}", &path, &ifaces))) {
-		if (g_variant_lookup_value (ifaces, BLUEZ5_DEVICE_INTERFACE,
-		                            G_VARIANT_TYPE_DICTIONARY)) {
-			device_added (proxy, path, self);
-		}
+		object_manager_interfaces_added (proxy, path, ifaces, self);
 		g_variant_unref (ifaces);
 	}
 
@@ -248,7 +446,7 @@ on_proxy_acquired (GObject *object,
 	priv->proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
 
 	if (!priv->proxy) {
-		nm_log_warn (LOGD_BT, "Couldn't acquire object manager proxy: %s", error->message);
+		_LOGW ("Couldn't acquire object manager proxy: %s", error->message);
 		g_clear_error (&error);
 		return;
 	}
@@ -281,9 +479,9 @@ bluez_connect (NMBluez5Manager *self)
 	g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
 	                          G_DBUS_PROXY_FLAGS_NONE,
 	                          NULL,
-	                          BLUEZ_SERVICE,
-	                          BLUEZ_MANAGER_PATH,
-	                          OBJECT_MANAGER_INTERFACE,
+	                          NM_BLUEZ_SERVICE,
+	                          NM_BLUEZ_MANAGER_PATH,
+	                          NM_OBJECT_MANAGER_INTERFACE,
 	                          NULL,
 	                          (GAsyncReadyCallback) on_proxy_acquired,
 	                          self);
@@ -306,33 +504,26 @@ name_owner_changed_cb (GObject *object,
 	}
 }
 
-static void
-bluez_cleanup (NMBluez5Manager *self, gboolean do_signal)
-{
-	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
-
-	if (priv->proxy) {
-		g_signal_handlers_disconnect_by_func (priv->proxy, G_CALLBACK (name_owner_changed_cb), self);
-		g_clear_object (&priv->proxy);
-	}
-
-	if (do_signal)
-		remove_all_devices (self);
-	else
-		g_hash_table_remove_all (priv->devices);
-}
-
 /*****************************************************************************/
 
 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 (g_str_hash, g_str_equal,
+	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 *
@@ -351,8 +542,18 @@ static void
 dispose (GObject *object)
 {
 	NMBluez5Manager *self = NM_BLUEZ5_MANAGER (object);
+	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
+	CList *iter, *safe;
 
-	bluez_cleanup (self, FALSE);
+	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);
 }
@@ -360,7 +561,8 @@ dispose (GObject *object)
 static void
 finalize (GObject *object)
 {
-	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE ((NMBluez5Manager *) object);
+	NMBluez5Manager *self = NM_BLUEZ5_MANAGER (object);
+	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
 
 	g_hash_table_destroy (priv->devices);
 
@@ -384,4 +586,11 @@ nm_bluez5_manager_class_init (NMBluez5ManagerClass *klass)
 	                  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-device-bt.c b/src/devices/bluetooth/nm-device-bt.c
index 4ee71489..0d46be8f 100644
--- a/src/devices/bluetooth/nm-device-bt.c
+++ b/src/devices/bluetooth/nm-device-bt.c
@@ -40,15 +40,14 @@
 #include "nm-bt-error.h"
 #include "platform/nm-platform.h"
 
+#include "devices/wwan/nm-modem-manager.h"
+#include "devices/wwan/nm-modem.h"
+
 #include "introspection/org.freedesktop.NetworkManager.Device.Bluetooth.h"
 
 #include "devices/nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceBt);
 
-#define MM_DBUS_SERVICE   "org.freedesktop.ModemManager1"
-#define MM_DBUS_PATH      "/org/freedesktop/ModemManager1"
-#define MM_DBUS_INTERFACE "org.freedesktop.ModemManager1"
-
 /*****************************************************************************/
 
 NM_GOBJECT_PROPERTIES_DEFINE_BASE (
@@ -65,7 +64,8 @@ enum {
 static guint signals[LAST_SIGNAL] = { 0 };
 
 typedef struct {
-	GDBusProxy *mm_proxy;
+	NMModemManager *modem_manager;
+
 	gboolean mm_running;
 
 	NMBluezDevice *bt_device;
@@ -647,7 +647,7 @@ component_added (NMDevice *device, GObject *component)
 	NMDeviceState state;
 	NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE;
 
-	if (!NM_IS_MODEM (component))
+	if (!component || !NM_IS_MODEM (component))
 		return FALSE;
 	modem = NM_MODEM (component);
 
@@ -967,9 +967,12 @@ is_available (NMDevice *dev, NMDeviceCheckDevAvailableFlags flags)
 }
 
 static void
-set_mm_running (NMDeviceBt *self, gboolean running)
+set_mm_running (NMDeviceBt *self)
 {
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
+	gboolean running;
+
+	running = (nm_modem_manager_name_owner_get (priv->modem_manager) != NULL);
 
 	if (priv->mm_running != running) {
 		_LOGD (LOGD_BT, "ModemManager now %s",
@@ -983,18 +986,11 @@ set_mm_running (NMDeviceBt *self, gboolean running)
 }
 
 static void
-mm_name_owner_changed (GObject *object,
-                       GParamSpec *pspec,
-                       NMDeviceBt *self)
+mm_name_owner_changed_cb (GObject *object,
+                          GParamSpec *pspec,
+                          gpointer user_data)
 {
-	char *owner;
-
-	owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (object));
-	if (owner) {
-		set_mm_running (self, TRUE);
-		g_free (owner);
-	} else
-		set_mm_running (self, FALSE);
+	set_mm_running (user_data);
 }
 
 /*****************************************************************************/
@@ -1039,7 +1035,8 @@ set_property (GObject *object, guint prop_id,
 	case PROP_BT_DEVICE:
 		/* construct-only */
 		priv->bt_device = g_value_dup_object (value);
-		g_signal_connect (priv->bt_device, "removed", G_CALLBACK (bluez_device_removed), object);
+		if (!priv->bt_device)
+			g_return_if_reached ();
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -1052,46 +1049,42 @@ set_property (GObject *object, guint prop_id,
 static void
 nm_device_bt_init (NMDeviceBt *self)
 {
-	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
-	GError *error = NULL;
-
-	priv->mm_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
-	                                                G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
-	                                                    G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
-	                                                    G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
-	                                                NULL,
-	                                                MM_DBUS_SERVICE,
-	                                                MM_DBUS_PATH,
-	                                                MM_DBUS_INTERFACE,
-	                                                NULL, &error);
-	if (priv->mm_proxy) {
-		g_signal_connect (priv->mm_proxy, "notify::g-name-owner",
-		                  G_CALLBACK (mm_name_owner_changed),
-		                  self);
-		mm_name_owner_changed (G_OBJECT (priv->mm_proxy), NULL, self);
-	} else {
-		_LOGW (LOGD_MB, "Could not create proxy for '%s': %s",
-		       MM_DBUS_SERVICE, error->message);
-		g_clear_error (&error);
-	}
 }
 
 static void
 constructed (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);
 	const char *my_hwaddr;
 
 	G_OBJECT_CLASS (nm_device_bt_parent_class)->constructed (object);
 
+	priv->modem_manager = g_object_ref (nm_modem_manager_get ());
+
+	nm_modem_manager_name_owner_ref (priv->modem_manager);
+
+	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));
-	g_assert (my_hwaddr);
-	priv->bdaddr = g_strdup (my_hwaddr);
+	if (my_hwaddr)
+		priv->bdaddr = g_strdup (my_hwaddr);
+	else
+		g_warn_if_reached ();
 
-	/* Watch for BT device property changes */
-	g_signal_connect (priv->bt_device, "notify::" NM_BLUEZ_DEVICE_CONNECTED,
-	                  G_CALLBACK (bluez_connected_changed),
-	                  object);
+	set_mm_running (self);
 }
 
 NMDevice *
@@ -1129,9 +1122,10 @@ dispose (GObject *object)
 
 	g_signal_handlers_disconnect_matched (priv->bt_device, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, object);
 
-	if (priv->mm_proxy) {
-		g_signal_handlers_disconnect_by_func (priv->mm_proxy, G_CALLBACK (mm_name_owner_changed), object);
-		g_clear_object (&priv->mm_proxy);
+	if (priv->modem_manager) {
+		g_signal_handlers_disconnect_by_func (priv->modem_manager, G_CALLBACK (mm_name_owner_changed_cb), object);
+		nm_modem_manager_name_owner_unref (priv->modem_manager);
+		g_clear_object (&priv->modem_manager);
 	}
 
 	modem_cleanup (NM_DEVICE_BT (object));
diff --git a/src/devices/bluetooth/nm-device-bt.h b/src/devices/bluetooth/nm-device-bt.h
index 9bcf6ca8..b90dbd2a 100644
--- a/src/devices/bluetooth/nm-device-bt.h
+++ b/src/devices/bluetooth/nm-device-bt.h
@@ -24,8 +24,6 @@
 #include "devices/nm-device.h"
 #include "nm-bluez-device.h"
 
-#include "devices/wwan/nm-modem.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))
 #define NM_DEVICE_BT_CLASS(klass)           (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_BT, NMDeviceBtClass))
@@ -52,8 +50,10 @@ NMDevice *nm_device_bt_new (NMBluezDevice *bt_device,
 
 guint32 nm_device_bt_get_capabilities (NMDeviceBt *device);
 
+struct _NMModem;
+
 gboolean nm_device_bt_modem_added (NMDeviceBt *device,
-                                   NMModem *modem,
+                                   struct _NMModem *modem,
                                    const char *driver);
 
 #endif /* __NETWORKMANAGER_DEVICE_BT_H__ */