[network-manager-applet/cc-bt: 1/3] bluetooth: fix GNOME Bluetooth plugin when used from the control center



commit 8228bf2bb046a9b5e27506f78d94ba136a44612e
Author: Dan Williams <dcbw redhat com>
Date:   Tue Aug 7 21:32:28 2012 -0500

    bluetooth: fix GNOME Bluetooth plugin when used from the control center
    
    Since the control center added support for showing the "extra configuration
    widgets" (gnome-bluetooth commit 3b5d7dee45cf8b9c8ce91e65aa9004f784a29b34)
    the NM BT plugin has been broken for DUN.  During the DUN setup process,
    the plugin calls bluez to connect to the phone to start the rfcomm port,
    which changes the bluez device's properties.  That triggers a call to
    cc_bluetooth_panel_update_properties(), which removes all the extra
    configuration widgets and re-adds them.  Unfortunately, that caused all
    the NM BT plugin's data to be freed, while the DUN inspect operation
    was ongoing, eventually resulting in access of freed data when various
    operations finally completed.
    
    To fix that, break out the actual work code into a model, called
    NmaBtDevice, that lives longer than the GtkWidget that's embedded in
    the control panel, and that handles all the necessary PAN/DUN tasks.
    The panel widget is then reworked to simply listen to device signals
    so that it can die and a new instance be created, while the work goes
    on in the background in NmaBtDevice.

 po/POTFILES.in                      |    1 +
 src/gnome-bluetooth/Makefile.am     |    5 +-
 src/gnome-bluetooth/bt-widget.c     | 1192 ++++++++---------------------------
 src/gnome-bluetooth/nma-bt-device.c | 1176 ++++++++++++++++++++++++++++++++++
 src/gnome-bluetooth/nma-bt-device.h |   84 +++
 5 files changed, 1532 insertions(+), 926 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 8657908..ab32404 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -48,6 +48,7 @@ src/connection-editor/page-wireless.c
 src/connection-editor/page-wireless-security.c
 src/connection-editor/vpn-helpers.c
 src/gnome-bluetooth/bt-widget.c
+src/gnome-bluetooth/nma-bt-device.c
 [type: gettext/glade]src/gsm-unlock.ui
 [type: gettext/glade]src/info.ui
 src/libnm-gtk/nm-mobile-wizard.c
diff --git a/src/gnome-bluetooth/Makefile.am b/src/gnome-bluetooth/Makefile.am
index b5f93f0..6679589 100644
--- a/src/gnome-bluetooth/Makefile.am
+++ b/src/gnome-bluetooth/Makefile.am
@@ -10,7 +10,10 @@ INCLUDES = \
 	$(DISABLE_DEPRECATED) \
 	$(WARN_CFLAGS)
 
-BT_WIDGET_SOURCES = bt-widget.c
+BT_WIDGET_SOURCES = \
+	bt-widget.c \
+	nma-bt-device.c \
+	nma-bt-device.h
 
 if HAVE_GBT
 plugindir = $(libdir)/gnome-bluetooth/plugins
diff --git a/src/gnome-bluetooth/bt-widget.c b/src/gnome-bluetooth/bt-widget.c
index 4de3f84..6757fc2 100644
--- a/src/gnome-bluetooth/bt-widget.c
+++ b/src/gnome-bluetooth/bt-widget.c
@@ -2,9 +2,6 @@
 /*
  *  NetworkManager Applet
  *
- *  Copyright (C) 2009  Bastien Nocera <hadess hadess net>
- *  Copyright (C) 2009 - 2010  Dan Williams <dcbw redhat com>
- *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Lesser General Public
  *  License as published by the Free Software Foundation; either
@@ -19,7 +16,7 @@
  *  License along with this library; if not, write to the Free Software
  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  *
- * (C) Copyright 2009 - 2011 Red Hat, Inc.
+ * (C) Copyright 2009 - 2012 Red Hat, Inc.
  *
  */
 
@@ -32,73 +29,65 @@
 #include <glib/gi18n-lib.h>
 
 #include <gtk/gtk.h>
+#include <dbus/dbus.h>
 #include <bluetooth-plugin.h>
 #include <bluetooth-client.h>
-#include <nm-setting-connection.h>
-#include <nm-setting-bluetooth.h>
-#include <nm-setting-ip4-config.h>
-#include <nm-setting-cdma.h>
-#include <nm-setting-gsm.h>
-#include <nm-setting-serial.h>
-#include <nm-setting-ppp.h>
-#include <nm-utils.h>
 #include <nm-remote-settings.h>
 #include <nm-remote-connection.h>
 
-#include <dbus/dbus.h>
-#include <dbus/dbus-glib.h>
-
-#include "nma-marshal.h"
-#include "nm-mobile-wizard.h"
-
-#define DBUS_TYPE_G_MAP_OF_VARIANT (dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE))
-
-#define BLUEZ_SERVICE           "org.bluez"
-#define BLUEZ_MANAGER_PATH      "/"
-#define BLUEZ_MANAGER_INTERFACE "org.bluez.Manager"
-#define BLUEZ_ADAPTER_INTERFACE "org.bluez.Adapter"
-#define BLUEZ_DEVICE_INTERFACE  "org.bluez.Device"
-#define BLUEZ_SERIAL_INTERFACE  "org.bluez.Serial"
-#define BLUEZ_NETWORK_INTERFACE "org.bluez.Network"
-
-#define MM_SERVICE         "org.freedesktop.ModemManager"
-#define MM_PATH            "/org/freedesktop/ModemManager"
-#define MM_INTERFACE       "org.freedesktop.ModemManager"
-#define MM_MODEM_INTERFACE "org.freedesktop.ModemManager.Modem"
+#include "nma-bt-device.h"
 
 typedef struct {
-	NMRemoteSettings *settings;
-	char *bdaddr;
+	NmaBtDevice *device;
 	BluetoothClient *btclient;
-	GtkTreeModel *btmodel;
 
-	gboolean pan;
+	GSList *sigids;
+
 	GtkWidget *pan_button;
 	guint pan_toggled_id;
-	NMRemoteConnection *pan_connection;
 
-	gboolean dun;
 	GtkWidget *dun_button;
 	guint dun_toggled_id;
-	NMRemoteConnection *dun_connection;
+	gboolean powered;
 
 	GtkWidget *hbox;
-	GtkWidget *label;
+	GtkWidget *status;
 	GtkWidget *spinner;
+} WidgetInfo;
 
-	/* DUN stuff */
-	DBusGConnection *bus;
-	DBusGProxy *dun_proxy;
+/***************************************************************/
+
+static GHashTable *devices = NULL;
+
+static NmaBtDevice *
+get_device (const char *bdaddr)
+{
+	if (G_UNLIKELY (devices == NULL))
+		devices = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
+
+	return g_hash_table_lookup (devices, bdaddr);
+}
+
+static void
+add_device (NmaBtDevice *device)
+{
+	const char *bdaddr = nma_bt_device_get_bdaddr (device);
+
+	if (get_device (bdaddr)) {
+		g_warning ("%s already exists in the device table!", bdaddr);
+		return;
+	}
 
-	DBusGProxy *mm_proxy;
-	GSList *modem_proxies;
+	g_hash_table_insert (devices, (gpointer) bdaddr, device);
+}
 
-	char *rfcomm_iface;
-	guint dun_timeout_id;
+static void
+remove_device (NmaBtDevice *device)
+{
+	g_hash_table_remove (devices, device);
+}
 
-	NMAMobileWizard *wizard;
-	GtkWindowGroup *window_group;
-} PluginInfo;
+/***************************************************************/
 
 static void
 get_capabilities (const char *bdaddr,
@@ -133,21 +122,7 @@ has_config_widget (const char *bdaddr, const char **uuids)
 	return pan || dun;
 }
 
-static GByteArray *
-get_array_from_bdaddr (const char *str)
-{
-	struct ether_addr *addr;
-	GByteArray *array;
-
-	addr = ether_aton (str);
-	if (addr) {
-		array = g_byte_array_sized_new (ETH_ALEN);
-		g_byte_array_append (array, (const guint8 *) addr->ether_addr_octet, ETH_ALEN);
-		return array;
-	}
-
-	return NULL;
-}
+/***************************************************************/
 
 static gboolean
 get_device_iter (GtkTreeModel *model, const char *bdaddr, GtkTreeIter *out_iter)
@@ -190,885 +165,187 @@ get_device_iter (GtkTreeModel *model, const char *bdaddr, GtkTreeIter *out_iter)
 /*******************************************************************/
 
 static void
-pan_cleanup (PluginInfo *info, const char *message, gboolean uncheck)
-{
-	if (info->spinner) {
-		gtk_spinner_stop (GTK_SPINNER (info->spinner));
-		gtk_widget_hide (info->spinner);
-	}
-
-	gtk_label_set_text (GTK_LABEL (info->label), message);
-	gtk_widget_set_sensitive (info->pan_button, TRUE);
-
-	if (uncheck) {
-		g_signal_handler_block (info->pan_button, info->pan_toggled_id);
-		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (info->pan_button), FALSE);
-		g_signal_handler_unblock (info->pan_button, info->pan_toggled_id);
-	}
-}
-
-static void
-pan_add_cb (NMRemoteSettings *settings,
-            NMRemoteConnection *connection,
-            GError *error,
-            gpointer user_data)
+pan_button_toggled (GtkToggleButton *button, WidgetInfo *info)
 {
-	PluginInfo *info = user_data;
-	char *message;
-
-	if (error) {
-		message = g_strdup_printf (_("Failed to create PAN connection: %s"), error->message);
-		pan_cleanup (info, message, TRUE);
-		g_free (message);
-	} else {
-		info->pan_connection = connection;
-		pan_cleanup (info, _("Your phone is now ready to use!"), FALSE);
-	}
-}
-
-static void
-add_pan_connection (PluginInfo *info)
-{
-	NMConnection *connection;
-	NMSetting *setting, *bt_setting, *ip_setting;
-	GByteArray *mac;
-	char *id, *uuid, *alias = NULL;
-	GtkTreeIter iter;
-
-	mac = get_array_from_bdaddr (info->bdaddr);
-	g_assert (mac);
-
-	if (get_device_iter (info->btmodel, info->bdaddr, &iter))
-		gtk_tree_model_get (info->btmodel, &iter, BLUETOOTH_COLUMN_ALIAS, &alias, -1);
-
-	/* The connection */
-	connection = nm_connection_new ();
-
-	/* The connection settings */
-	setting = nm_setting_connection_new ();
-	id = g_strdup_printf (_("%s Network"), alias ? alias : info->bdaddr);
-	uuid = nm_utils_uuid_generate ();
-	g_object_set (G_OBJECT (setting),
-	              NM_SETTING_CONNECTION_ID, id,
-	              NM_SETTING_CONNECTION_UUID, uuid,
-	              NM_SETTING_CONNECTION_TYPE, NM_SETTING_BLUETOOTH_SETTING_NAME,
-	              NM_SETTING_CONNECTION_AUTOCONNECT, FALSE,
-	              NULL);
-	g_free (id);
-	g_free (uuid);
-	nm_connection_add_setting (connection, setting);
-
-	/* The Bluetooth settings */
-	bt_setting = nm_setting_bluetooth_new ();
-	g_object_set (G_OBJECT (bt_setting),
-	              NM_SETTING_BLUETOOTH_BDADDR, mac,
-	              NM_SETTING_BLUETOOTH_TYPE, NM_SETTING_BLUETOOTH_TYPE_PANU,
-	              NULL);
-	nm_connection_add_setting (connection, bt_setting);
-
-	/* The IPv4 settings */
-	ip_setting = nm_setting_ip4_config_new ();
-	g_object_set (G_OBJECT (ip_setting),
-	              NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO,
-	              NULL);
-	nm_connection_add_setting (connection, ip_setting);
-
-	/* Add the connection to the settings service */
-	nm_remote_settings_add_connection (info->settings,
-	                                   connection,
-	                                   pan_add_cb,
-	                                   info);
-
-	g_byte_array_free (mac, TRUE);
-	g_free (alias);
+	nma_bt_device_set_pan_enabled (info->device, gtk_toggle_button_get_active (button));
 }
 
 static void
-pan_start (PluginInfo *info)
+dun_button_toggled (GtkToggleButton *button, WidgetInfo *info)
 {
-	/* Start the spinner */
-	if (!info->spinner) {
-		info->spinner = gtk_spinner_new ();
-		gtk_box_pack_start (GTK_BOX (info->hbox), info->spinner, FALSE, FALSE, 6);
-	}
-	gtk_spinner_start (GTK_SPINNER (info->spinner));
-	gtk_widget_show_all (info->hbox);
+	GtkWidget *parent;
 
-	gtk_widget_set_sensitive (info->pan_button, FALSE);
+	/* Update the toplevel for the mobile wizard now that the widget is
+	 * realized.
+	 */
+	parent = gtk_widget_get_toplevel (info->hbox);
+	if (gtk_widget_is_toplevel (parent))
+		nma_bt_device_set_parent_window (info->device, GTK_WINDOW (parent));
 
-	add_pan_connection (info);
+	nma_bt_device_set_dun_enabled (info->device, gtk_toggle_button_get_active (button));
 }
 
-/*******************************************************************/
-
-static void dun_property_changed (DBusGProxy *proxy,
-                                  const char *property,
-                                  GValue *value,
-                                  gpointer user_data);
-
 static void
-dun_cleanup (PluginInfo *info, const char *message, gboolean uncheck)
+widget_info_destroy (gpointer data)
 {
+	WidgetInfo *info = data;
 	GSList *iter;
 
-	for (iter = info->modem_proxies; iter; iter = g_slist_next (iter))
-		g_object_unref (DBUS_G_PROXY (iter->data));
-	g_slist_free (info->modem_proxies);
-	info->modem_proxies = NULL;
-
-	if (info->mm_proxy) {
-		g_object_unref (info->mm_proxy);
-		info->mm_proxy = NULL;
-	}
-
-	if (info->dun_proxy) {
-		if (info->rfcomm_iface) {
-			dbus_g_proxy_call_no_reply (info->dun_proxy, "Disconnect",
-			                            G_TYPE_STRING, info->rfcomm_iface,
-			                            G_TYPE_INVALID);
-		}
-
-		dbus_g_proxy_disconnect_signal (info->dun_proxy, "PropertyChanged",
-		                                G_CALLBACK (dun_property_changed), info);
-
-		g_object_unref (info->dun_proxy);
-		info->dun_proxy = NULL;
-	}
-
-	g_free (info->rfcomm_iface);
-	info->rfcomm_iface = NULL;
-
-	if (info->dun_timeout_id) {
-		g_source_remove (info->dun_timeout_id);
-		info->dun_timeout_id = 0;
-	}
-
-	if (info->window_group) {
-		g_object_unref (info->window_group);
-		info->window_group = NULL;
-	}
-
-	if (info->wizard) {
-		nma_mobile_wizard_destroy (info->wizard);
-		info->wizard = NULL;
-	}
-
-	if (info->spinner) {
-		gtk_spinner_stop (GTK_SPINNER (info->spinner));
-		gtk_widget_hide (info->spinner);
-	}
-	gtk_label_set_text (GTK_LABEL (info->label), message);
-	gtk_widget_set_sensitive (info->dun_button, TRUE);
-
-	if (uncheck) {
-		g_signal_handler_block (info->dun_button, info->dun_toggled_id);
-		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (info->dun_button), FALSE);
-		g_signal_handler_unblock (info->dun_button, info->dun_toggled_id);
-	}
-}
-
-static void
-dun_error (PluginInfo *info, const char *func, GError *error, const char *fallback)
-{
-	char *message;
-
-	message = g_strdup_printf (_("Error: %s"), (error && error->message) ? error->message : fallback);
-	g_warning ("%s: %s", func, message);
-	dun_cleanup (info, message, TRUE);
-	g_free (message);
-}
+	g_message ("%s: NM Bluetooth widget info being destroyed", __func__);
 
-static NMConnection *
-dun_new_cdma (NMAMobileWizardAccessMethod *method)
-{
-	NMConnection *connection;
-	NMSetting *setting;
-	char *uuid, *id;
-
-	connection = nm_connection_new ();
-
-	setting = nm_setting_cdma_new ();
-	g_object_set (setting,
-	              NM_SETTING_CDMA_NUMBER, "#777",
-	              NM_SETTING_CDMA_USERNAME, method->username,
-	              NM_SETTING_CDMA_PASSWORD, method->password,
-	              NULL);
-	nm_connection_add_setting (connection, setting);
-
-	/* Serial setting */
-	setting = nm_setting_serial_new ();
-	g_object_set (setting,
-	              NM_SETTING_SERIAL_BAUD, 115200,
-	              NM_SETTING_SERIAL_BITS, 8,
-	              NM_SETTING_SERIAL_PARITY, 'n',
-	              NM_SETTING_SERIAL_STOPBITS, 1,
-	              NULL);
-	nm_connection_add_setting (connection, setting);
-
-	nm_connection_add_setting (connection, nm_setting_ppp_new ());
-
-	setting = nm_setting_connection_new ();
-	if (method->plan_name)
-		id = g_strdup_printf ("%s %s", method->provider_name, method->plan_name);
-	else
-		id = g_strdup_printf ("%s connection", method->provider_name);
-	uuid = nm_utils_uuid_generate ();
-	g_object_set (setting,
-	              NM_SETTING_CONNECTION_ID, id,
-	              NM_SETTING_CONNECTION_TYPE, NM_SETTING_BLUETOOTH_SETTING_NAME,
-	              NM_SETTING_CONNECTION_AUTOCONNECT, FALSE,
-	              NM_SETTING_CONNECTION_UUID, uuid,
-	              NULL);
-	g_free (uuid);
-	g_free (id);
-	nm_connection_add_setting (connection, setting);
-
-	return connection;
-}
+	g_signal_handlers_disconnect_matched (info->btclient,
+	                                      G_SIGNAL_MATCH_DATA, 0, 0, NULL,
+	                                      NULL, info);
+	g_object_unref (info->btclient);
 
-static NMConnection *
-dun_new_gsm (NMAMobileWizardAccessMethod *method)
-{
-	NMConnection *connection;
-	NMSetting *setting;
-	char *uuid, *id;
-
-	connection = nm_connection_new ();
-
-	setting = nm_setting_gsm_new ();
-	g_object_set (setting,
-	              NM_SETTING_GSM_NUMBER, "*99#",
-	              NM_SETTING_GSM_USERNAME, method->username,
-	              NM_SETTING_GSM_PASSWORD, method->password,
-	              NM_SETTING_GSM_APN, method->gsm_apn,
-	              NULL);
-	nm_connection_add_setting (connection, setting);
-
-	/* Serial setting */
-	setting = nm_setting_serial_new ();
-	g_object_set (setting,
-	              NM_SETTING_SERIAL_BAUD, 115200,
-	              NM_SETTING_SERIAL_BITS, 8,
-	              NM_SETTING_SERIAL_PARITY, 'n',
-	              NM_SETTING_SERIAL_STOPBITS, 1,
-	              NULL);
-	nm_connection_add_setting (connection, setting);
-
-	nm_connection_add_setting (connection, nm_setting_ppp_new ());
-
-	setting = nm_setting_connection_new ();
-	if (method->plan_name)
-		id = g_strdup_printf ("%s %s", method->provider_name, method->plan_name);
-	else
-		id = g_strdup_printf ("%s connection", method->provider_name);
-	uuid = nm_utils_uuid_generate ();
-	g_object_set (setting,
-	              NM_SETTING_CONNECTION_ID, id,
-	              NM_SETTING_CONNECTION_TYPE, NM_SETTING_BLUETOOTH_SETTING_NAME,
-	              NM_SETTING_CONNECTION_AUTOCONNECT, FALSE,
-	              NM_SETTING_CONNECTION_UUID, uuid,
-	              NULL);
-	g_free (uuid);
-	g_free (id);
-	nm_connection_add_setting (connection, setting);
-
-	return connection;
-}
+	for (iter = info->sigids; iter; iter = g_slist_next (iter))
+		g_signal_handler_disconnect (info->device, GPOINTER_TO_UINT (iter->data));
+	g_slist_free (info->sigids);
 
-static void
-dun_add_cb (NMRemoteSettings *settings,
-            NMRemoteConnection *connection,
-            GError *error,
-            gpointer user_data)
-{
-	PluginInfo *info = user_data;
-	char *message;
+	g_object_unref (info->device);
 
-	if (error) {
-		message = g_strdup_printf (_("Failed to create DUN connection: %s"), error->message);
-		dun_cleanup (info, message, TRUE);
-		g_free (message);
-	} else {
-		info->dun_connection = connection;
-		dun_cleanup (info, _("Your phone is now ready to use!"), FALSE);
-	}
+	memset (info, 0, sizeof (WidgetInfo));
+	g_free (info);
 }
 
 static void
-wizard_done_cb (NMAMobileWizard *self,
-                gboolean canceled,
-                NMAMobileWizardAccessMethod *method,
-                gpointer user_data)
+set_dun_button_sensitive (WidgetInfo *info, gboolean sensitive)
 {
-	PluginInfo *info = user_data;
-	NMConnection *connection = NULL;
-	GByteArray *mac;
-	NMSetting *s_bt;
-
-	g_message ("%s: mobile wizard done", __func__);
-
-	if (canceled || !method) {
-		dun_error (info, __func__, NULL, _("Mobile wizard was canceled"));
-		return;
-	}
-
-	if (method->devtype == NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO)
-		connection = dun_new_cdma (method);
-	else if (method->devtype == NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS)
-		connection = dun_new_gsm (method);
-	else {
-		dun_error (info, __func__, NULL, _("Unknown phone device type (not GSM or CDMA)"));
-		return;
-	}
-
-	nma_mobile_wizard_destroy (info->wizard);
-	info->wizard = NULL;
-
-	g_assert (connection);
-
-	/* The Bluetooth settings */
-	mac = get_array_from_bdaddr (info->bdaddr);
-	g_assert (mac);
-	s_bt = nm_setting_bluetooth_new ();
-	g_object_set (G_OBJECT (s_bt),
-	              NM_SETTING_BLUETOOTH_BDADDR, mac,
-	              NM_SETTING_BLUETOOTH_TYPE, NM_SETTING_BLUETOOTH_TYPE_DUN,
-	              NULL);
-	g_byte_array_free (mac, TRUE);
-	nm_connection_add_setting (connection, s_bt);
-
-	g_message ("%s: adding new setting", __func__);
-
-	/* Add the connection to the settings service */
-	nm_remote_settings_add_connection (info->settings,
-	                                   connection,
-	                                   dun_add_cb,
-	                                   info);
-
-	g_message ("%s: waiting for add connection result...", __func__);
+	gtk_widget_set_sensitive (info->dun_button,
+	                          sensitive && info->powered && !nma_bt_device_get_busy (info->device));
 }
 
 static void
-modem_get_all_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
+default_adapter_powered_changed (GObject *object,
+                                 GParamSpec *pspec,
+                                 WidgetInfo *info)
 {
-	PluginInfo *info = user_data;
-	const char *path;
-	GHashTable *properties = NULL;
-	GError *error = NULL;
-	GValue *value;
-	NMDeviceType devtype = NM_DEVICE_TYPE_UNKNOWN;
-
-	path = dbus_g_proxy_get_path (proxy);
-	g_message ("%s: (%s) processing GetAll reply", __func__, path);
-
-	if (!dbus_g_proxy_end_call (proxy, call, &error,
-	                            DBUS_TYPE_G_MAP_OF_VARIANT, &properties,
-	                            G_TYPE_INVALID)) {
-		g_warning ("%s: (%s) Error getting modem properties: (%d) %s",
-		           __func__,
-		           path,
-		           error ? error->code : -1,
-		           (error && error->message) ? error->message : "(unknown)");
-		g_error_free (error);
-		goto out;
-	}
-
-	/* check whether this is the device we care about */
-	value = g_hash_table_lookup (properties, "Device");
-	if (value && G_VALUE_HOLDS_STRING (value) && g_value_get_string (value)) {
-		char *iface_basename = g_path_get_basename (info->rfcomm_iface);
-		const char *modem_iface = g_value_get_string (value);
-
-		if (!strcmp (iface_basename, modem_iface)) {
-			/* yay, found it! */
-
-			value = g_hash_table_lookup (properties, "Type");
-			if (value && G_VALUE_HOLDS_UINT (value)) {
-				switch (g_value_get_uint (value)) {
-				case 1:
-					devtype = NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS;
-					break;
-				case 2:
-					devtype = NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO;
-					break;
-				default:
-					g_message ("%s: (%s) unknown modem type", __func__, path);
-					break;
-				}
-			}
-		} else {
-			g_message ("%s: (%s) (%s) not the modem we're looking for (%s)",
-			           __func__, path, modem_iface, iface_basename);
-		}
-
-		g_free (iface_basename);
-	} else
-		g_message ("%s: (%s) modem had no 'Device' property", __func__, path);
-
-	g_hash_table_unref (properties);
-
-	if (devtype != NM_DEVICE_TYPE_UNKNOWN) {
-		GtkWidget *parent;
-
-		if (info->wizard) {
-			g_message ("%s: (%s) oops! not starting Wizard as one is already in progress", __func__, path);
-			goto out;
-		}
-
-		g_message ("%s: (%s) starting the mobile wizard", __func__, path);
+	gboolean powered = TRUE;
 
-		g_source_remove (info->dun_timeout_id);
-		info->dun_timeout_id = 0;
+	g_object_get (G_OBJECT (info->btclient), "default-adapter-powered", &powered, NULL);
+	g_message ("Default Bluetooth adapter is %s", powered ? "powered" : "switched off");
 
-		parent = gtk_widget_get_toplevel (info->hbox);
-		if (gtk_widget_is_toplevel (parent)) {
-			info->window_group = gtk_window_group_new ();
-			gtk_window_group_add_window (info->window_group, GTK_WINDOW (parent));
-		} else {
-			parent = NULL;
-			info->window_group = NULL;
+	/* If the default adapter isn't powered we can't inspect the device
+	 * and create a connection for it.
+	 */
+	info->powered = powered;
+	if (powered) {
+		if (info->dun_button) {
+			gtk_label_set_text (GTK_LABEL (info->status), NULL); 
+			set_dun_button_sensitive (info, TRUE);
 		}
-
-		/* Start the mobile wizard */
-		info->wizard = nma_mobile_wizard_new (parent ? GTK_WINDOW (parent) : NULL,
-											  info->window_group,
-											  devtype,
-											  FALSE,
-											  wizard_done_cb,
-											  info);
-		nma_mobile_wizard_present (info->wizard);
-	}
-
-out:
-	g_message ("%s: finished", __func__);
-}
-
-static void
-modem_added (DBusGProxy *proxy, const char *path, gpointer user_data)
-{
-	PluginInfo *info = user_data;
-	DBusGProxy *props_proxy;
-
-	g_return_if_fail (path != NULL);
-
-	g_message ("%s: (%s) modem found", __func__, path);
-
-	/* Create a proxy for the modem and get its properties */
-	props_proxy = dbus_g_proxy_new_for_name (info->bus,
-	                                         MM_SERVICE,
-	                                         path,
-	                                         "org.freedesktop.DBus.Properties");
-	g_assert (proxy);
-	info->modem_proxies = g_slist_append (info->modem_proxies, props_proxy);
-
-	g_message ("%s: (%s) calling GetAll...", __func__, path);
-
-	dbus_g_proxy_begin_call (props_proxy, "GetAll",
-	                         modem_get_all_cb,
-	                         info,
-	                         NULL,
-	                         G_TYPE_STRING, MM_MODEM_INTERFACE,
-	                         G_TYPE_INVALID);
-}
-
-static void
-modem_removed (DBusGProxy *proxy, const char *path, gpointer user_data)
-{
-	PluginInfo *info = user_data;
-	GSList *iter;
-	DBusGProxy *found = NULL;
-
-	g_return_if_fail (path != NULL);
-
-	g_message ("%s: (%s) modem removed", __func__, path);
-
-	/* Clean up if a modem gets removed */
-
-	for (iter = info->modem_proxies; iter; iter = g_slist_next (iter)) {
-		if (!strcmp (path, dbus_g_proxy_get_path (DBUS_G_PROXY (iter->data)))) {
-			found = iter->data;
-			break;
+	} else {
+		/* powered only matters for DUN */
+		if (info->dun_button) {
+			nma_bt_device_cancel_dun (info->device);
+			/* Can't toggle the DUN button unless the adapter is powered */
+			set_dun_button_sensitive (info, FALSE);
 		}
 	}
-
-	if (found) {
-		info->modem_proxies = g_slist_remove (info->modem_proxies, found);
-		g_object_unref (found);
-	}
-}
-
-static void
-dun_connect_cb (DBusGProxy *proxy,
-                DBusGProxyCall *call,
-                void *user_data)
-{
-	PluginInfo *info = user_data;
-	GError *error = NULL;
-	char *device;
-
-	g_message ("%s: processing Connect reply", __func__);
-
-	if (!dbus_g_proxy_end_call (proxy, call, &error,
-	                            G_TYPE_STRING, &device,
-	                            G_TYPE_INVALID)) {
-		dun_error (info, __func__, error, _("failed to connect to the phone."));
-		g_clear_error (&error);
-		goto out;
-	}
-
-	if (!device || !strlen (device)) {
-		dun_error (info, __func__, NULL, _("failed to connect to the phone."));
-		g_free (device);
-		goto out;
-	}
-
-	info->rfcomm_iface = device;
-	g_message ("%s: new rfcomm interface '%s'", __func__, device);
-
-out:
-	g_message ("%s: finished", __func__);
-}
-
-static void
-dun_property_changed (DBusGProxy *proxy,
-                      const char *property,
-                      GValue *value,
-                      gpointer user_data)
-{
-	PluginInfo *info = user_data;
-	gboolean connected;
-
-	if (strcmp (property, "Connected"))
-		return;
-
-	connected = g_value_get_boolean (value);
-
-	g_message ("%s: device property Connected changed to %s",
-	           __func__,
-	           connected ? "TRUE" : "FALSE");
-
-	if (connected) {
-		/* Wait for MM here ? */
-	} else
-		dun_error (info, __func__, NULL, _("unexpectedly disconnected from the phone."));
-}
-
-static gboolean
-dun_timeout_cb (gpointer user_data)
-{
-	PluginInfo *info = user_data;
-
-	info->dun_timeout_id = 0;
-	dun_error (info, __func__, NULL, _("timed out detecting phone details."));
-	return FALSE;
 }
 
 static void
-dun_start (PluginInfo *info)
+default_adapter_changed (GObject *gobject,
+                         GParamSpec *pspec,
+                         WidgetInfo *info)
 {
-	GError *error = NULL;
-	GtkTreeIter iter;
-
-	g_message ("%s: starting DUN device discovery...", __func__);
+	char *adapter = NULL;
 
-	gtk_label_set_text (GTK_LABEL (info->label), _("Detecting phone configuration..."));
+	g_object_get (G_OBJECT (gobject), "default-adapter", &adapter, NULL);
+	g_message ("Default Bluetooth adapter changed: %s", adapter ? adapter : "(none)");
+	g_free (adapter);
 
-	/* Start the spinner */
-	if (!info->spinner) {
-		info->spinner = gtk_spinner_new ();
-		gtk_box_pack_start (GTK_BOX (info->hbox), info->spinner, FALSE, FALSE, 6);
-	}
-	gtk_spinner_start (GTK_SPINNER (info->spinner));
-	gtk_widget_show_all (info->hbox);
-
-	gtk_widget_set_sensitive (info->dun_button, FALSE);
-
-	/* ModemManager stuff */
-	info->mm_proxy = dbus_g_proxy_new_for_name (info->bus,
-	                                            MM_SERVICE,
-	                                            MM_PATH,
-	                                            MM_INTERFACE);
-	g_assert (info->mm_proxy);
-
-	dbus_g_object_register_marshaller (g_cclosure_marshal_VOID__BOXED,
-	                                   G_TYPE_NONE,
-	                                   G_TYPE_BOXED,
-	                                   G_TYPE_INVALID);
-	dbus_g_proxy_add_signal (info->mm_proxy, "DeviceAdded",
-	                         DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
-	dbus_g_proxy_connect_signal (info->mm_proxy, "DeviceAdded",
-								 G_CALLBACK (modem_added), info,
-								 NULL);
-
-	dbus_g_proxy_add_signal (info->mm_proxy, "DeviceRemoved",
-	                         DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
-	dbus_g_proxy_connect_signal (info->mm_proxy, "DeviceRemoved",
-								 G_CALLBACK (modem_removed), info,
-								 NULL);
-
-	/* Get the device we're looking for */
-	if (!info->dun_proxy) {
-		if (get_device_iter (info->btmodel, info->bdaddr, &iter)) {
-			gpointer proxy = NULL;
-
-			gtk_tree_model_get (info->btmodel, &iter, BLUETOOTH_COLUMN_PROXY, &proxy, -1);
-
-			/* At some point gnome-bluetooth switched to gdbus, so we don't know
-			 * if the proxy will be a DBusGProxy (dbus-glib) or a GDBusProxy (gdbus).
-			 */
-			if (G_IS_DBUS_PROXY (proxy)) {
-				info->dun_proxy = dbus_g_proxy_new_for_name (info->bus,
-				                                             BLUEZ_SERVICE,
-				                                             g_dbus_proxy_get_object_path (G_DBUS_PROXY (proxy)),
-				                                             BLUEZ_SERIAL_INTERFACE);
-				g_object_unref (proxy);
-			} else if (DBUS_IS_G_PROXY (proxy)) {
-				info->dun_proxy = proxy;
-				dbus_g_proxy_set_interface (info->dun_proxy, BLUEZ_SERIAL_INTERFACE);
-			} else {
-				dun_error (info, __func__, error, _("failed to find Bluetooth device (unknown gnome-bluetooth proxy object type)."));
-				goto out;
-			}
-		}
-	}
-	g_assert (info->dun_proxy);
-
-	info->dun_timeout_id = g_timeout_add_seconds (45, dun_timeout_cb, info);
-
-	g_message ("%s: calling Connect...", __func__);
-
-	/* Watch for BT device property changes */
-	dbus_g_object_register_marshaller (_nma_marshal_VOID__STRING_BOXED,
-	                                   G_TYPE_NONE,
-	                                   G_TYPE_STRING, G_TYPE_VALUE,
-	                                   G_TYPE_INVALID);
-	dbus_g_proxy_add_signal (info->dun_proxy, "PropertyChanged",
-	                         G_TYPE_STRING, G_TYPE_VALUE, G_TYPE_INVALID);
-	dbus_g_proxy_connect_signal (info->dun_proxy, "PropertyChanged",
-	                             G_CALLBACK (dun_property_changed), info, NULL);
-
-	/* Request a connection to the device and get the port */
-	dbus_g_proxy_begin_call_with_timeout (info->dun_proxy, "Connect",
-	                                      dun_connect_cb,
-	                                      info,
-	                                      NULL,
-	                                      20000,
-	                                      G_TYPE_STRING, "dun",
-	                                      G_TYPE_INVALID);
-
-out:
-	g_message ("%s: finished", __func__);
+	default_adapter_powered_changed (G_OBJECT (info->btclient), NULL, info);
 }
 
-/*******************************************************************/
-
 static void
-delete_cb (NMRemoteConnection *connection, GError *error, gpointer user_data)
+device_pan_enabled_cb (NmaBtDevice *device, GParamSpec *pspec, WidgetInfo *info)
 {
-	if (error) {
-		g_warning ("Error deleting connection: (%d) %s",
-		           error ? error->code : -1,
-		           error && error->message ? error->message : "(unknown)");
-	}
+	g_signal_handler_block (info->pan_button, info->pan_toggled_id);
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (info->pan_button),
+	                              nma_bt_device_get_pan_enabled (device));
+	g_signal_handler_unblock (info->pan_button, info->pan_toggled_id);
 }
 
 static void
-pan_button_toggled (GtkToggleButton *button, gpointer user_data)
+device_dun_enabled_cb (NmaBtDevice *device, GParamSpec *pspec, WidgetInfo *info)
 {
-	PluginInfo *info = user_data;
-
-	if (gtk_toggle_button_get_active (button) == FALSE) {
-		nm_remote_connection_delete (info->pan_connection, delete_cb, NULL);
-		info->pan_connection = NULL;
-	} else
-		pan_start (info);
+	g_signal_handler_block (info->dun_button, info->dun_toggled_id);
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (info->dun_button),
+	                              nma_bt_device_get_dun_enabled (device));
+	g_signal_handler_unblock (info->dun_button, info->dun_toggled_id);
 }
 
 static void
-dun_button_toggled (GtkToggleButton *button, gpointer user_data)
-{
-	PluginInfo *info = user_data;
-
-	if (gtk_toggle_button_get_active (button) == FALSE) {
-		nm_remote_connection_delete (info->dun_connection, delete_cb, NULL);
-		info->dun_connection = NULL;
-	} else
-		dun_start (info);
-}
-
-static gboolean
-match_connection (NMConnection *connection, GByteArray *bdaddr, gboolean pan)
+device_busy_cb (NmaBtDevice *device, GParamSpec *pspec, WidgetInfo *info)
 {
-	NMSetting *setting;
-	const char *type;
-	const GByteArray *tmp;
-
-	setting = nm_connection_get_setting_by_name (connection, NM_SETTING_BLUETOOTH_SETTING_NAME);
-	if (setting == NULL)
-		return FALSE;
-
-	type = nm_setting_bluetooth_get_connection_type (NM_SETTING_BLUETOOTH (setting));
-	if (pan) {
-		if (g_strcmp0 (type, NM_SETTING_BLUETOOTH_TYPE_PANU) != 0)
-			return FALSE;
-	} else {
-		if (g_strcmp0 (type, NM_SETTING_BLUETOOTH_TYPE_DUN) != 0)
-			return FALSE;
-	}
+	gboolean busy = nma_bt_device_get_busy (device);
 
-	tmp = nm_setting_bluetooth_get_bdaddr (NM_SETTING_BLUETOOTH (setting));
-	if (tmp == NULL || memcmp (tmp->data, bdaddr->data, tmp->len) != 0)
-		return FALSE;
-
-	return TRUE;
-}
+	if (info->pan_button)
+		gtk_widget_set_sensitive (info->pan_button, !busy);
+	if (info->dun_button)
+		set_dun_button_sensitive (info, !busy);
 
-static NMRemoteConnection *
-get_connection_for_bdaddr (NMRemoteSettings *settings,
-                           const char *bdaddr,
-                           gboolean pan)
-{
-	NMRemoteConnection *found = NULL;
-	GSList *list, *iter;
-	GByteArray *array;
-
-	array = get_array_from_bdaddr (bdaddr);
-	if (array) {
-		list = nm_remote_settings_list_connections (settings);
-		for (iter = list; iter != NULL; iter = g_slist_next (iter)) {
-			if (match_connection (NM_CONNECTION (iter->data), array, pan)) {
-				found = iter->data;
-				break;
-			}
+	if (busy) {
+		if (!info->spinner) {
+			info->spinner = gtk_spinner_new ();
+			gtk_box_pack_start (GTK_BOX (info->hbox), info->spinner, FALSE, FALSE, 6);
 		}
-		g_slist_free (list);
-		g_byte_array_free (array, TRUE);
-	}
-	return found;
-}
-
-static void
-plugin_info_destroy (gpointer data)
-{
-	PluginInfo *info = data;
-
-	g_message ("%s: NM Bluetooth widget info being destroyed", __func__);
-
-	g_free (info->bdaddr);
-	g_free (info->rfcomm_iface);
-	if (info->pan_connection)
-		g_object_unref (info->pan_connection);
-	if (info->dun_connection)
-		g_object_unref (info->dun_connection);
-	if (info->spinner)
-		gtk_spinner_stop (GTK_SPINNER (info->spinner));
-	g_object_unref (info->settings);
-	g_object_unref (info->btmodel);
-
-	g_signal_handlers_disconnect_matched (info->btclient,
-	                                      G_SIGNAL_MATCH_DATA, 0, 0, NULL,
-	                                      NULL, info);
-	g_object_unref (info->btclient);
-
-	if (info->bus)
-		dbus_g_connection_unref (info->bus);
-	memset (info, 0, sizeof (PluginInfo));
-	g_free (info);
-}
-
-static void
-default_adapter_powered_changed (GObject *object,
-                                 GParamSpec *pspec,
-                                 gpointer user_data)
-{
-	PluginInfo *info = user_data;
-	gboolean powered = TRUE;
-
-	g_object_get (G_OBJECT (info->btclient), "default-adapter-powered", &powered, NULL);
-	g_message ("Default Bluetooth adapter is %s", powered ? "powered" : "switched off");
-
-	/* If the default adapter isn't powered we can't inspect the device
-	 * and create a connection for it.
-	 */
-	if (powered) {
-		gtk_label_set_text (GTK_LABEL (info->label), NULL); 
-		if (info->dun)
-			gtk_widget_set_sensitive (info->dun_button, TRUE);
+		gtk_spinner_start (GTK_SPINNER (info->spinner));
+		gtk_widget_show (info->spinner);
 	} else {
-		/* powered only matters for DUN */
-		if (info->dun) {
-			dun_cleanup (info, _("The default Bluetooth adapter must be enabled before setting up a Dial-Up-Networking connection."), TRUE);
-			/* Can't toggle the DUN button unless the adapter is powered */
-			gtk_widget_set_sensitive (info->dun_button, FALSE);
+		if (info->spinner) {
+			gtk_spinner_stop (GTK_SPINNER (info->spinner));
+			gtk_widget_destroy (info->spinner);
+			info->spinner = NULL;
 		}
 	}
 }
 
 static void
-default_adapter_changed (GObject *gobject,
-                         GParamSpec *pspec,
-                         gpointer user_data)
+device_status_cb (NmaBtDevice *device, GParamSpec *pspec, WidgetInfo *info)
 {
-	PluginInfo *info = user_data;
-	char *adapter;
-
-	g_object_get (G_OBJECT (gobject), "default-adapter", &adapter, NULL);
-	g_message ("Default Bluetooth adapter changed: %s", adapter ? adapter : "(none)");
-	g_free (adapter);
-
-	default_adapter_powered_changed (G_OBJECT (info->btclient), NULL, info);
+	gtk_label_set_text (GTK_LABEL (info->status), nma_bt_device_get_status (device));
 }
 
 static gboolean
 nm_is_running (void)
 {
-	DBusGConnection *bus;
-	DBusGProxy *proxy = NULL;
-	GError *error = NULL;
+	DBusConnection *bus;
+	DBusError error;
 	gboolean running = FALSE;
 
-	bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
-	if (error || !bus) {
-		g_message (_("Bluetooth configuration not possible (failed to connect to D-Bus: %s)."),
-		           (error && error->message) ? error->message : "unknown");
-		goto out;
-	}
-
-	proxy = dbus_g_proxy_new_for_name (bus,
-	                                   "org.freedesktop.DBus",
-	                                   "/org/freedesktop/DBus",
-	                                   "org.freedesktop.DBus");
-	if (!proxy) {
-		g_message (_("Bluetooth configuration not possible (failed to create D-Bus proxy)."));
-		goto out;
+	dbus_error_init (&error);
+	bus = dbus_bus_get (DBUS_BUS_SYSTEM, &error);
+	if (dbus_error_is_set (&error)) {
+		g_message (_("Bluetooth configuration not possible (failed to connect to D-Bus: (%s) %s)."),
+		           error.name, error.message);
+		dbus_error_free (&error);
+		return FALSE;
 	}
 
-	if (!dbus_g_proxy_call (proxy, "NameHasOwner", &error,
-	                        G_TYPE_STRING, NM_DBUS_SERVICE,
-	                        G_TYPE_INVALID,
-	                        G_TYPE_BOOLEAN, &running,
-	                        G_TYPE_INVALID)) {
-		g_message (_("Bluetooth configuration not possible (error finding NetworkManager: %s)."),
-		           error && error->message ? error->message : "unknown");
+	dbus_error_init (&error);
+	running = dbus_bus_name_has_owner (bus, NM_DBUS_SERVICE, &error);
+	if (dbus_error_is_set (&error)) {
+		g_message (_("Bluetooth configuration not possible (error finding NetworkManager: (%s) %s)."),
+		           error.name, error.message);
 	}
 
-out:
-	g_clear_error (&error);
-	if (proxy)
-		g_object_unref (proxy);
-	if (bus)
-		dbus_g_connection_unref (bus);
+	dbus_connection_unref (bus);
 	return running;
 }
 
 static GtkWidget *
 get_config_widgets (const char *bdaddr, const char **uuids)
 {
-	PluginInfo *info;
+	WidgetInfo *info;
+	NmaBtDevice *device;
 	GtkWidget *vbox, *hbox;
-	gboolean pan = FALSE, dun = FALSE;
-	DBusGConnection *bus;
-	GError *error = NULL;
+	gboolean pan = FALSE, dun = FALSE, busy;
+	GtkTreeIter iter;
+	BluetoothClient *btclient;
+	GtkTreeModel *btmodel;
+	guint id;
 
 	/* Don't allow configuration if NM isn't running; it just confuses people
 	 * if they see the checkboxes but the configuration doesn't seem to have
@@ -1081,29 +358,78 @@ get_config_widgets (const char *bdaddr, const char **uuids)
 	if (!pan && !dun)
 		return NULL;
 
-	/* Set up dbus */
-	bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
-	if (error || !bus) {
-		g_warning ("%s: failed to get a connection to D-Bus! %s", __func__,
-		           error ? error->message : "(unknown)");
-		g_clear_error (&error);
-		return NULL;
+	/* BluetoothClient setup */
+	btclient = bluetooth_client_new ();
+	btmodel = bluetooth_client_get_model (btclient);
+	g_assert (btmodel);
+
+	device = get_device (bdaddr);
+	if (!device) {
+		const char *op = NULL;
+		gpointer proxy;
+		char *alias;
+
+		if (!get_device_iter (btmodel, bdaddr, &iter)) {
+			g_warning ("%s: failed to retrieve device %s from gnome-bluetooth!", __func__, bdaddr);
+			g_object_unref (btmodel);
+			g_object_unref (btclient);
+			return NULL;
+		}
+
+		gtk_tree_model_get (btmodel, &iter,
+		                    BLUETOOTH_COLUMN_ALIAS, &alias,
+		                    BLUETOOTH_COLUMN_PROXY, &proxy,
+		                    -1);
+		g_assert (proxy);
+
+		/* At some point gnome-bluetooth switched to gdbus, so we don't know
+		* if the proxy will be a DBusGProxy (dbus-glib) or a GDBusProxy (gdbus).
+		*/
+		if (G_IS_DBUS_PROXY (proxy))
+			op = g_dbus_proxy_get_object_path (G_DBUS_PROXY (proxy));
+		else if (DBUS_IS_G_PROXY (proxy))
+			op = dbus_g_proxy_get_path (DBUS_G_PROXY (proxy));
+		else
+			g_assert_not_reached ();
+
+		device = nma_bt_device_new (bdaddr, alias, op, pan, dun);
+		g_free (alias);
+		g_object_unref (proxy);
+
+		if (!device) {
+			g_warning ("%s: failed to create Bluetooth proxy object!", bdaddr);
+			g_object_unref (btmodel);
+			g_object_unref (btclient);
+			return NULL;
+		}
+
+		add_device (device);
 	}
 
-	info = g_malloc0 (sizeof (PluginInfo));
-	info->bus = bus;
-	info->settings = nm_remote_settings_new (bus);
-	info->bdaddr = g_strdup (bdaddr);
-	info->pan = pan;
-	info->dun = dun;
-	
-	/* BluetoothClient setup */
-	info->btclient = bluetooth_client_new ();
-	info->btmodel = bluetooth_client_get_model (info->btclient);
-	g_signal_connect (G_OBJECT (info->btclient), "notify::default-adapter",
-			  G_CALLBACK (default_adapter_changed), info);
-	g_signal_connect (G_OBJECT (info->btclient), "notify::default-adapter-powered",
-			  G_CALLBACK (default_adapter_powered_changed), info);
+	info = g_malloc0 (sizeof (WidgetInfo));
+	info->device = g_object_ref (device);
+	info->btclient = btclient;
+
+	g_signal_connect (G_OBJECT (btclient), "notify::default-adapter",
+	                  G_CALLBACK (default_adapter_changed), info);
+	g_signal_connect (G_OBJECT (btclient), "notify::default-adapter-powered",
+	                  G_CALLBACK (default_adapter_powered_changed), info);
+
+	id = g_signal_connect (device, "notify::" NMA_BT_DEVICE_PAN_ENABLED,
+	                       G_CALLBACK (device_pan_enabled_cb), info);
+	info->sigids = g_slist_prepend (info->sigids, GUINT_TO_POINTER (id));
+
+	id = g_signal_connect (device, "notify::" NMA_BT_DEVICE_DUN_ENABLED,
+	                       G_CALLBACK (device_dun_enabled_cb), info);
+	info->sigids = g_slist_prepend (info->sigids, GUINT_TO_POINTER (id));
+
+	id = g_signal_connect (device, "notify::" NMA_BT_DEVICE_BUSY,
+	                       G_CALLBACK (device_busy_cb), info);
+	info->sigids = g_slist_prepend (info->sigids, GUINT_TO_POINTER (id));
+
+	id = g_signal_connect (device, "notify::" NMA_BT_DEVICE_STATUS,
+	                       G_CALLBACK (device_status_cb), info);
+	info->sigids = g_slist_prepend (info->sigids, GUINT_TO_POINTER (id));
 
 	/* UI setup */
 #if GTK_CHECK_VERSION (3,1,6)
@@ -1111,24 +437,26 @@ get_config_widgets (const char *bdaddr, const char **uuids)
 #else
 	vbox = gtk_vbox_new (FALSE, 6);
 #endif
-	g_object_set_data_full (G_OBJECT (vbox), "info", info, plugin_info_destroy);
+	g_object_set_data_full (G_OBJECT (vbox), "info", info, widget_info_destroy);
+
+	busy = nma_bt_device_get_busy (device);
 
 	if (pan) {
-		info->pan_connection = get_connection_for_bdaddr (info->settings, bdaddr, TRUE);
 		info->pan_button = gtk_check_button_new_with_label (_("Use your mobile phone as a network device (PAN/NAP)"));
-		if (info->pan_connection)
-			gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (info->pan_button), TRUE);
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (info->pan_button),
+		                              nma_bt_device_get_pan_enabled (device));
 		info->pan_toggled_id = g_signal_connect (G_OBJECT (info->pan_button), "toggled", G_CALLBACK (pan_button_toggled), info);
 		gtk_box_pack_start (GTK_BOX (vbox), info->pan_button, FALSE, TRUE, 6);
+		gtk_widget_set_sensitive (info->pan_button, !busy);
 	}
 
 	if (dun) {
-		info->dun_connection = get_connection_for_bdaddr (info->settings, bdaddr, FALSE);
 		info->dun_button = gtk_check_button_new_with_label (_("Access the Internet using your mobile phone (DUN)"));
-		if (info->dun_connection)
-			gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (info->dun_button), TRUE);
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (info->dun_button),
+		                              nma_bt_device_get_dun_enabled (device));
 		info->dun_toggled_id = g_signal_connect (G_OBJECT (info->dun_button), "toggled", G_CALLBACK (dun_button_toggled), info);
 		gtk_box_pack_start (GTK_BOX (vbox), info->dun_button, FALSE, TRUE, 6);
+		set_dun_button_sensitive (info, !busy);
 	}
 
 #if GTK_CHECK_VERSION (3,1,6)
@@ -1146,16 +474,21 @@ get_config_widgets (const char *bdaddr, const char **uuids)
 #endif
 	gtk_box_pack_start (GTK_BOX (hbox), info->hbox, FALSE, FALSE, 0);
 
-	info->label = gtk_label_new ("");
-	gtk_label_set_max_width_chars (GTK_LABEL (info->label), 80);
-	gtk_label_set_line_wrap (GTK_LABEL (info->label), TRUE);
-	gtk_box_pack_start (GTK_BOX (hbox), info->label, FALSE, TRUE, 6);
+	device_busy_cb (device, NULL, info);
+
+	/* Status label */
+	info->status = gtk_label_new (nma_bt_device_get_status (device));
+	gtk_label_set_max_width_chars (GTK_LABEL (info->status), 80);
+	gtk_label_set_line_wrap (GTK_LABEL (info->status), TRUE);
+	gtk_box_pack_start (GTK_BOX (hbox), info->status, FALSE, TRUE, 6);
 
 	default_adapter_powered_changed (G_OBJECT (info->btclient), NULL, info);
 
 	return vbox;
 }
 
+/**************************************************************/
+
 typedef struct {
 	NMRemoteSettings *settings;
 	GByteArray *bdaddr;
@@ -1173,18 +506,14 @@ remove_cleanup (RemoveInfo *info)
 	g_free (info);
 }
 
-static GSList *
-list_connections_for_bdaddr (NMRemoteSettings *settings, GByteArray *bdaddr, gboolean pan)
+static void
+delete_cb (NMRemoteConnection *connection, GError *error, gpointer user_data)
 {
-	GSList *list, *iter, *ret = NULL;
-
-	list = nm_remote_settings_list_connections (settings);
-	for (iter = list; iter != NULL; iter = g_slist_next (iter)) {
-		if (match_connection (NM_CONNECTION (iter->data), bdaddr, pan))
-			ret = g_slist_prepend (ret, iter->data);
+	if (error) {
+		g_warning ("Error deleting connection: (%d) %s",
+		           error ? error->code : -1,
+		           error && error->message ? error->message : "(unknown)");
 	}
-	g_slist_free (list);
-	return ret;
 }
 
 static void
@@ -1197,16 +526,19 @@ remove_connections_read (NMRemoteSettings *settings, gpointer user_data)
 
 	g_message ("Removing Bluetooth connections for %s", info->str_bdaddr);
 
-	/* First PAN */
-	list = list_connections_for_bdaddr (info->settings, info->bdaddr, TRUE);
-	for (iter = list; iter; iter = g_slist_next (iter))
-			nm_remote_connection_delete (NM_REMOTE_CONNECTION (iter->data), delete_cb, NULL);
-	g_slist_free (list);
-
-	/* Now DUN */
-	list = list_connections_for_bdaddr (info->settings, info->bdaddr, FALSE);
-	for (iter = list; iter; iter = g_slist_next (iter))
-			nm_remote_connection_delete (NM_REMOTE_CONNECTION (iter->data), delete_cb, NULL);
+	list = nm_remote_settings_list_connections (settings);
+	for (iter = list; iter != NULL; iter = g_slist_next (iter)) {
+		NMConnection *connection = iter->data;
+		NMSettingBluetooth *s_bt;
+		const GByteArray *tmp;
+
+		s_bt = nm_connection_get_setting_bluetooth (connection);
+		if (s_bt) {
+			tmp = nm_setting_bluetooth_get_bdaddr (s_bt);
+			if (tmp && memcmp (tmp->data, info->bdaddr->data, tmp->len) == 0)
+				nm_remote_connection_delete (NM_REMOTE_CONNECTION (connection), delete_cb, NULL);
+		}
+	}
 	g_slist_free (list);
 
 	remove_cleanup (info);
@@ -1228,14 +560,15 @@ device_removed (const char *bdaddr)
 	GError *error = NULL;
 	DBusGConnection *bus;
 	RemoveInfo *info;
-	GByteArray *array;
+	struct ether_addr *addr;
+	NmaBtDevice *device;
 
 	g_message ("Device '%s' was removed; deleting connections", bdaddr);
 
 	/* Remove any connections associated with the deleted device */
 
-	array = get_array_from_bdaddr (bdaddr);
-	if (!array) {
+	addr = ether_aton (bdaddr);
+	if (!addr) {
 		g_warning ("Failed to convert Bluetooth address '%s'", bdaddr);
 		return;
 	}
@@ -1245,13 +578,15 @@ device_removed (const char *bdaddr)
 		g_warning ("%s: failed to get a connection to D-Bus! %s", __func__,
 		           error ? error->message : "(unknown)");
 		g_clear_error (&error);
-		g_byte_array_free (array, TRUE);
 		return;
 	}
 
 	info = g_malloc0 (sizeof (RemoveInfo));
 	info->settings = nm_remote_settings_new (bus);
-	info->bdaddr = array;
+
+	info->bdaddr = g_byte_array_sized_new (ETH_ALEN);
+	g_byte_array_append (info->bdaddr, (const guint8 *) addr->ether_addr_octet, ETH_ALEN);
+
 	info->str_bdaddr = g_strdup (bdaddr);
 	info->timeout_id = g_timeout_add_seconds (15, remove_timeout, info);
 
@@ -1261,8 +596,15 @@ device_removed (const char *bdaddr)
 	                  info);
 
 	dbus_g_connection_unref (bus);
+
+	/* Kill the device */
+	device = get_device (bdaddr);
+	if (device)
+		remove_device (device);
 }
 
+/**************************************************************/
+
 static GbtPluginInfo plugin_info = {
 	"network-manager-applet",
 	has_config_widget,
diff --git a/src/gnome-bluetooth/nma-bt-device.c b/src/gnome-bluetooth/nma-bt-device.c
new file mode 100644
index 0000000..a81918f
--- /dev/null
+++ b/src/gnome-bluetooth/nma-bt-device.c
@@ -0,0 +1,1176 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ *  NetworkManager Applet
+ *
+ *  Copyright (C) 2009  Bastien Nocera <hadess hadess net>
+ *  Copyright (C) 2009 - 2010  Dan Williams <dcbw redhat com>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * (C) Copyright 2009 - 2012 Red Hat, Inc.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <net/ethernet.h>
+#include <netinet/ether.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include <nm-remote-settings.h>
+#include <nm-remote-connection.h>
+
+#include "nma-bt-device.h"
+#include "nma-marshal.h"
+#include "nm-mobile-wizard.h"
+#include "nm-utils.h"
+
+#if !GLIB_CHECK_VERSION(2,28,0)
+#define g_clear_object(object_ptr) \
+	G_STMT_START { \
+		GObject **__obj_p = (gpointer) (object_ptr); \
+		if (*__obj_p) { \
+			g_object_unref (*__obj_p); \
+			*__obj_p = NULL; \
+		} \
+	} G_STMT_END
+#endif
+
+
+G_DEFINE_TYPE (NmaBtDevice, nma_bt_device, G_TYPE_OBJECT)
+
+#define NMA_BT_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMA_TYPE_BT_DEVICE, NmaBtDevicePrivate))
+
+typedef struct {
+	DBusGConnection *bus;
+	NMRemoteSettings *settings;
+
+	char *bdaddr;
+	GByteArray *bdaddr_array;
+	char *alias;
+	char *object_path;
+
+	char *status;
+	gboolean busy;
+
+	gboolean has_pan;
+	gboolean pan_enabled;
+	gboolean has_dun;
+	gboolean dun_enabled;
+
+	/* DUN stuff */
+	DBusGProxy *dun_proxy;
+	DBusGProxy *mm_proxy;
+	GSList *modem_proxies;
+	char *rfcomm_iface;
+	guint dun_timeout_id;
+
+	GtkWindow *parent_window;
+	NMAMobileWizard *wizard;
+	GtkWindowGroup *window_group;
+} NmaBtDevicePrivate;
+
+
+enum {
+	PROP_0,
+	PROP_BDADDR,
+	PROP_ALIAS,
+	PROP_OBJECT_PATH,
+	PROP_HAS_PAN,
+	PROP_PAN_ENABLED,
+	PROP_HAS_DUN,
+	PROP_DUN_ENABLED,
+	PROP_BUSY,
+	PROP_STATUS,
+
+	LAST_PROP
+};
+
+static void _set_pan_enabled (NmaBtDevice *device, gboolean enabled);
+static void _set_dun_enabled (NmaBtDevice *device, gboolean enabled);
+
+#define DBUS_TYPE_G_MAP_OF_VARIANT (dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE))
+
+#define BLUEZ_SERVICE           "org.bluez"
+#define BLUEZ_MANAGER_PATH      "/"
+#define BLUEZ_MANAGER_INTERFACE "org.bluez.Manager"
+#define BLUEZ_ADAPTER_INTERFACE "org.bluez.Adapter"
+#define BLUEZ_DEVICE_INTERFACE  "org.bluez.Device"
+#define BLUEZ_SERIAL_INTERFACE  "org.bluez.Serial"
+#define BLUEZ_NETWORK_INTERFACE "org.bluez.Network"
+
+#define MM_SERVICE         "org.freedesktop.ModemManager"
+#define MM_PATH            "/org/freedesktop/ModemManager"
+#define MM_INTERFACE       "org.freedesktop.ModemManager"
+#define MM_MODEM_INTERFACE "org.freedesktop.ModemManager.Modem"
+
+/*********************************************************************/
+
+static gboolean
+match_connection_bdaddr (NMConnection *connection, const GByteArray *bdaddr)
+{
+	NMSettingBluetooth *s_bt;
+	const GByteArray *tmp;
+
+	s_bt = nm_connection_get_setting_bluetooth (connection);
+	if (s_bt) {
+		tmp = nm_setting_bluetooth_get_bdaddr (s_bt);
+		if (tmp && memcmp (tmp->data, bdaddr->data, tmp->len) == 0)
+			return TRUE;
+	}
+	return FALSE;
+}
+
+static gboolean
+match_connection_service (NMConnection *connection,
+                          const GByteArray *bdaddr,
+                          gboolean pan)
+{
+	NMSettingBluetooth *s_bt;
+	const char *type;
+
+	if (!match_connection_bdaddr (connection, bdaddr))
+		return FALSE;
+
+	s_bt = nm_connection_get_setting_bluetooth (connection);
+	g_assert (s_bt);
+	type = nm_setting_bluetooth_get_connection_type (s_bt);
+	if (pan) {
+		if (g_strcmp0 (type, NM_SETTING_BLUETOOTH_TYPE_PANU) != 0)
+			return FALSE;
+	} else {
+		if (g_strcmp0 (type, NM_SETTING_BLUETOOTH_TYPE_DUN) != 0)
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+delete_cb (NMRemoteConnection *connection, GError *error, gpointer user_data)
+{
+	if (error) {
+		g_warning ("Error deleting connection: (%d) %s",
+		           error ? error->code : -1,
+		           error && error->message ? error->message : "(unknown)");
+	}
+}
+
+static void
+delete_connections_of_type (NMRemoteSettings *settings,
+                            const GByteArray *bdaddr,
+                            gboolean pan)
+{
+	GSList *list, *iter;
+
+	list = nm_remote_settings_list_connections (settings);
+	for (iter = list; iter != NULL; iter = g_slist_next (iter)) {
+		NMRemoteConnection *remote = iter->data;
+
+		if (match_connection_service (NM_CONNECTION (remote), bdaddr, pan))
+			nm_remote_connection_delete (remote, delete_cb, NULL);
+	}
+	g_slist_free (list);
+}
+
+static void
+recheck_services_enabled (NmaBtDevice *self)
+{
+	NmaBtDevicePrivate *priv = NMA_BT_DEVICE_GET_PRIVATE (self);
+	GSList *list, *iter;
+	gboolean pan = FALSE, dun = FALSE;
+
+	/* Retrieve initial enabled state for both PAN and DUN; if there are any
+	 * existing Bluetooth connections for the given device for either PAN
+	 * or DUN, then we consider that service enabled.
+	 */
+	list = nm_remote_settings_list_connections (priv->settings);
+	for (iter = list; iter != NULL; iter = g_slist_next (iter)) {
+		NMConnection *connection = iter->data;
+
+		if (match_connection_bdaddr (connection, priv->bdaddr_array)) {
+			NMSettingBluetooth *s_bt;
+			const char *type;
+
+			s_bt = nm_connection_get_setting_bluetooth (connection);
+			g_assert (s_bt);
+			type = nm_setting_bluetooth_get_connection_type (s_bt);
+			if (priv->has_pan && g_strcmp0 (type, NM_SETTING_BLUETOOTH_TYPE_PANU) == 0)
+				pan = TRUE;
+			else if (priv->has_dun && g_strcmp0 (type, NM_SETTING_BLUETOOTH_TYPE_DUN) == 0)
+				dun =  TRUE;
+		}
+	}
+	g_slist_free (list);
+
+	_set_pan_enabled (self, pan);
+	_set_dun_enabled (self, dun);
+}
+
+/*********************************************************************/
+
+const char *
+nma_bt_device_get_bdaddr (NmaBtDevice *device)
+{
+	return NMA_BT_DEVICE_GET_PRIVATE (device)->bdaddr;
+}
+
+gboolean
+nma_bt_device_get_busy (NmaBtDevice *device)
+{
+	return NMA_BT_DEVICE_GET_PRIVATE (device)->busy;
+}
+
+static void
+_set_busy (NmaBtDevice *device, gboolean busy)
+{
+	NmaBtDevicePrivate *priv = NMA_BT_DEVICE_GET_PRIVATE (device);
+
+	if (priv->busy != busy) {
+		priv->busy = busy;
+		g_object_notify (G_OBJECT (device), NMA_BT_DEVICE_BUSY);
+	}
+}
+
+const char *
+nma_bt_device_get_status (NmaBtDevice *device)
+{
+	return NMA_BT_DEVICE_GET_PRIVATE (device)->status;
+}
+
+static void
+_set_status (NmaBtDevice *device, const char *fmt, ...)
+{
+	NmaBtDevicePrivate *priv = NMA_BT_DEVICE_GET_PRIVATE (device);
+	va_list args;
+
+	g_free (priv->status);
+	priv->status = NULL;
+
+	if (fmt) {
+		va_start (args, fmt);
+		priv->status = g_strdup_vprintf (fmt, args);
+		va_end (args);
+		g_message ("%s", priv->status);
+	}
+
+	g_object_notify (G_OBJECT (device), NMA_BT_DEVICE_STATUS);
+}
+
+/*********************************************************************/
+
+static void
+dun_cleanup (NmaBtDevice *self)
+{
+	NmaBtDevicePrivate *priv = NMA_BT_DEVICE_GET_PRIVATE (self);
+	GSList *iter;
+
+	for (iter = priv->modem_proxies; iter; iter = g_slist_next (iter))
+		g_object_unref (DBUS_G_PROXY (iter->data));
+	g_slist_free (priv->modem_proxies);
+	priv->modem_proxies = NULL;
+
+	g_clear_object (&priv->mm_proxy);
+
+	if (priv->dun_proxy && priv->rfcomm_iface) {
+		dbus_g_proxy_call_no_reply (priv->dun_proxy, "Disconnect",
+		                            G_TYPE_STRING, priv->rfcomm_iface,
+		                            G_TYPE_INVALID);
+	}
+	g_clear_object (&priv->dun_proxy);
+
+	g_free (priv->rfcomm_iface);
+	priv->rfcomm_iface = NULL;
+
+	if (priv->dun_timeout_id) {
+		g_source_remove (priv->dun_timeout_id);
+		priv->dun_timeout_id = 0;
+	}
+
+	if (priv->wizard) {
+		nma_mobile_wizard_destroy (priv->wizard);
+		priv->wizard = NULL;
+	}
+}
+
+static void
+dun_error (NmaBtDevice *self, const char *func, GError *error, const char *fallback)
+{
+	g_warning ("%s: DUN error: %s", func, (error && error->message) ? error->message : fallback);
+	_set_status (self, _("Error: %s"), (error && error->message) ? error->message : fallback);
+
+	_set_busy (self, FALSE);
+	dun_cleanup (self);
+	recheck_services_enabled (self);
+}
+
+static char *
+get_connection_name (NMAMobileWizardAccessMethod *method)
+{
+	if (method->plan_name) {
+		/* Results in something like "T-Mobile ToGo (prepaid)" */
+		return g_strdup_printf (_("%s %s"), method->provider_name, method->plan_name);
+	}
+
+	/* Results in something like "T-Mobile connection" */
+	return g_strdup_printf (_("%s connection"), method->provider_name);
+}
+
+static NMConnection *
+dun_new_cdma (NMAMobileWizardAccessMethod *method)
+{
+	NMConnection *connection;
+	NMSetting *setting;
+	char *uuid, *id;
+
+	connection = nm_connection_new ();
+
+	setting = nm_setting_cdma_new ();
+	g_object_set (setting,
+	              NM_SETTING_CDMA_NUMBER, "#777",
+	              NM_SETTING_CDMA_USERNAME, method->username,
+	              NM_SETTING_CDMA_PASSWORD, method->password,
+	              NULL);
+	nm_connection_add_setting (connection, setting);
+
+	/* Serial setting */
+	setting = nm_setting_serial_new ();
+	g_object_set (setting,
+	              NM_SETTING_SERIAL_BAUD, 115200,
+	              NM_SETTING_SERIAL_BITS, 8,
+	              NM_SETTING_SERIAL_PARITY, 'n',
+	              NM_SETTING_SERIAL_STOPBITS, 1,
+	              NULL);
+	nm_connection_add_setting (connection, setting);
+
+	nm_connection_add_setting (connection, nm_setting_ppp_new ());
+
+	setting = nm_setting_connection_new ();
+	id = get_connection_name (method);
+	uuid = nm_utils_uuid_generate ();
+	g_object_set (setting,
+	              NM_SETTING_CONNECTION_ID, id,
+	              NM_SETTING_CONNECTION_TYPE, NM_SETTING_BLUETOOTH_SETTING_NAME,
+	              NM_SETTING_CONNECTION_AUTOCONNECT, FALSE,
+	              NM_SETTING_CONNECTION_UUID, uuid,
+	              NULL);
+	g_free (uuid);
+	g_free (id);
+	nm_connection_add_setting (connection, setting);
+
+	return connection;
+}
+
+static NMConnection *
+dun_new_gsm (NMAMobileWizardAccessMethod *method)
+{
+	NMConnection *connection;
+	NMSetting *setting;
+	char *uuid, *id;
+
+	connection = nm_connection_new ();
+
+	setting = nm_setting_gsm_new ();
+	g_object_set (setting,
+	              NM_SETTING_GSM_NUMBER, "*99#",
+	              NM_SETTING_GSM_USERNAME, method->username,
+	              NM_SETTING_GSM_PASSWORD, method->password,
+	              NM_SETTING_GSM_APN, method->gsm_apn,
+	              NULL);
+	nm_connection_add_setting (connection, setting);
+
+	/* Serial setting */
+	setting = nm_setting_serial_new ();
+	g_object_set (setting,
+	              NM_SETTING_SERIAL_BAUD, 115200,
+	              NM_SETTING_SERIAL_BITS, 8,
+	              NM_SETTING_SERIAL_PARITY, 'n',
+	              NM_SETTING_SERIAL_STOPBITS, 1,
+	              NULL);
+	nm_connection_add_setting (connection, setting);
+
+	nm_connection_add_setting (connection, nm_setting_ppp_new ());
+
+	setting = nm_setting_connection_new ();
+	id = get_connection_name (method);
+	uuid = nm_utils_uuid_generate ();
+	g_object_set (setting,
+	              NM_SETTING_CONNECTION_ID, id,
+	              NM_SETTING_CONNECTION_TYPE, NM_SETTING_BLUETOOTH_SETTING_NAME,
+	              NM_SETTING_CONNECTION_AUTOCONNECT, FALSE,
+	              NM_SETTING_CONNECTION_UUID, uuid,
+	              NULL);
+	g_free (uuid);
+	g_free (id);
+	nm_connection_add_setting (connection, setting);
+
+	return connection;
+}
+
+static void
+dun_add_cb (NMRemoteSettings *settings,
+            NMRemoteConnection *connection,
+            GError *error,
+            gpointer user_data)
+{
+	NmaBtDevice *self = NMA_BT_DEVICE (user_data);
+
+	if (error)
+		_set_status (self, _("Failed to create DUN connection: %s"), error->message);
+	else
+		_set_status (self, _("Your phone is now ready to use!"));
+
+	_set_busy (self, FALSE);
+	dun_cleanup (self);
+	recheck_services_enabled (self);
+}
+
+static void
+wizard_done_cb (NMAMobileWizard *wizard,
+                gboolean canceled,
+                NMAMobileWizardAccessMethod *method,
+                gpointer user_data)
+{
+	NmaBtDevice *self = NMA_BT_DEVICE (user_data);
+	NmaBtDevicePrivate *priv = NMA_BT_DEVICE_GET_PRIVATE (self);
+	NMConnection *connection = NULL;
+	NMSetting *s_bt;
+
+	g_return_if_fail (wizard == priv->wizard);
+
+	g_message ("%s: mobile wizard done", __func__);
+
+	if (canceled || !method) {
+		dun_error (self, __func__, NULL, _("Mobile wizard was canceled"));
+		return;
+	}
+
+	if (method->devtype == NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO)
+		connection = dun_new_cdma (method);
+	else if (method->devtype == NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS)
+		connection = dun_new_gsm (method);
+	else {
+		dun_error (self, __func__, NULL, _("Unknown phone device type (not GSM or CDMA)"));
+		return;
+	}
+
+	nma_mobile_wizard_destroy (priv->wizard);
+	priv->wizard = NULL;
+
+	g_assert (connection);
+
+	/* The Bluetooth settings */
+	s_bt = nm_setting_bluetooth_new ();
+	g_object_set (G_OBJECT (s_bt),
+	              NM_SETTING_BLUETOOTH_BDADDR, priv->bdaddr_array,
+	              NM_SETTING_BLUETOOTH_TYPE, NM_SETTING_BLUETOOTH_TYPE_DUN,
+	              NULL);
+	nm_connection_add_setting (connection, s_bt);
+
+	g_message ("%s: adding new setting", __func__);
+
+	/* Add the connection to the settings service */
+	nm_remote_settings_add_connection (priv->settings,
+	                                   connection,
+	                                   dun_add_cb,
+	                                   self);
+
+	g_message ("%s: waiting for add connection result...", __func__);
+}
+
+static void
+modem_get_all_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
+{
+	NmaBtDevice *self = NMA_BT_DEVICE (user_data);
+	NmaBtDevicePrivate *priv = NMA_BT_DEVICE_GET_PRIVATE (self);
+	const char *path;
+	GHashTable *properties = NULL;
+	GError *error = NULL;
+	GValue *value;
+	NMDeviceType devtype = NM_DEVICE_TYPE_UNKNOWN;
+
+	path = dbus_g_proxy_get_path (proxy);
+	g_message ("%s: (%s) processing GetAll reply", __func__, path);
+
+	if (!dbus_g_proxy_end_call (proxy, call, &error,
+	                            DBUS_TYPE_G_MAP_OF_VARIANT, &properties,
+	                            G_TYPE_INVALID)) {
+		g_warning ("%s: (%s) Error getting modem properties: (%d) %s",
+		           __func__,
+		           path,
+		           error ? error->code : -1,
+		           (error && error->message) ? error->message : "(unknown)");
+		g_error_free (error);
+		goto out;
+	}
+
+	/* check whether this is the device we care about */
+	value = g_hash_table_lookup (properties, "Device");
+	if (value && G_VALUE_HOLDS_STRING (value) && g_value_get_string (value)) {
+		char *iface_basename = g_path_get_basename (priv->rfcomm_iface);
+		const char *modem_iface = g_value_get_string (value);
+
+		if (strcmp (iface_basename, modem_iface) == 0) {
+			/* yay, found it! */
+
+			value = g_hash_table_lookup (properties, "Type");
+			if (value && G_VALUE_HOLDS_UINT (value)) {
+				switch (g_value_get_uint (value)) {
+				case 1:
+					devtype = NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS;
+					break;
+				case 2:
+					devtype = NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO;
+					break;
+				default:
+					g_message ("%s: (%s) unknown modem type", __func__, path);
+					break;
+				}
+			}
+		} else {
+			g_message ("%s: (%s) (%s) not the modem we're looking for (%s)",
+			           __func__, path, modem_iface, iface_basename);
+		}
+
+		g_free (iface_basename);
+	} else
+		g_message ("%s: (%s) modem had no 'Device' property", __func__, path);
+
+	g_hash_table_unref (properties);
+
+	if (devtype != NM_DEVICE_TYPE_UNKNOWN) {
+		if (priv->wizard) {
+			g_message ("%s: (%s) oops! not starting Wizard as one is already in progress", __func__, path);
+			goto out;
+		}
+
+		g_message ("%s: (%s) starting the mobile wizard", __func__, path);
+
+		g_source_remove (priv->dun_timeout_id);
+		priv->dun_timeout_id = 0;
+
+		/* Start the mobile wizard */
+		priv->wizard = nma_mobile_wizard_new (priv->parent_window,
+											  priv->window_group,
+											  devtype,
+											  FALSE,
+											  wizard_done_cb,
+											  self);
+		nma_mobile_wizard_present (priv->wizard);
+	} else {
+		dun_error (self, __func__, NULL, _("unknown modem type."));
+	}
+
+out:
+	g_message ("%s: finished", __func__);
+}
+
+static void
+modem_added (DBusGProxy *proxy, const char *path, gpointer user_data)
+{
+	NmaBtDevice *self = NMA_BT_DEVICE (user_data);
+	NmaBtDevicePrivate *priv = NMA_BT_DEVICE_GET_PRIVATE (self);
+	DBusGProxy *props_proxy;
+
+	g_return_if_fail (path != NULL);
+
+	g_message ("%s: (%s) modem found", __func__, path);
+
+	/* Create a proxy for the modem and get its properties */
+	props_proxy = dbus_g_proxy_new_for_name (priv->bus,
+	                                         MM_SERVICE,
+	                                         path,
+	                                         "org.freedesktop.DBus.Properties");
+	g_assert (proxy);
+	priv->modem_proxies = g_slist_append (priv->modem_proxies, props_proxy);
+
+	g_message ("%s: (%s) calling GetAll...", __func__, path);
+
+	dbus_g_proxy_begin_call (props_proxy, "GetAll",
+	                         modem_get_all_cb,
+	                         self,
+	                         NULL,
+	                         G_TYPE_STRING, MM_MODEM_INTERFACE,
+	                         G_TYPE_INVALID);
+}
+
+static void
+modem_removed (DBusGProxy *proxy, const char *path, gpointer user_data)
+{
+	NmaBtDevice *self = NMA_BT_DEVICE (user_data);
+	NmaBtDevicePrivate *priv = NMA_BT_DEVICE_GET_PRIVATE (self);
+	GSList *iter;
+
+	g_return_if_fail (path != NULL);
+
+	g_message ("%s: (%s) modem removed", __func__, path);
+
+	/* Clean up if a modem gets removed */
+	for (iter = priv->modem_proxies; iter; iter = g_slist_next (iter)) {
+		if (!strcmp (path, dbus_g_proxy_get_path (DBUS_G_PROXY (iter->data)))) {
+			priv->modem_proxies = g_slist_remove (priv->modem_proxies, iter->data);
+			g_object_unref (iter->data);
+			break;
+		}
+	}
+}
+
+static void
+dun_connect_cb (DBusGProxy *proxy,
+                DBusGProxyCall *call,
+                void *user_data)
+{
+	NmaBtDevice *self = NMA_BT_DEVICE (user_data);
+	NmaBtDevicePrivate *priv = NMA_BT_DEVICE_GET_PRIVATE (self);
+	GError *error = NULL;
+	char *device;
+
+	g_message ("%s: processing Connect reply", __func__);
+
+	if (!dbus_g_proxy_end_call (proxy, call, &error,
+	                            G_TYPE_STRING, &device,
+	                            G_TYPE_INVALID)) {
+		dun_error (self, __func__, error, _("failed to connect to the phone."));
+		g_clear_error (&error);
+		goto out;
+	}
+
+	if (!device || !strlen (device)) {
+		dun_error (self, __func__, NULL, _("failed to connect to the phone."));
+		g_free (device);
+		goto out;
+	}
+
+	g_free (priv->rfcomm_iface);
+	priv->rfcomm_iface = device;
+	g_message ("%s: new rfcomm interface '%s'", __func__, device);
+
+out:
+	g_message ("%s: finished", __func__);
+}
+
+static void
+dun_property_changed (DBusGProxy *proxy,
+                      const char *property,
+                      GValue *value,
+                      gpointer user_data)
+{
+	NmaBtDevice *self = NMA_BT_DEVICE (user_data);
+	gboolean connected;
+
+	if (strcmp (property, "Connected") == 0) {
+		connected = g_value_get_boolean (value);
+		g_message ("%s: device property Connected changed to %s",
+			       __func__,
+			       connected ? "TRUE" : "FALSE");
+
+		if (connected) {
+			/* Wait for MM here ? */
+		} else
+			dun_error (self, __func__, NULL, _("unexpectedly disconnected from the phone."));
+	}
+}
+
+static gboolean
+dun_timeout_cb (gpointer user_data)
+{
+	NmaBtDevice *self = NMA_BT_DEVICE (user_data);
+
+	NMA_BT_DEVICE_GET_PRIVATE (self)->dun_timeout_id = 0;
+	dun_error (self, __func__, NULL, _("timed out detecting phone details."));
+	return FALSE;
+}
+
+static void
+dun_start (NmaBtDevice *self)
+{
+	NmaBtDevicePrivate *priv = NMA_BT_DEVICE_GET_PRIVATE (self);
+
+	g_message ("%s: starting DUN device discovery...", __func__);
+
+	_set_status (self, _("Detecting phone configuration..."));
+
+	/* ModemManager stuff */
+	priv->mm_proxy = dbus_g_proxy_new_for_name (priv->bus,
+	                                            MM_SERVICE,
+	                                            MM_PATH,
+	                                            MM_INTERFACE);
+	g_assert (priv->mm_proxy);
+
+	dbus_g_object_register_marshaller (g_cclosure_marshal_VOID__BOXED,
+	                                   G_TYPE_NONE,
+	                                   G_TYPE_BOXED,
+	                                   G_TYPE_INVALID);
+	dbus_g_proxy_add_signal (priv->mm_proxy, "DeviceAdded",
+	                         DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal (priv->mm_proxy, "DeviceAdded",
+								 G_CALLBACK (modem_added), self,
+								 NULL);
+
+	dbus_g_proxy_add_signal (priv->mm_proxy, "DeviceRemoved",
+	                         DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal (priv->mm_proxy, "DeviceRemoved",
+								 G_CALLBACK (modem_removed), self,
+								 NULL);
+
+	priv->dun_proxy = dbus_g_proxy_new_for_name (priv->bus,
+	                                             BLUEZ_SERVICE,
+	                                             priv->object_path,
+	                                             BLUEZ_SERIAL_INTERFACE);
+	g_assert (priv->dun_proxy);
+
+	priv->dun_timeout_id = g_timeout_add_seconds (45, dun_timeout_cb, self);
+
+	g_message ("%s: calling Connect...", __func__);
+
+	/* Watch for BT device property changes */
+	dbus_g_object_register_marshaller (_nma_marshal_VOID__STRING_BOXED,
+	                                   G_TYPE_NONE,
+	                                   G_TYPE_STRING, G_TYPE_VALUE,
+	                                   G_TYPE_INVALID);
+	dbus_g_proxy_add_signal (priv->dun_proxy, "PropertyChanged",
+	                         G_TYPE_STRING, G_TYPE_VALUE, G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal (priv->dun_proxy, "PropertyChanged",
+	                             G_CALLBACK (dun_property_changed), self, NULL);
+
+	/* Request a connection to the device and get the port */
+	dbus_g_proxy_begin_call_with_timeout (priv->dun_proxy, "Connect",
+	                                      dun_connect_cb,
+	                                      self,
+	                                      NULL,
+	                                      20000,
+	                                      G_TYPE_STRING, "dun",
+	                                      G_TYPE_INVALID);
+
+	g_message ("%s: waiting for Connect success...", __func__);
+}
+
+gboolean
+nma_bt_device_get_has_dun (NmaBtDevice *device)
+{
+	return NMA_BT_DEVICE_GET_PRIVATE (device)->has_dun;
+}
+
+gboolean
+nma_bt_device_get_dun_enabled (NmaBtDevice *device)
+{
+	return NMA_BT_DEVICE_GET_PRIVATE (device)->dun_enabled;
+}
+
+static void
+_set_dun_enabled (NmaBtDevice *device, gboolean enabled)
+{
+	NmaBtDevicePrivate *priv = NMA_BT_DEVICE_GET_PRIVATE (device);
+
+	if (priv->dun_enabled != enabled) {
+		priv->dun_enabled = enabled;
+		g_object_notify (G_OBJECT (device), NMA_BT_DEVICE_DUN_ENABLED);
+	}
+}
+
+void
+nma_bt_device_set_dun_enabled (NmaBtDevice *device, gboolean enabled)
+{
+	NmaBtDevicePrivate *priv = NMA_BT_DEVICE_GET_PRIVATE (device);
+
+	_set_dun_enabled (device, enabled);
+
+	if (enabled) {
+		_set_busy (device, TRUE);
+		dun_start (device);
+	} else
+		delete_connections_of_type (priv->settings, priv->bdaddr_array, FALSE);
+}
+
+void
+nma_bt_device_cancel_dun (NmaBtDevice *device)
+{
+	dun_error (device, __func__, NULL, _("The default Bluetooth adapter must be enabled before setting up a Dial-Up-Networking connection."));
+}
+
+/*********************************************************************/
+
+gboolean
+nma_bt_device_get_has_pan (NmaBtDevice *device)
+{
+	return NMA_BT_DEVICE_GET_PRIVATE (device)->has_pan;
+}
+
+gboolean
+nma_bt_device_get_pan_enabled (NmaBtDevice *device)
+{
+	return NMA_BT_DEVICE_GET_PRIVATE (device)->pan_enabled;
+}
+
+static void
+_set_pan_enabled (NmaBtDevice *device, gboolean enabled)
+{
+	NmaBtDevicePrivate *priv = NMA_BT_DEVICE_GET_PRIVATE (device);
+
+	if (priv->pan_enabled != enabled) {
+		priv->pan_enabled = enabled;
+		g_object_notify (G_OBJECT (device), NMA_BT_DEVICE_PAN_ENABLED);
+	}
+}
+
+static void
+pan_add_cb (NMRemoteSettings *settings,
+            NMRemoteConnection *connection,
+            GError *error,
+            gpointer user_data)
+{
+	NmaBtDevice *self = NMA_BT_DEVICE (user_data);
+
+	if (error)
+		_set_status (self, _("Failed to create PAN connection: %s"), error->message);
+	else
+		_set_status (self, _("Your phone is now ready to use!"));
+
+	recheck_services_enabled (self);
+	_set_busy (self, FALSE);
+}
+
+static void
+add_pan_connection (NmaBtDevice *self)
+{
+	NmaBtDevicePrivate *priv = NMA_BT_DEVICE_GET_PRIVATE (self);
+	NMConnection *connection;
+	NMSetting *setting, *bt_setting, *ip_setting;
+	char *id, *uuid;
+
+	/* The connection */
+	connection = nm_connection_new ();
+
+	/* The connection settings */
+	setting = nm_setting_connection_new ();
+	id = g_strdup_printf (_("%s Network"), priv->alias ? priv->alias : priv->bdaddr);
+	uuid = nm_utils_uuid_generate ();
+	g_object_set (G_OBJECT (setting),
+	              NM_SETTING_CONNECTION_ID, id,
+	              NM_SETTING_CONNECTION_UUID, uuid,
+	              NM_SETTING_CONNECTION_TYPE, NM_SETTING_BLUETOOTH_SETTING_NAME,
+	              NM_SETTING_CONNECTION_AUTOCONNECT, FALSE,
+	              NULL);
+	g_free (id);
+	g_free (uuid);
+	nm_connection_add_setting (connection, setting);
+
+	/* The Bluetooth settings */
+	bt_setting = nm_setting_bluetooth_new ();
+	g_object_set (G_OBJECT (bt_setting),
+	              NM_SETTING_BLUETOOTH_BDADDR, priv->bdaddr_array,
+	              NM_SETTING_BLUETOOTH_TYPE, NM_SETTING_BLUETOOTH_TYPE_PANU,
+	              NULL);
+	nm_connection_add_setting (connection, bt_setting);
+
+	/* IPv4 */
+	ip_setting = nm_setting_ip4_config_new ();
+	g_object_set (G_OBJECT (ip_setting),
+	              NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO,
+	              NM_SETTING_IP6_CONFIG_MAY_FAIL, FALSE,
+	              NULL);
+	nm_connection_add_setting (connection, ip_setting);
+
+	/* IPv6 */
+	ip_setting = nm_setting_ip6_config_new ();
+	g_object_set (G_OBJECT (ip_setting),
+	              NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO,
+	              NM_SETTING_IP6_CONFIG_MAY_FAIL, TRUE,
+	              NULL);
+	nm_connection_add_setting (connection, ip_setting);
+
+	/* Add the connection to the settings service */
+	nm_remote_settings_add_connection (priv->settings,
+	                                   connection,
+	                                   pan_add_cb,
+	                                   self);
+}
+
+void
+nma_bt_device_set_pan_enabled (NmaBtDevice *device, gboolean enabled)
+{
+	NmaBtDevicePrivate *priv = NMA_BT_DEVICE_GET_PRIVATE (device);
+
+	_set_pan_enabled (device, enabled);
+
+	if (enabled) {
+		_set_busy (device, TRUE);
+		add_pan_connection (device);
+	} else
+		delete_connections_of_type (priv->settings, priv->bdaddr_array, TRUE);
+}
+
+/*********************************************************************/
+
+void
+nma_bt_device_set_parent_window (NmaBtDevice *device, GtkWindow *window)
+{
+	NmaBtDevicePrivate *priv = NMA_BT_DEVICE_GET_PRIVATE (device);
+
+	if (window == priv->parent_window)
+		return;
+
+	if (priv->parent_window) {
+		gtk_window_group_remove_window (priv->window_group, priv->parent_window);
+		g_object_unref (priv->parent_window);
+	}
+	priv->parent_window = g_object_ref (window);
+	gtk_window_group_add_window (priv->window_group, window);
+}
+
+/*********************************************************************/
+
+static void
+connections_read (NMRemoteSettings *settings, gpointer user_data)
+{
+	recheck_services_enabled (NMA_BT_DEVICE (user_data));
+}
+
+NmaBtDevice *
+nma_bt_device_new (const char *bdaddr,
+                   const char *alias,
+                   const char *object_path,
+                   gboolean has_pan,
+                   gboolean has_dun)
+{
+	NmaBtDevice *self;
+	GError *error = NULL;
+
+	g_return_val_if_fail (bdaddr != NULL, NULL);
+	g_return_val_if_fail (object_path != NULL, NULL);
+
+	self = (NmaBtDevice *) g_object_new (NMA_TYPE_BT_DEVICE,
+	                                     NMA_BT_DEVICE_BDADDR, bdaddr,
+	                                     NMA_BT_DEVICE_ALIAS, alias,
+	                                     NMA_BT_DEVICE_OBJECT_PATH, object_path,
+	                                     NMA_BT_DEVICE_HAS_PAN, has_pan,
+	                                     NMA_BT_DEVICE_HAS_DUN, has_dun,
+	                                     NULL);
+	if (self) {
+		NmaBtDevicePrivate *priv = NMA_BT_DEVICE_GET_PRIVATE (self);
+		struct ether_addr *addr;
+
+		g_assert (priv->bdaddr);
+		g_assert (priv->object_path);
+
+		addr = ether_aton (priv->bdaddr);
+		if (!addr) {
+			g_warning ("%s: invalid Bluetooth address '%s'", __func__, priv->bdaddr);
+			g_object_unref (self);
+			return NULL;
+		}
+
+		priv->bdaddr_array = g_byte_array_sized_new (ETH_ALEN);
+		g_byte_array_append (priv->bdaddr_array, (const guint8 *) addr->ether_addr_octet, ETH_ALEN);
+
+		priv->bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
+		if (error) {
+			g_warning ("%s: failed to connect to D-Bus: %s", __func__, error->message);
+			g_object_unref (self);
+			self = NULL;
+		}
+
+		priv->window_group = gtk_window_group_new ();
+
+		priv->settings = nm_remote_settings_new (priv->bus);
+		g_signal_connect (priv->settings,
+				          NM_REMOTE_SETTINGS_CONNECTIONS_READ,
+				          G_CALLBACK (connections_read),
+				          self);
+	}
+	return self;
+}
+
+static void
+nma_bt_device_init (NmaBtDevice *self)
+{
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	NmaBtDevicePrivate *priv = NMA_BT_DEVICE_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_BDADDR:
+		g_value_set_string (value, priv->bdaddr);
+		break;
+	case PROP_ALIAS:
+		g_value_set_string (value, priv->alias);
+		break;
+	case PROP_OBJECT_PATH:
+		g_value_set_string (value, priv->object_path);
+		break;
+	case PROP_HAS_PAN:
+		g_value_set_boolean (value, priv->has_pan);
+		break;
+	case PROP_PAN_ENABLED:
+		g_value_set_boolean (value, priv->pan_enabled);
+		break;
+	case PROP_HAS_DUN:
+		g_value_set_boolean (value, priv->has_dun);
+		break;
+	case PROP_DUN_ENABLED:
+		g_value_set_boolean (value, priv->dun_enabled);
+		break;
+	case PROP_BUSY:
+		g_value_set_boolean (value, priv->busy);
+		break;
+	case PROP_STATUS:
+		g_value_set_string (value, priv->status);
+		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)
+{
+	NmaBtDevicePrivate *priv = NMA_BT_DEVICE_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_BDADDR:
+		priv->bdaddr = g_value_dup_string (value);
+		break;
+	case PROP_ALIAS:
+		priv->alias = g_value_dup_string (value);
+		break;
+	case PROP_OBJECT_PATH:
+		priv->object_path = g_value_dup_string (value);
+		break;
+	case PROP_HAS_PAN:
+		priv->has_pan = g_value_get_boolean (value);
+		break;
+	case PROP_HAS_DUN:
+		priv->has_dun = g_value_get_boolean (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+dispose (GObject *object)
+{
+	NmaBtDevicePrivate *priv = NMA_BT_DEVICE_GET_PRIVATE (object);
+
+	dun_cleanup (NMA_BT_DEVICE (object));
+
+	g_free (priv->bdaddr);
+	priv->bdaddr = NULL;
+	g_free (priv->alias);
+	priv->alias = NULL;
+	g_free (priv->object_path);
+	priv->object_path = NULL;
+	g_free (priv->status);
+	priv->status = NULL;
+
+	g_clear_object (&priv->window_group);
+	g_clear_object (&priv->parent_window);
+
+	if (priv->bdaddr_array) {
+		g_byte_array_free (priv->bdaddr_array, TRUE);
+		priv->bdaddr_array = NULL;
+	}
+
+	G_OBJECT_CLASS (nma_bt_device_parent_class)->dispose (object);
+}
+
+static void
+nma_bt_device_class_init (NmaBtDeviceClass *btdevice_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (btdevice_class);
+
+	g_type_class_add_private (btdevice_class, sizeof (NmaBtDevicePrivate));
+
+	/* virtual methods */
+	object_class->get_property = get_property;
+	object_class->set_property = set_property;
+	object_class->dispose = dispose;
+
+	/* properties */
+	g_object_class_install_property (object_class, PROP_BDADDR,
+		 g_param_spec_string (NMA_BT_DEVICE_BDADDR,
+		                      "Bluetooth address",
+		                      "Bluetooth address",
+		                      NULL,
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property (object_class, PROP_ALIAS,
+		 g_param_spec_string (NMA_BT_DEVICE_ALIAS,
+		                      "Bluetooth alias",
+		                      "Bluetooth alias",
+		                      NULL,
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property (object_class, PROP_OBJECT_PATH,
+		 g_param_spec_string (NMA_BT_DEVICE_OBJECT_PATH,
+		                      "Bluez object path",
+		                      "Bluez object path",
+		                      NULL,
+		                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property (object_class, PROP_HAS_PAN,
+		 g_param_spec_boolean (NMA_BT_DEVICE_HAS_PAN,
+		                       "PAN capable",
+		                       "PAN capable",
+		                       FALSE,
+		                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property (object_class, PROP_PAN_ENABLED,
+		 g_param_spec_boolean (NMA_BT_DEVICE_PAN_ENABLED,
+		                       "PAN enabled",
+		                       "PAN enabled",
+		                       FALSE,
+		                       G_PARAM_READABLE));
+
+	g_object_class_install_property (object_class, PROP_HAS_DUN,
+		 g_param_spec_boolean (NMA_BT_DEVICE_HAS_DUN,
+		                       "DUN capable",
+		                       "DUN capable",
+		                       FALSE,
+		                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property (object_class, PROP_DUN_ENABLED,
+		 g_param_spec_boolean (NMA_BT_DEVICE_DUN_ENABLED,
+		                       "DUN enabled",
+		                       "DUN enabled",
+		                       FALSE,
+		                       G_PARAM_READABLE));
+
+	g_object_class_install_property (object_class, PROP_BUSY,
+		 g_param_spec_boolean (NMA_BT_DEVICE_BUSY,
+		                       "Busy",
+		                       "Busy",
+		                       FALSE,
+		                       G_PARAM_READABLE));
+
+	g_object_class_install_property (object_class, PROP_STATUS,
+		 g_param_spec_string (NMA_BT_DEVICE_STATUS,
+		                      "Status",
+		                      "Status",
+		                      NULL,
+		                      G_PARAM_READABLE));
+}
diff --git a/src/gnome-bluetooth/nma-bt-device.h b/src/gnome-bluetooth/nma-bt-device.h
new file mode 100644
index 0000000..a09fedb
--- /dev/null
+++ b/src/gnome-bluetooth/nma-bt-device.h
@@ -0,0 +1,84 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ *  NetworkManager Applet
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * (C) Copyright 2009 - 2012 Red Hat, Inc.
+ *
+ */
+
+#ifndef NMA_BT_DEVICE_H
+#define NMA_BT_DEVICE_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <dbus/dbus-glib.h>
+
+#define NMA_TYPE_BT_DEVICE            (nma_bt_device_get_type ())
+#define NMA_BT_DEVICE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMA_TYPE_BT_DEVICE, NmaBtDevice))
+#define NMA_BT_DEVICE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMA_TYPE_BT_DEVICE, NmaBtDeviceClass))
+#define NMA_IS_BT_DEVICE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMA_TYPE_BT_DEVICE))
+#define NMA_IS_BT_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMA_TYPE_BT_DEVICE))
+#define NMA_BT_DEVICE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMA_TYPE_BT_DEVICE, NmaBtDeviceClass))
+
+#define NMA_BT_DEVICE_BDADDR      "bdaddr"
+#define NMA_BT_DEVICE_ALIAS       "alias"
+#define NMA_BT_DEVICE_OBJECT_PATH "object-path"
+#define NMA_BT_DEVICE_HAS_PAN     "has-pan"
+#define NMA_BT_DEVICE_PAN_ENABLED "pan-enabled"
+#define NMA_BT_DEVICE_HAS_DUN     "has-dun"
+#define NMA_BT_DEVICE_DUN_ENABLED "dun-enabled"
+#define NMA_BT_DEVICE_BUSY        "busy"
+#define NMA_BT_DEVICE_STATUS      "status"
+
+typedef struct {
+	GObject parent;
+} NmaBtDevice;
+
+typedef struct {
+	GObjectClass parent;
+} NmaBtDeviceClass;
+
+GType nma_bt_device_get_type (void);
+
+NmaBtDevice *nma_bt_device_new (const char *bdaddr,
+                                const char *alias,
+                                const char *object_path,
+                                gboolean has_pan,
+                                gboolean has_dun);
+
+void nma_bt_device_set_parent_window   (NmaBtDevice *device,
+                                        GtkWindow *window);
+
+const char *nma_bt_device_get_bdaddr   (NmaBtDevice *device);
+
+gboolean nma_bt_device_get_has_dun     (NmaBtDevice *device);
+gboolean nma_bt_device_get_dun_enabled (NmaBtDevice *device);
+void     nma_bt_device_set_dun_enabled (NmaBtDevice *device, gboolean enabled);
+
+void     nma_bt_device_cancel_dun      (NmaBtDevice *device);
+
+gboolean nma_bt_device_get_has_pan     (NmaBtDevice *device);
+gboolean nma_bt_device_get_pan_enabled (NmaBtDevice *device);
+void     nma_bt_device_set_pan_enabled (NmaBtDevice *device, gboolean enabled);
+
+gboolean nma_bt_device_get_busy (NmaBtDevice *device);
+
+const char *nma_bt_device_get_status (NmaBtDevice *device);
+
+#endif /* NMA_BT_DEVICE_H */
+



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]