bluetooth: handle absence of bluez D-Bus service properly
authorLennart Poettering <lennart@poettering.net>
Wed, 1 Jul 2009 00:20:23 +0000 (02:20 +0200)
committerLennart Poettering <lennart@poettering.net>
Wed, 1 Jul 2009 00:20:23 +0000 (02:20 +0200)
src/modules/bluetooth/bluetooth-util.c

index 5c7681d..66e1c31 100644 (file)
@@ -309,6 +309,17 @@ static void run_callback(pa_bluetooth_discovery *y, pa_bluetooth_device *d, pa_b
     pa_hook_fire(&y->hook, d);
 }
 
+static void remove_all_devices(pa_bluetooth_discovery *y) {
+    pa_bluetooth_device *d;
+
+    pa_assert(y);
+
+    while ((d = pa_hashmap_steal_first(y->devices))) {
+        run_callback(y, d, TRUE);
+        device_free(d);
+    }
+}
+
 static void get_properties_reply(DBusPendingCall *pending, void *userdata) {
     DBusMessage *r;
     DBusMessageIter arg_i, element_i;
@@ -332,6 +343,12 @@ static void get_properties_reply(DBusPendingCall *pending, void *userdata) {
     if (dbus_message_is_method_call(p->message, "org.bluez.Device", "GetProperties"))
         d->device_info_valid = valid;
 
+    if (dbus_message_is_error(r, DBUS_ERROR_SERVICE_UNKNOWN)) {
+        pa_log_debug("Bluetooth daemon is apparently not available.");
+        remove_all_devices(y);
+        goto finish2;
+    }
+
     if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
 
         if (!dbus_message_is_error(r, DBUS_ERROR_UNKNOWN_METHOD))
@@ -383,6 +400,7 @@ static void get_properties_reply(DBusPendingCall *pending, void *userdata) {
 finish:
     run_callback(y, d, FALSE);
 
+finish2:
     dbus_message_unref(r);
 
     PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
@@ -412,6 +430,9 @@ static void found_device(pa_bluetooth_discovery *y, const char* path) {
     pa_assert(y);
     pa_assert(path);
 
+    if (pa_hashmap_get(y->devices, path))
+        return;
+
     d = device_new(path);
 
     pa_hashmap_put(y->devices, d->path, d);
@@ -439,9 +460,15 @@ static void list_devices_reply(DBusPendingCall *pending, void *userdata) {
     pa_assert_se(y = p->context_data);
     pa_assert_se(r = dbus_pending_call_steal_reply(pending));
 
+    if (dbus_message_is_error(r, DBUS_ERROR_SERVICE_UNKNOWN)) {
+        pa_log_debug("Bluetooth daemon is apparently not available.");
+        remove_all_devices(y);
+        goto finish;
+    }
+
     if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
         pa_log("Error from ListDevices reply: %s", dbus_message_get_error_name(r));
-        goto end;
+        goto finish;
     }
 
     if (!dbus_message_get_args(r, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &paths, &num, DBUS_TYPE_INVALID)) {
@@ -454,7 +481,7 @@ static void list_devices_reply(DBusPendingCall *pending, void *userdata) {
             found_device(y, paths[i]);
     }
 
-end:
+finish:
     if (paths)
         dbus_free_string_array (paths);
 
@@ -487,9 +514,15 @@ static void list_adapters_reply(DBusPendingCall *pending, void *userdata) {
     pa_assert_se(y = p->context_data);
     pa_assert_se(r = dbus_pending_call_steal_reply(pending));
 
+    if (dbus_message_is_error(r, DBUS_ERROR_SERVICE_UNKNOWN)) {
+        pa_log_debug("Bluetooth daemon is apparently not available.");
+        remove_all_devices(y);
+        goto finish;
+    }
+
     if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
         pa_log("Error from ListAdapters reply: %s", dbus_message_get_error_name(r));
-        goto end;
+        goto finish;
     }
 
     if (!dbus_message_get_args(r, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &paths, &num, DBUS_TYPE_INVALID)) {
@@ -502,7 +535,7 @@ static void list_adapters_reply(DBusPendingCall *pending, void *userdata) {
             found_adapter(y, paths[i]);
     }
 
-end:
+finish:
     if (paths)
         dbus_free_string_array (paths);
 
@@ -616,6 +649,32 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
         }
 
         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    } else if (dbus_message_is_signal(m, "org.freedesktop.DBus", "NameOwnerChanged")) {
+        const char *name, *old_owner, *new_owner;
+
+        if (!dbus_message_get_args(m, &err,
+                                   DBUS_TYPE_STRING, &name,
+                                   DBUS_TYPE_STRING, &old_owner,
+                                   DBUS_TYPE_STRING, &new_owner,
+                                   DBUS_TYPE_INVALID)) {
+            pa_log("Failed to parse org.freedesktop.DBus.NameOwnerChanged: %s", err.message);
+            goto fail;
+        }
+
+        if (pa_streq(name, "org.bluez")) {
+            if (old_owner && *old_owner) {
+                pa_log_debug("Bluetooth daemon disappeared.");
+                remove_all_devices(y);
+            }
+
+            if (new_owner && *new_owner) {
+                pa_log_debug("Bluetooth daemon appeared.");
+                list_adapters(y);
+            }
+        }
+
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
     }
 
 fail:
@@ -699,6 +758,7 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c) {
 
     if (pa_dbus_add_matches(
                 pa_dbus_connection_get(y->connection), &err,
+                "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'",
                 "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'",
                 "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'",
                 "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceCreated'",
@@ -734,8 +794,6 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_ref(pa_bluetooth_discovery *y) {
 }
 
 void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) {
-    pa_bluetooth_device *d;
-
     pa_assert(y);
     pa_assert(PA_REFCNT_VALUE(y) > 0);
 
@@ -745,16 +803,13 @@ void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) {
     pa_dbus_free_pending_list(&y->pending);
 
     if (y->devices) {
-        while ((d = pa_hashmap_steal_first(y->devices))) {
-            run_callback(y, d, TRUE);
-            device_free(d);
-        }
-
+        remove_all_devices(y);
         pa_hashmap_free(y->devices, NULL, NULL);
     }
 
     if (y->connection) {
         pa_dbus_remove_matches(pa_dbus_connection_get(y->connection),
+                               "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'",
                                "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'",
                                "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterRemoved'",
                                "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'",