about summary refs log tree commit diff
path: root/src/bluez-manager/nm-bluez-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/bluez-manager/nm-bluez-manager.c')
-rw-r--r--src/bluez-manager/nm-bluez-manager.c330
1 files changed, 191 insertions, 139 deletions
diff --git a/src/bluez-manager/nm-bluez-manager.c b/src/bluez-manager/nm-bluez-manager.c
index a6848631..3801d5ee 100644
--- a/src/bluez-manager/nm-bluez-manager.c
+++ b/src/bluez-manager/nm-bluez-manager.c
@@ -17,21 +17,20 @@
  *
  * Copyright (C) 2007 - 2008 Novell, Inc.
  * Copyright (C) 2007 - 2012 Red Hat, Inc.
+ * Copyright (C) 2013 Intel Corporation.
  */
 
 #include <signal.h>
 #include <string.h>
 #include <stdlib.h>
-#include <dbus/dbus-glib.h>
+#include <gio/gio.h>
 
 #include "nm-logging.h"
-#include "nm-dbus-glib-types.h"
-#include "nm-marshal.h"
 #include "nm-bluez-manager.h"
-#include "nm-bluez-adapter.h"
-#include "nm-dbus-manager.h"
+#include "nm-bluez-device.h"
 #include "nm-bluez-common.h"
 
+#include "nm-dbus-manager.h"
 
 typedef struct {
 	NMDBusManager *dbus_mgr;
@@ -39,9 +38,9 @@ typedef struct {
 
 	NMConnectionProvider *provider;
 
-	DBusGProxy *proxy;
+	GDBusProxy *proxy;
 
-	NMBluezAdapter *adapter;
+	GHashTable *devices;
 } NMBluezManagerPrivate;
 
 #define NM_BLUEZ_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_BLUEZ_MANAGER, NMBluezManagerPrivate))
@@ -57,11 +56,14 @@ enum {
 
 static guint signals[LAST_SIGNAL] = { 0 };
 
-static void
+static void device_initialized (NMBluezDevice *device, gboolean success, NMBluezManager *self);
+static void device_usable (NMBluezDevice *device, GParamSpec *pspec, NMBluezManager *self);
 
+static void
 emit_bdaddr_added (NMBluezManager *self, NMBluezDevice *device)
 {
 	g_signal_emit (self, signals[BDADDR_ADDED], 0,
+	               device,
 	               nm_bluez_device_get_address (device),
 	               nm_bluez_device_get_name (device),
 	               nm_bluez_device_get_path (device),
@@ -72,173 +74,216 @@ void
 nm_bluez_manager_query_devices (NMBluezManager *self)
 {
 	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
-	GSList *devices, *iter;
+	NMBluezDevice *device;
+	GHashTableIter iter;
 
-	if (!priv->adapter)
-		return;
+	g_hash_table_iter_init (&iter, priv->devices);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &device)) {
+		if (nm_bluez_device_get_usable (device))
+			emit_bdaddr_added (self, device);
+	}
+}
 
-	devices = nm_bluez_adapter_get_devices (priv->adapter);
-	for (iter = devices; iter; iter = g_slist_next (iter))
-		emit_bdaddr_added (self, NM_BLUEZ_DEVICE (iter->data));
-	g_slist_free (devices);
+static void
+remove_device (NMBluezManager *self, NMBluezDevice *device)
+{
+	if (nm_bluez_device_get_usable (device)) {
+		g_signal_emit (self, signals[BDADDR_REMOVED], 0,
+		               nm_bluez_device_get_address (device),
+		               nm_bluez_device_get_path (device));
+	}
+	g_signal_handlers_disconnect_by_func (device, G_CALLBACK (device_initialized), self);
+	g_signal_handlers_disconnect_by_func (device, G_CALLBACK (device_usable), self);
 }
 
 static void
-device_added (NMBluezAdapter *adapter, NMBluezDevice *device, gpointer user_data)
+remove_all_devices (NMBluezManager *self)
 {
-	emit_bdaddr_added (NM_BLUEZ_MANAGER (user_data), device);
+	GHashTableIter iter;
+	NMBluezDevice *device;
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+
+	g_hash_table_iter_init (&iter, priv->devices);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &device)) {
+		g_hash_table_iter_steal (&iter);
+		remove_device (self, device);
+		g_object_unref (device);
+	}
 }
 
 static void
-device_removed (NMBluezAdapter *adapter, NMBluezDevice *device, gpointer user_data)
+device_usable (NMBluezDevice *device, GParamSpec *pspec, NMBluezManager *self)
 {
-	NMBluezManager *self = NM_BLUEZ_MANAGER (user_data);
+	gboolean usable = nm_bluez_device_get_usable (device);
+
+	nm_log_dbg (LOGD_BT, "(%s): bluez device now %s",
+	            nm_bluez_device_get_path (device),
+	            usable ? "usable" : "unusable");
+
+	if (usable) {
+		nm_log_dbg (LOGD_BT, "(%s): bluez device address %s",
+				    nm_bluez_device_get_path (device),
+				    nm_bluez_device_get_address (device));
+		emit_bdaddr_added (self, device);
+	} else
+		g_signal_emit (self, signals[BDADDR_REMOVED], 0,
+		               nm_bluez_device_get_address (device),
+		               nm_bluez_device_get_path (device));
+}
 
-	g_signal_emit (self, signals[BDADDR_REMOVED], 0,
-	               nm_bluez_device_get_address (device),
-	               nm_bluez_device_get_path (device));
+static void
+device_initialized (NMBluezDevice *device, gboolean success, NMBluezManager *self)
+{
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+
+	nm_log_dbg (LOGD_BT, "(%s): bluez device %s",
+	            nm_bluez_device_get_path (device),
+	            success ? "initialized" : "failed to initialize");
+	if (!success)
+		g_hash_table_remove (priv->devices, nm_bluez_device_get_path (device));
 }
 
 static void
-adapter_initialized (NMBluezAdapter *adapter, gboolean success, gpointer user_data)
+device_added (GDBusProxy *proxy, const gchar *path, NMBluezManager *self)
 {
-	NMBluezManager *self = NM_BLUEZ_MANAGER (user_data);
 	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+	NMBluezDevice *device;
+
+	device = nm_bluez_device_new (path, priv->provider);
+	g_signal_connect (device, "initialized", G_CALLBACK (device_initialized), self);
+	g_signal_connect (device, "notify::usable", G_CALLBACK (device_usable), self);
+	g_hash_table_insert (priv->devices, (gpointer) nm_bluez_device_get_path (device), device);
+
+	nm_log_dbg (LOGD_BT, "(%s): new bluez device found", path);
+}
 
-	if (success) {
-		GSList *devices, *iter;
+static void
+device_removed (GDBusProxy *proxy, const gchar *path, NMBluezManager *self)
+{
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+	NMBluezDevice *device;
 
-		devices = nm_bluez_adapter_get_devices (adapter);
-		for (iter = devices; iter; iter = g_slist_next (iter))
-			emit_bdaddr_added (self, NM_BLUEZ_DEVICE (iter->data));
-		g_slist_free (devices);
+	nm_log_dbg (LOGD_BT, "(%s): bluez device removed", path);
 
-		g_signal_connect (adapter, "device-added", G_CALLBACK (device_added), self);
-		g_signal_connect (adapter, "device-removed", G_CALLBACK (device_removed), self);
-	} else {
-		g_object_unref (priv->adapter);
-		priv->adapter = NULL;
+	device = g_hash_table_lookup (priv->devices, path);
+	if (device) {
+		g_hash_table_steal (priv->devices, nm_bluez_device_get_path (device));
+		remove_device (NM_BLUEZ_MANAGER (self), device);
+		g_object_unref (device);
 	}
 }
 
 static void
-adapter_removed (DBusGProxy *proxy, const char *path, NMBluezManager *self)
+object_manager_g_signal (GDBusProxy     *proxy,
+                         gchar          *sender_name,
+                         gchar          *signal_name,
+                         GVariant       *parameters,
+                         NMBluezManager *self)
 {
-	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
+	GVariant *variant;
+	const gchar *path;
 
-	if (priv->adapter && !strcmp (path, nm_bluez_adapter_get_path (priv->adapter))) {
-		if (nm_bluez_adapter_get_initialized (priv->adapter)) {
-			GSList *devices, *iter;
+	if (!strcmp (signal_name, "InterfacesRemoved")) {
+		const gchar **ifaces;
+		gsize i, length;
 
-			devices = nm_bluez_adapter_get_devices (priv->adapter);
-			for (iter = devices; iter; iter = g_slist_next (iter)) {
-				NMBluezDevice *device = NM_BLUEZ_DEVICE (iter->data);
+		g_variant_get (parameters, "(&o*)", &path, &variant);
 
-				g_signal_emit (self, signals[BDADDR_REMOVED], 0,
-				               nm_bluez_device_get_address (device),
-				               nm_bluez_device_get_path (device));
+		ifaces = g_variant_get_strv (variant, &length);
+
+		for (i = 0; i < length; i++) {
+			if (!strcmp (ifaces[i], BLUEZ_DEVICE_INTERFACE)) {
+				device_removed (proxy, path, self);
+				break;
 			}
-			g_slist_free (devices);
 		}
 
-		g_object_unref (priv->adapter);
-		priv->adapter = NULL;
+		g_free (ifaces);
+
+	} else if (!strcmp (signal_name, "InterfacesAdded")) {
+		g_variant_get (parameters, "(&o*)", &path, &variant);
+
+		if (g_variant_lookup_value (variant, BLUEZ_DEVICE_INTERFACE,
+		                            G_VARIANT_TYPE_DICTIONARY))
+			device_added (proxy, path, self);
 	}
 }
 
 static void
-default_adapter_changed (DBusGProxy *proxy, const char *path, NMBluezManager *self)
+get_managed_objects_cb (GDBusProxy *proxy,
+                        GAsyncResult *res,
+                        NMBluezManager *self)
 {
-	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
-	const char *cur_path = NULL;
-
-	if (priv->adapter)
-		cur_path = nm_bluez_adapter_get_path (priv->adapter);
-
-	if (cur_path) {
-		if (!path || strcmp (path, cur_path)) {
-			/* Default adapter changed */
-			adapter_removed (priv->proxy, cur_path, self);
-		} else {
-			/* This adapter is already the default */
-			return;
+	GVariant *variant, *ifaces;
+	GVariantIter i;
+	GError *error = NULL;
+	const char *path;
+
+	variant = g_dbus_proxy_call_finish (proxy, res, &error);
+
+	if (!variant) {
+		nm_log_warn (LOGD_BT, "Couldn't get managed objects: %s",
+		             error && error->message ? error->message : "(unknown)");
+		g_clear_error (&error);
+		return;
+	}
+	g_variant_iter_init (&i, g_variant_get_child_value (variant, 0));
+	while ((g_variant_iter_next (&i, "{&o*}", &path, &ifaces))) {
+		if (g_variant_lookup_value (ifaces, BLUEZ_DEVICE_INTERFACE,
+		                            G_VARIANT_TYPE_DICTIONARY)) {
+			device_added (proxy, path, self);
 		}
 	}
 
-	/* Add the new default adapter */
-	if (path) {
-		priv->adapter = nm_bluez_adapter_new (path, priv->provider);
-		g_signal_connect (priv->adapter, "initialized", G_CALLBACK (adapter_initialized), self);
-	}
+	g_variant_unref (variant);
 }
 
 static void
-default_adapter_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
+on_proxy_acquired (GObject *object,
+                   GAsyncResult *res,
+                   NMBluezManager *self)
 {
-	NMBluezManager *self = NM_BLUEZ_MANAGER (user_data);
 	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
-	const char *default_adapter = NULL;
-	GError *err = NULL;
-
-	if (!dbus_g_proxy_end_call (proxy, call, &err,
-	                            DBUS_TYPE_G_OBJECT_PATH, &default_adapter,
-	                            G_TYPE_INVALID)) {
-		/* Ignore "No such adapter" errors; just means bluetooth isn't active */
-		if (   !dbus_g_error_has_name (err, "org.bluez.Error.NoSuchAdapter")
-		    && !dbus_g_error_has_name (err, "org.freedesktop.systemd1.LoadFailed")
-		    && !g_error_matches (err, DBUS_GERROR, DBUS_GERROR_SERVICE_UNKNOWN)) {
-			nm_log_warn (LOGD_BT, "bluez error getting default adapter: %s",
-			             err && err->message ? err->message : "(unknown)");
-		}
-		g_error_free (err);
+	GError *error = NULL;
+
+	priv->proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
+
+	if (!priv->proxy) {
+		nm_log_warn (LOGD_BT, "Couldn't acquire object manager proxy: %s",
+		             error && error->message ? error->message : "(unknown)");
+		g_clear_error (&error);
 		return;
 	}
 
-	default_adapter_changed (priv->proxy, default_adapter, self);
-}
-
-static void
-query_default_adapter (NMBluezManager *self)
-{
-	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
-	DBusGProxyCall *call;
-
-	call = dbus_g_proxy_begin_call (priv->proxy, "DefaultAdapter",
-	                                default_adapter_cb,
-	                                self,
-	                                NULL, G_TYPE_INVALID);
-	if (!call)
-		nm_log_warn (LOGD_BT, "failed to request default Bluetooth adapter.");
+	/* Get already managed devices. */
+	g_dbus_proxy_call (priv->proxy, "GetManagedObjects",
+	                   NULL,
+	                   G_DBUS_CALL_FLAGS_NONE,
+	                   -1,
+	                   NULL,
+	                   (GAsyncReadyCallback) get_managed_objects_cb,
+	                   self);
+
+	g_signal_connect (priv->proxy, "g-signal",
+	                  G_CALLBACK (object_manager_g_signal), self);
 }
 
 static void
 bluez_connect (NMBluezManager *self)
 {
 	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
-	DBusGConnection *connection;
 
 	g_return_if_fail (priv->proxy == NULL);
 
-	connection = nm_dbus_manager_get_connection (priv->dbus_mgr);
-	if (!connection)
-		return;
-
-	priv->proxy = dbus_g_proxy_new_for_name (connection,
-	                                         BLUEZ_SERVICE,
-	                                         BLUEZ_MANAGER_PATH,
-	                                         BLUEZ_MANAGER_INTERFACE);
-
-	dbus_g_proxy_add_signal (priv->proxy, "AdapterRemoved",
-	                         DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
-	dbus_g_proxy_connect_signal (priv->proxy, "AdapterRemoved",
-	                             G_CALLBACK (adapter_removed), self, NULL);
-
-	dbus_g_proxy_add_signal (priv->proxy, "DefaultAdapterChanged",
-	                         DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
-	dbus_g_proxy_connect_signal (priv->proxy, "DefaultAdapterChanged",
-	                             G_CALLBACK (default_adapter_changed), self, NULL);
-
-	query_default_adapter (self);
+	g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+	                          G_DBUS_PROXY_FLAGS_NONE,
+	                          NULL,
+	                          BLUEZ_SERVICE,
+	                          BLUEZ_MANAGER_PATH,
+	                          OBJECT_MANAGER_INTERFACE,
+	                          NULL,
+	                          (GAsyncReadyCallback) on_proxy_acquired,
+	                          self);
 }
 
 static void
@@ -257,14 +302,9 @@ name_owner_changed_cb (NMDBusManager *dbus_mgr,
 	if (strcmp (BLUEZ_SERVICE, name))
 		return;
 
-	if (!old_owner_good && new_owner_good)
-		query_default_adapter (self);
-	else if (old_owner_good && !new_owner_good) {
-		/* Throwing away the adapter removes all devices too */
-		if (priv->adapter) {
-			g_object_unref (priv->adapter);
-			priv->adapter = NULL;
-		}
+	if (old_owner_good && !new_owner_good) {
+		if (priv->devices)
+			remove_all_devices (self);
 	}
 }
 
@@ -278,10 +318,10 @@ bluez_cleanup (NMBluezManager *self, gboolean do_signal)
 		priv->proxy = NULL;
 	}
 
-	if (priv->adapter) {
-		g_object_unref (priv->adapter);
-		priv->adapter = NULL;
-	}
+	if (do_signal)
+		remove_all_devices (self);
+	else
+		g_hash_table_remove_all (priv->devices);
 }
 
 static void
@@ -335,6 +375,9 @@ nm_bluez_manager_init (NMBluezManager *self)
 	                  self);
 
 	bluez_connect (self);
+
+	priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal,
+	                                       NULL, g_object_unref);
 }
 
 static void
@@ -348,13 +391,23 @@ dispose (GObject *object)
 	if (priv->dbus_mgr) {
 		g_signal_handlers_disconnect_by_func (priv->dbus_mgr, name_owner_changed_cb, self);
 		g_signal_handlers_disconnect_by_func (priv->dbus_mgr, dbus_connection_changed_cb, self);
-		g_object_unref (priv->dbus_mgr);
+		priv->dbus_mgr = NULL;
 	}
 
 	G_OBJECT_CLASS (nm_bluez_manager_parent_class)->dispose (object);
 }
 
 static void
+finalize (GObject *object)
+{
+	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (object);
+
+	g_hash_table_destroy (priv->devices);
+
+	G_OBJECT_CLASS (nm_bluez_manager_parent_class)->finalize (object);
+}
+
+static void
 nm_bluez_manager_class_init (NMBluezManagerClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
@@ -363,6 +416,7 @@ nm_bluez_manager_class_init (NMBluezManagerClass *klass)
 
 	/* virtual methods */
 	object_class->dispose = dispose;
+	object_class->finalize = finalize;
 
 	/* Signals */
 	signals[BDADDR_ADDED] =
@@ -370,17 +424,15 @@ nm_bluez_manager_class_init (NMBluezManagerClass *klass)
 		              G_OBJECT_CLASS_TYPE (object_class),
 		              G_SIGNAL_RUN_FIRST,
 		              G_STRUCT_OFFSET (NMBluezManagerClass, bdaddr_added),
-		              NULL, NULL,
-		              _nm_marshal_VOID__STRING_STRING_STRING_UINT,
-		              G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT);
+		              NULL, NULL, NULL,
+		              G_TYPE_NONE, 5, G_TYPE_OBJECT, G_TYPE_STRING,
+		              G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT);
 
 	signals[BDADDR_REMOVED] =
 		g_signal_new (NM_BLUEZ_MANAGER_BDADDR_REMOVED,
 		              G_OBJECT_CLASS_TYPE (object_class),
 		              G_SIGNAL_RUN_FIRST,
 		              G_STRUCT_OFFSET (NMBluezManagerClass, bdaddr_removed),
-		              NULL, NULL,
-		              _nm_marshal_VOID__STRING_STRING,
+		              NULL, NULL, NULL,
 		              G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
 }
-