2 This file is part of PulseAudio.
4 Copyright 2008-2009 Joao Paulo Rechi Vita
6 PulseAudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as
8 published by the Free Software Foundation; either version 2.1 of the
9 License, or (at your option) any later version.
11 PulseAudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with PulseAudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
26 #include <pulsecore/core-util.h>
27 #include <pulsecore/shared.h>
28 #include <pulsecore/dbus-shared.h>
30 #include "bluetooth-util.h"
32 struct pa_bluetooth_discovery
{
36 pa_dbus_connection
*connection
;
37 PA_LLIST_HEAD(pa_dbus_pending
, pending
);
42 static void get_properties_reply(DBusPendingCall
*pending
, void *userdata
);
43 static pa_dbus_pending
* send_and_add_to_pending(pa_bluetooth_discovery
*y
, pa_bluetooth_device
*d
, DBusMessage
*m
, DBusPendingCallNotifyFunction func
);
45 static pa_bt_audio_state_t
pa_bt_audio_state_from_string(const char* value
) {
48 if (pa_streq(value
, "disconnected"))
49 return PA_BT_AUDIO_STATE_DISCONNECTED
;
50 else if (pa_streq(value
, "connecting"))
51 return PA_BT_AUDIO_STATE_CONNECTING
;
52 else if (pa_streq(value
, "connected"))
53 return PA_BT_AUDIO_STATE_CONNECTED
;
54 else if (pa_streq(value
, "playing"))
55 return PA_BT_AUDIO_STATE_PLAYING
;
57 return PA_BT_AUDIO_STATE_INVALID
;
60 static pa_bluetooth_uuid
*uuid_new(const char *uuid
) {
63 u
= pa_xnew(pa_bluetooth_uuid
, 1);
64 u
->uuid
= pa_xstrdup(uuid
);
65 PA_LLIST_INIT(pa_bluetooth_uuid
, u
);
70 static void uuid_free(pa_bluetooth_uuid
*u
) {
77 static pa_bluetooth_device
* device_new(const char *path
) {
78 pa_bluetooth_device
*d
;
80 d
= pa_xnew(pa_bluetooth_device
, 1);
84 d
->device_info_valid
= 0;
87 d
->path
= pa_xstrdup(path
);
90 d
->device_connected
= -1;
91 PA_LLIST_HEAD_INIT(pa_bluetooth_uuid
, d
->uuids
);
96 d
->audio_state
= PA_BT_AUDIO_STATE_INVALID
;
97 d
->audio_sink_state
= PA_BT_AUDIO_STATE_INVALID
;
98 d
->audio_source_state
= PA_BT_AUDIO_STATE_INVALID
;
99 d
->headset_state
= PA_BT_AUDIO_STATE_INVALID
;
104 static void device_free(pa_bluetooth_device
*d
) {
105 pa_bluetooth_uuid
*u
;
109 while ((u
= d
->uuids
)) {
110 PA_LLIST_REMOVE(pa_bluetooth_uuid
, d
->uuids
, u
);
117 pa_xfree(d
->address
);
121 static pa_bool_t
device_is_audio(pa_bluetooth_device
*d
) {
125 d
->device_info_valid
&&
126 (d
->audio_state
!= PA_BT_AUDIO_STATE_INVALID
&&
127 (d
->audio_sink_state
!= PA_BT_AUDIO_STATE_INVALID
||
128 d
->audio_source_state
!= PA_BT_AUDIO_STATE_INVALID
||
129 d
->headset_state
!= PA_BT_AUDIO_STATE_INVALID
));
132 static int parse_device_property(pa_bluetooth_discovery
*y
, pa_bluetooth_device
*d
, DBusMessageIter
*i
) {
134 DBusMessageIter variant_i
;
140 if (dbus_message_iter_get_arg_type(i
) != DBUS_TYPE_STRING
) {
141 pa_log("Property name not a string.");
145 dbus_message_iter_get_basic(i
, &key
);
147 if (!dbus_message_iter_next(i
)) {
148 pa_log("Property value missing");
152 if (dbus_message_iter_get_arg_type(i
) != DBUS_TYPE_VARIANT
) {
153 pa_log("Property value not a variant.");
157 dbus_message_iter_recurse(i
, &variant_i
);
159 /* pa_log_debug("Parsing property org.bluez.Device.%s", key); */
161 switch (dbus_message_iter_get_arg_type(&variant_i
)) {
163 case DBUS_TYPE_STRING
: {
166 dbus_message_iter_get_basic(&variant_i
, &value
);
168 if (pa_streq(key
, "Name")) {
170 d
->name
= pa_xstrdup(value
);
171 } else if (pa_streq(key
, "Alias")) {
173 d
->alias
= pa_xstrdup(value
);
174 } else if (pa_streq(key
, "Address")) {
175 pa_xfree(d
->address
);
176 d
->address
= pa_xstrdup(value
);
179 /* pa_log_debug("Value %s", value); */
184 case DBUS_TYPE_BOOLEAN
: {
187 dbus_message_iter_get_basic(&variant_i
, &value
);
189 if (pa_streq(key
, "Paired"))
191 else if (pa_streq(key
, "Connected"))
192 d
->device_connected
= !!value
;
193 else if (pa_streq(key
, "Trusted"))
194 d
->trusted
= !!value
;
196 /* pa_log_debug("Value %s", pa_yes_no(value)); */
201 case DBUS_TYPE_UINT32
: {
204 dbus_message_iter_get_basic(&variant_i
, &value
);
206 if (pa_streq(key
, "Class"))
207 d
->class = (int) value
;
209 /* pa_log_debug("Value %u", (unsigned) value); */
214 case DBUS_TYPE_ARRAY
: {
217 dbus_message_iter_recurse(&variant_i
, &ai
);
219 if (dbus_message_iter_get_arg_type(&ai
) == DBUS_TYPE_STRING
&&
220 pa_streq(key
, "UUIDs")) {
222 while (dbus_message_iter_get_arg_type(&ai
) != DBUS_TYPE_INVALID
) {
223 pa_bluetooth_uuid
*node
;
227 dbus_message_iter_get_basic(&ai
, &value
);
228 node
= uuid_new(value
);
229 PA_LLIST_PREPEND(pa_bluetooth_uuid
, d
->uuids
, node
);
231 /* Vudentz said the interfaces are here when the UUIDs are announced */
232 if (strcasecmp(HSP_HS_UUID
, value
) == 0 || strcasecmp(HFP_HS_UUID
, value
) == 0) {
233 pa_assert_se(m
= dbus_message_new_method_call("org.bluez", d
->path
, "org.bluez.Headset", "GetProperties"));
234 send_and_add_to_pending(y
, d
, m
, get_properties_reply
);
235 } else if (strcasecmp(A2DP_SINK_UUID
, value
) == 0) {
236 pa_assert_se(m
= dbus_message_new_method_call("org.bluez", d
->path
, "org.bluez.AudioSink", "GetProperties"));
237 send_and_add_to_pending(y
, d
, m
, get_properties_reply
);
238 } else if (strcasecmp(A2DP_SOURCE_UUID
, value
) == 0) {
239 pa_assert_se(m
= dbus_message_new_method_call("org.bluez", d
->path
, "org.bluez.AudioSource", "GetProperties"));
240 send_and_add_to_pending(y
, d
, m
, get_properties_reply
);
243 /* this might eventually be racy if .Audio is not there yet, but the State change will come anyway later, so this call is for cold-detection mostly */
244 pa_assert_se(m
= dbus_message_new_method_call("org.bluez", d
->path
, "org.bluez.Audio", "GetProperties"));
245 send_and_add_to_pending(y
, d
, m
, get_properties_reply
);
247 if (!dbus_message_iter_next(&ai
))
259 static int parse_audio_property(pa_bluetooth_discovery
*u
, int *state
, DBusMessageIter
*i
) {
261 DBusMessageIter variant_i
;
267 if (dbus_message_iter_get_arg_type(i
) != DBUS_TYPE_STRING
) {
268 pa_log("Property name not a string.");
272 dbus_message_iter_get_basic(i
, &key
);
274 if (!dbus_message_iter_next(i
)) {
275 pa_log("Property value missing");
279 if (dbus_message_iter_get_arg_type(i
) != DBUS_TYPE_VARIANT
) {
280 pa_log("Property value not a variant.");
284 dbus_message_iter_recurse(i
, &variant_i
);
286 /* pa_log_debug("Parsing property org.bluez.{Audio|AudioSink|AudioSource|Headset}.%s", key); */
288 switch (dbus_message_iter_get_arg_type(&variant_i
)) {
290 case DBUS_TYPE_STRING
: {
293 dbus_message_iter_get_basic(&variant_i
, &value
);
295 if (pa_streq(key
, "State"))
296 *state
= pa_bt_audio_state_from_string(value
);
297 /* pa_log_debug("Value %s", value); */
306 static void run_callback(pa_bluetooth_discovery
*y
, pa_bluetooth_device
*d
, pa_bool_t dead
) {
310 if (!device_is_audio(d
))
314 pa_hook_fire(&y
->hook
, d
);
317 static void remove_all_devices(pa_bluetooth_discovery
*y
) {
318 pa_bluetooth_device
*d
;
322 while ((d
= pa_hashmap_steal_first(y
->devices
))) {
323 run_callback(y
, d
, TRUE
);
328 static void get_properties_reply(DBusPendingCall
*pending
, void *userdata
) {
330 DBusMessageIter arg_i
, element_i
;
332 pa_bluetooth_device
*d
;
333 pa_bluetooth_discovery
*y
;
336 pa_assert_se(p
= userdata
);
337 pa_assert_se(y
= p
->context_data
);
338 pa_assert_se(r
= dbus_pending_call_steal_reply(pending
));
340 /* pa_log_debug("Got %s.GetProperties response for %s", */
341 /* dbus_message_get_interface(p->message), */
342 /* dbus_message_get_path(p->message)); */
346 valid
= dbus_message_get_type(r
) == DBUS_MESSAGE_TYPE_ERROR
? -1 : 1;
348 if (dbus_message_is_method_call(p
->message
, "org.bluez.Device", "GetProperties"))
349 d
->device_info_valid
= valid
;
351 if (dbus_message_is_error(r
, DBUS_ERROR_SERVICE_UNKNOWN
)) {
352 pa_log_debug("Bluetooth daemon is apparently not available.");
353 remove_all_devices(y
);
357 if (dbus_message_get_type(r
) == DBUS_MESSAGE_TYPE_ERROR
) {
359 if (!dbus_message_is_error(r
, DBUS_ERROR_UNKNOWN_METHOD
))
360 pa_log("Error from GetProperties reply: %s", dbus_message_get_error_name(r
));
365 if (!dbus_message_iter_init(r
, &arg_i
)) {
366 pa_log("GetProperties reply has no arguments.");
370 if (dbus_message_iter_get_arg_type(&arg_i
) != DBUS_TYPE_ARRAY
) {
371 pa_log("GetProperties argument is not an array.");
375 dbus_message_iter_recurse(&arg_i
, &element_i
);
376 while (dbus_message_iter_get_arg_type(&element_i
) != DBUS_TYPE_INVALID
) {
378 if (dbus_message_iter_get_arg_type(&element_i
) == DBUS_TYPE_DICT_ENTRY
) {
379 DBusMessageIter dict_i
;
381 dbus_message_iter_recurse(&element_i
, &dict_i
);
383 if (dbus_message_has_interface(p
->message
, "org.bluez.Device")) {
384 if (parse_device_property(y
, d
, &dict_i
) < 0)
387 } else if (dbus_message_has_interface(p
->message
, "org.bluez.Audio")) {
388 if (parse_audio_property(y
, &d
->audio_state
, &dict_i
) < 0)
391 } else if (dbus_message_has_interface(p
->message
, "org.bluez.Headset")) {
392 if (parse_audio_property(y
, &d
->headset_state
, &dict_i
) < 0)
395 } else if (dbus_message_has_interface(p
->message
, "org.bluez.AudioSink")) {
396 if (parse_audio_property(y
, &d
->audio_sink_state
, &dict_i
) < 0)
398 } else if (dbus_message_has_interface(p
->message
, "org.bluez.AudioSource")) {
399 if (parse_audio_property(y
, &d
->audio_source_state
, &dict_i
) < 0)
404 if (!dbus_message_iter_next(&element_i
))
409 run_callback(y
, d
, FALSE
);
412 dbus_message_unref(r
);
414 PA_LLIST_REMOVE(pa_dbus_pending
, y
->pending
, p
);
415 pa_dbus_pending_free(p
);
418 static pa_dbus_pending
* send_and_add_to_pending(pa_bluetooth_discovery
*y
, pa_bluetooth_device
*d
, DBusMessage
*m
, DBusPendingCallNotifyFunction func
) {
420 DBusPendingCall
*call
;
425 pa_assert_se(dbus_connection_send_with_reply(pa_dbus_connection_get(y
->connection
), m
, &call
, -1));
427 p
= pa_dbus_pending_new(pa_dbus_connection_get(y
->connection
), m
, call
, y
, d
);
428 PA_LLIST_PREPEND(pa_dbus_pending
, y
->pending
, p
);
429 dbus_pending_call_set_notify(call
, func
, p
, NULL
);
434 static void found_device(pa_bluetooth_discovery
*y
, const char* path
) {
436 pa_bluetooth_device
*d
;
441 if (pa_hashmap_get(y
->devices
, path
))
444 d
= device_new(path
);
446 pa_hashmap_put(y
->devices
, d
->path
, d
);
448 pa_assert_se(m
= dbus_message_new_method_call("org.bluez", path
, "org.bluez.Device", "GetProperties"));
449 send_and_add_to_pending(y
, d
, m
, get_properties_reply
);
451 /* Before we read the other properties (Audio, AudioSink, AudioSource,
452 * Headset) we wait that the UUID is read */
455 static void list_devices_reply(DBusPendingCall
*pending
, void *userdata
) {
461 pa_bluetooth_discovery
*y
;
467 pa_assert_se(p
= userdata
);
468 pa_assert_se(y
= p
->context_data
);
469 pa_assert_se(r
= dbus_pending_call_steal_reply(pending
));
471 if (dbus_message_is_error(r
, DBUS_ERROR_SERVICE_UNKNOWN
)) {
472 pa_log_debug("Bluetooth daemon is apparently not available.");
473 remove_all_devices(y
);
477 if (dbus_message_get_type(r
) == DBUS_MESSAGE_TYPE_ERROR
) {
478 pa_log("Error from ListDevices reply: %s", dbus_message_get_error_name(r
));
482 if (!dbus_message_get_args(r
, &e
, DBUS_TYPE_ARRAY
, DBUS_TYPE_OBJECT_PATH
, &paths
, &num
, DBUS_TYPE_INVALID
)) {
483 pa_log("org.bluez.Adapter.ListDevices returned an error: '%s'\n", e
.message
);
488 for (i
= 0; i
< num
; ++i
)
489 found_device(y
, paths
[i
]);
494 dbus_free_string_array (paths
);
496 dbus_message_unref(r
);
498 PA_LLIST_REMOVE(pa_dbus_pending
, y
->pending
, p
);
499 pa_dbus_pending_free(p
);
502 static void found_adapter(pa_bluetooth_discovery
*y
, const char *path
) {
505 pa_assert_se(m
= dbus_message_new_method_call("org.bluez", path
, "org.bluez.Adapter", "ListDevices"));
506 send_and_add_to_pending(y
, NULL
, m
, list_devices_reply
);
509 static void list_adapters_reply(DBusPendingCall
*pending
, void *userdata
) {
515 pa_bluetooth_discovery
*y
;
521 pa_assert_se(p
= userdata
);
522 pa_assert_se(y
= p
->context_data
);
523 pa_assert_se(r
= dbus_pending_call_steal_reply(pending
));
525 if (dbus_message_is_error(r
, DBUS_ERROR_SERVICE_UNKNOWN
)) {
526 pa_log_debug("Bluetooth daemon is apparently not available.");
527 remove_all_devices(y
);
531 if (dbus_message_get_type(r
) == DBUS_MESSAGE_TYPE_ERROR
) {
532 pa_log("Error from ListAdapters reply: %s", dbus_message_get_error_name(r
));
536 if (!dbus_message_get_args(r
, &e
, DBUS_TYPE_ARRAY
, DBUS_TYPE_OBJECT_PATH
, &paths
, &num
, DBUS_TYPE_INVALID
)) {
537 pa_log("org.bluez.Manager.ListAdapters returned an error: %s", e
.message
);
542 for (i
= 0; i
< num
; ++i
)
543 found_adapter(y
, paths
[i
]);
548 dbus_free_string_array (paths
);
550 dbus_message_unref(r
);
552 PA_LLIST_REMOVE(pa_dbus_pending
, y
->pending
, p
);
553 pa_dbus_pending_free(p
);
556 static void list_adapters(pa_bluetooth_discovery
*y
) {
560 pa_assert_se(m
= dbus_message_new_method_call("org.bluez", "/", "org.bluez.Manager", "ListAdapters"));
561 send_and_add_to_pending(y
, NULL
, m
, list_adapters_reply
);
564 static DBusHandlerResult
filter_cb(DBusConnection
*bus
, DBusMessage
*m
, void *userdata
) {
566 pa_bluetooth_discovery
*y
;
571 pa_assert_se(y
= userdata
);
573 dbus_error_init(&err
);
575 pa_log_debug("dbus: interface=%s, path=%s, member=%s\n",
576 dbus_message_get_interface(m
),
577 dbus_message_get_path(m
),
578 dbus_message_get_member(m
));
580 if (dbus_message_is_signal(m
, "org.bluez.Adapter", "DeviceRemoved")) {
582 pa_bluetooth_device
*d
;
584 if (!dbus_message_get_args(m
, &err
, DBUS_TYPE_OBJECT_PATH
, &path
, DBUS_TYPE_INVALID
)) {
585 pa_log("Failed to parse org.bluez.Adapter.DeviceRemoved: %s", err
.message
);
589 pa_log_debug("Device %s removed", path
);
591 if ((d
= pa_hashmap_remove(y
->devices
, path
))) {
592 run_callback(y
, d
, TRUE
);
596 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
598 } else if (dbus_message_is_signal(m
, "org.bluez.Adapter", "DeviceCreated")) {
601 if (!dbus_message_get_args(m
, &err
, DBUS_TYPE_OBJECT_PATH
, &path
, DBUS_TYPE_INVALID
)) {
602 pa_log("Failed to parse org.bluez.Adapter.DeviceCreated: %s", err
.message
);
606 pa_log_debug("Device %s created", path
);
608 found_device(y
, path
);
609 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
611 } else if (dbus_message_is_signal(m
, "org.bluez.Manager", "AdapterAdded")) {
614 if (!dbus_message_get_args(m
, &err
, DBUS_TYPE_OBJECT_PATH
, &path
, DBUS_TYPE_INVALID
)) {
615 pa_log("Failed to parse org.bluez.Manager.AdapterAdded: %s", err
.message
);
619 pa_log_debug("Adapter %s created", path
);
621 found_adapter(y
, path
);
622 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
624 } else if (dbus_message_is_signal(m
, "org.bluez.Audio", "PropertyChanged") ||
625 dbus_message_is_signal(m
, "org.bluez.Headset", "PropertyChanged") ||
626 dbus_message_is_signal(m
, "org.bluez.AudioSink", "PropertyChanged") ||
627 dbus_message_is_signal(m
, "org.bluez.AudioSource", "PropertyChanged") ||
628 dbus_message_is_signal(m
, "org.bluez.Device", "PropertyChanged")) {
630 pa_bluetooth_device
*d
;
632 if ((d
= pa_hashmap_get(y
->devices
, dbus_message_get_path(m
)))) {
633 DBusMessageIter arg_i
;
635 if (!dbus_message_iter_init(m
, &arg_i
)) {
636 pa_log("Failed to parse PropertyChanged: %s", err
.message
);
640 if (dbus_message_has_interface(m
, "org.bluez.Device")) {
641 if (parse_device_property(y
, d
, &arg_i
) < 0)
644 } else if (dbus_message_has_interface(m
, "org.bluez.Audio")) {
645 if (parse_audio_property(y
, &d
->audio_state
, &arg_i
) < 0)
648 } else if (dbus_message_has_interface(m
, "org.bluez.Headset")) {
649 if (parse_audio_property(y
, &d
->headset_state
, &arg_i
) < 0)
652 } else if (dbus_message_has_interface(m
, "org.bluez.AudioSink")) {
653 if (parse_audio_property(y
, &d
->audio_sink_state
, &arg_i
) < 0)
655 } else if (dbus_message_has_interface(m
, "org.bluez.AudioSource")) {
656 if (parse_audio_property(y
, &d
->audio_source_state
, &arg_i
) < 0)
660 run_callback(y
, d
, FALSE
);
663 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
665 } else if (dbus_message_is_signal(m
, "org.freedesktop.DBus", "NameOwnerChanged")) {
666 const char *name
, *old_owner
, *new_owner
;
668 if (!dbus_message_get_args(m
, &err
,
669 DBUS_TYPE_STRING
, &name
,
670 DBUS_TYPE_STRING
, &old_owner
,
671 DBUS_TYPE_STRING
, &new_owner
,
672 DBUS_TYPE_INVALID
)) {
673 pa_log("Failed to parse org.freedesktop.DBus.NameOwnerChanged: %s", err
.message
);
677 if (pa_streq(name
, "org.bluez")) {
678 if (old_owner
&& *old_owner
) {
679 pa_log_debug("Bluetooth daemon disappeared.");
680 remove_all_devices(y
);
683 if (new_owner
&& *new_owner
) {
684 pa_log_debug("Bluetooth daemon appeared.");
689 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
693 dbus_error_free(&err
);
695 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
698 const pa_bluetooth_device
* pa_bluetooth_discovery_get_by_address(pa_bluetooth_discovery
*y
, const char* address
) {
699 pa_bluetooth_device
*d
;
703 pa_assert(PA_REFCNT_VALUE(y
) > 0);
706 if (!pa_hook_is_firing(&y
->hook
))
707 pa_bluetooth_discovery_sync(y
);
709 while ((d
= pa_hashmap_iterate(y
->devices
, &state
, NULL
)))
710 if (pa_streq(d
->address
, address
))
716 const pa_bluetooth_device
* pa_bluetooth_discovery_get_by_path(pa_bluetooth_discovery
*y
, const char* path
) {
718 pa_assert(PA_REFCNT_VALUE(y
) > 0);
721 if (!pa_hook_is_firing(&y
->hook
))
722 pa_bluetooth_discovery_sync(y
);
724 return pa_hashmap_get(y
->devices
, path
);
727 static int setup_dbus(pa_bluetooth_discovery
*y
) {
730 dbus_error_init(&err
);
732 y
->connection
= pa_dbus_bus_get(y
->core
, DBUS_BUS_SYSTEM
, &err
);
734 if (dbus_error_is_set(&err
) || !y
->connection
) {
735 pa_log("Failed to get D-Bus connection: %s", err
.message
);
736 dbus_error_free(&err
);
743 pa_bluetooth_discovery
* pa_bluetooth_discovery_get(pa_core
*c
) {
745 pa_bluetooth_discovery
*y
;
749 dbus_error_init(&err
);
751 if ((y
= pa_shared_get(c
, "bluetooth-discovery")))
752 return pa_bluetooth_discovery_ref(y
);
754 y
= pa_xnew0(pa_bluetooth_discovery
, 1);
757 y
->devices
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
758 PA_LLIST_HEAD_INIT(pa_dbus_pending
, y
->pending
);
759 pa_hook_init(&y
->hook
, y
);
760 pa_shared_set(c
, "bluetooth-discovery", y
);
762 if (setup_dbus(y
) < 0)
765 /* dynamic detection of bluetooth audio devices */
766 if (!dbus_connection_add_filter(pa_dbus_connection_get(y
->connection
), filter_cb
, y
, NULL
)) {
767 pa_log_error("Failed to add filter function");
771 if (pa_dbus_add_matches(
772 pa_dbus_connection_get(y
->connection
), &err
,
773 "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='org.bluez'",
774 "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'",
775 "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'",
776 "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceCreated'",
777 "type='signal',sender='org.bluez',interface='org.bluez.Device',member='PropertyChanged'",
778 "type='signal',sender='org.bluez',interface='org.bluez.Audio',member='PropertyChanged'",
779 "type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'",
780 "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'",
781 "type='signal',sender='org.bluez',interface='org.bluez.AudioSource',member='PropertyChanged'", NULL
) < 0) {
782 pa_log("Failed to add D-Bus matches: %s", err
.message
);
793 pa_bluetooth_discovery_unref(y
);
795 dbus_error_free(&err
);
800 pa_bluetooth_discovery
* pa_bluetooth_discovery_ref(pa_bluetooth_discovery
*y
) {
802 pa_assert(PA_REFCNT_VALUE(y
) > 0);
809 void pa_bluetooth_discovery_unref(pa_bluetooth_discovery
*y
) {
811 pa_assert(PA_REFCNT_VALUE(y
) > 0);
813 if (PA_REFCNT_DEC(y
) > 0)
816 pa_dbus_free_pending_list(&y
->pending
);
819 remove_all_devices(y
);
820 pa_hashmap_free(y
->devices
, NULL
, NULL
);
824 pa_dbus_remove_matches(pa_dbus_connection_get(y
->connection
),
825 "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='org.bluez'",
826 "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'",
827 "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterRemoved'",
828 "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'",
829 "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceCreated'",
830 "type='signal',sender='org.bluez',interface='org.bluez.Device',member='PropertyChanged'",
831 "type='signal',sender='org.bluez',interface='org.bluez.Audio',member='PropertyChanged'",
832 "type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'",
833 "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'",
834 "type='signal',sender='org.bluez',interface='org.bluez.AudioSource',member='PropertyChanged'", NULL
);
836 dbus_connection_remove_filter(pa_dbus_connection_get(y
->connection
), filter_cb
, y
);
838 pa_dbus_connection_unref(y
->connection
);
841 pa_hook_done(&y
->hook
);
844 pa_shared_remove(y
->core
, "bluetooth-discovery");
849 void pa_bluetooth_discovery_sync(pa_bluetooth_discovery
*y
) {
851 pa_assert(PA_REFCNT_VALUE(y
) > 0);
853 pa_dbus_sync_pending_list(&y
->pending
);
856 pa_hook
* pa_bluetooth_discovery_hook(pa_bluetooth_discovery
*y
) {
858 pa_assert(PA_REFCNT_VALUE(y
) > 0);
863 const char*pa_bluetooth_get_form_factor(uint32_t class) {
867 static const char * const table
[] = {
878 if (((class >> 8) & 31) != 4)
881 if ((i
= (class >> 2) & 63) > PA_ELEMENTSOF(table
))
887 pa_log_debug("Unknown Bluetooth minor device class %u", i
);
892 char *pa_bluetooth_cleanup_name(const char *name
) {
894 pa_bool_t space
= FALSE
;
898 while ((*name
>= 1 && *name
<= 32) || *name
>= 127)
901 t
= pa_xstrdup(name
);
903 for (s
= d
= t
; *s
; s
++) {
905 if (*s
<= 32 || *s
>= 127 || *s
== '_') {
923 pa_bool_t
pa_bluetooth_uuid_has(pa_bluetooth_uuid
*uuids
, const char *uuid
) {
927 if (strcasecmp(uuids
->uuid
, uuid
) == 0)