2 This file is part of PulseAudio.
4 Copyright 2008-2013 João 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 <pulse/xmalloc.h>
28 #include <pulsecore/core.h>
29 #include <pulsecore/core-util.h>
30 #include <pulsecore/dbus-shared.h>
31 #include <pulsecore/log.h>
32 #include <pulsecore/macro.h>
33 #include <pulsecore/refcnt.h>
34 #include <pulsecore/shared.h>
36 #include "a2dp-codecs.h"
38 #include "bluez5-util.h"
40 #define BLUEZ_SERVICE "org.bluez"
41 #define BLUEZ_MEDIA_ENDPOINT_INTERFACE BLUEZ_SERVICE ".MediaEndpoint1"
42 #define BLUEZ_MEDIA_TRANSPORT_INTERFACE BLUEZ_SERVICE ".MediaTransport1"
44 #define A2DP_SOURCE_ENDPOINT "/MediaEndpoint/A2DPSource"
45 #define A2DP_SINK_ENDPOINT "/MediaEndpoint/A2DPSink"
47 #define ENDPOINT_INTROSPECT_XML \
48 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
50 " <interface name=\"" BLUEZ_MEDIA_ENDPOINT_INTERFACE "\">" \
51 " <method name=\"SetConfiguration\">" \
52 " <arg name=\"transport\" direction=\"in\" type=\"o\"/>" \
53 " <arg name=\"properties\" direction=\"in\" type=\"ay\"/>" \
55 " <method name=\"SelectConfiguration\">" \
56 " <arg name=\"capabilities\" direction=\"in\" type=\"ay\"/>" \
57 " <arg name=\"configuration\" direction=\"out\" type=\"ay\"/>" \
59 " <method name=\"ClearConfiguration\">" \
60 " <arg name=\"transport\" direction=\"in\" type=\"o\"/>" \
62 " <method name=\"Release\">" \
65 " <interface name=\"org.freedesktop.DBus.Introspectable\">" \
66 " <method name=\"Introspect\">" \
67 " <arg name=\"data\" type=\"s\" direction=\"out\"/>" \
72 struct pa_bluetooth_discovery
{
76 pa_dbus_connection
*connection
;
79 pa_hook hooks
[PA_BLUETOOTH_HOOK_MAX
];
82 pa_hashmap
*transports
;
85 pa_bluetooth_transport
*pa_bluetooth_transport_new(pa_bluetooth_device
*d
, const char *owner
, const char *path
,
86 pa_bluetooth_profile_t p
, const uint8_t *config
, size_t size
) {
87 pa_bluetooth_transport
*t
;
89 t
= pa_xnew0(pa_bluetooth_transport
, 1);
91 t
->owner
= pa_xstrdup(owner
);
92 t
->path
= pa_xstrdup(path
);
94 t
->config_size
= size
;
97 t
->config
= pa_xnew(uint8_t, size
);
98 memcpy(t
->config
, config
, size
);
101 pa_assert_se(pa_hashmap_put(d
->discovery
->transports
, t
->path
, t
) >= 0);
106 static const char *transport_state_to_string(pa_bluetooth_transport_state_t state
) {
108 case PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED
:
109 return "disconnected";
110 case PA_BLUETOOTH_TRANSPORT_STATE_IDLE
:
112 case PA_BLUETOOTH_TRANSPORT_STATE_PLAYING
:
119 static void transport_state_changed(pa_bluetooth_transport
*t
, pa_bluetooth_transport_state_t state
) {
120 bool old_any_connected
;
124 if (t
->state
== state
)
127 old_any_connected
= pa_bluetooth_device_any_transport_connected(t
->device
);
129 pa_log_debug("Transport %s state changed from %s to %s",
130 t
->path
, transport_state_to_string(t
->state
), transport_state_to_string(state
));
133 if (state
== PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED
)
134 t
->device
->transports
[t
->profile
] = NULL
;
136 pa_hook_fire(&t
->device
->discovery
->hooks
[PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED
], t
);
138 if (old_any_connected
!= pa_bluetooth_device_any_transport_connected(t
->device
))
139 pa_hook_fire(&t
->device
->discovery
->hooks
[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED
], t
->device
);
142 void pa_bluetooth_transport_put(pa_bluetooth_transport
*t
) {
143 transport_state_changed(t
, PA_BLUETOOTH_TRANSPORT_STATE_IDLE
);
146 void pa_bluetooth_transport_free(pa_bluetooth_transport
*t
) {
149 pa_hashmap_remove(t
->device
->discovery
->transports
, t
->path
);
156 static int bluez5_transport_acquire_cb(pa_bluetooth_transport
*t
, bool optional
, size_t *imtu
, size_t *omtu
) {
161 const char *method
= optional
? "TryAcquire" : "Acquire";
164 pa_assert(t
->device
);
165 pa_assert(t
->device
->discovery
);
167 pa_assert_se(m
= dbus_message_new_method_call(t
->owner
, t
->path
, BLUEZ_MEDIA_TRANSPORT_INTERFACE
, method
));
169 dbus_error_init(&err
);
171 r
= dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(t
->device
->discovery
->connection
), m
, -1, &err
);
173 if (optional
&& pa_streq(err
.name
, "org.bluez.Error.NotAvailable"))
174 pa_log_info("Failed optional acquire of unavailable transport %s", t
->path
);
176 pa_log_error("Transport %s() failed for transport %s (%s)", method
, t
->path
, err
.message
);
178 dbus_error_free(&err
);
182 if (!dbus_message_get_args(r
, &err
, DBUS_TYPE_UNIX_FD
, &ret
, DBUS_TYPE_UINT16
, &i
, DBUS_TYPE_UINT16
, &o
,
183 DBUS_TYPE_INVALID
)) {
184 pa_log_error("Failed to parse %s() reply: %s", method
, err
.message
);
185 dbus_error_free(&err
);
197 dbus_message_unref(r
);
201 static void bluez5_transport_release_cb(pa_bluetooth_transport
*t
) {
206 pa_assert(t
->device
);
207 pa_assert(t
->device
->discovery
);
209 dbus_error_init(&err
);
211 if (t
->state
<= PA_BLUETOOTH_TRANSPORT_STATE_IDLE
) {
212 pa_log_info("Transport %s auto-released by BlueZ or already released", t
->path
);
216 pa_assert_se(m
= dbus_message_new_method_call(t
->owner
, t
->path
, BLUEZ_MEDIA_TRANSPORT_INTERFACE
, "Release"));
217 dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(t
->device
->discovery
->connection
), m
, -1, &err
);
219 if (dbus_error_is_set(&err
)) {
220 pa_log_error("Failed to release transport %s: %s", t
->path
, err
.message
);
221 dbus_error_free(&err
);
223 pa_log_info("Transport %s released", t
->path
);
226 bool pa_bluetooth_device_any_transport_connected(const pa_bluetooth_device
*d
) {
231 if (d
->device_info_valid
!= 1)
234 for (i
= 0; i
< PA_BLUETOOTH_PROFILE_COUNT
; i
++)
235 if (d
->transports
[i
] && d
->transports
[i
]->state
!= PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED
)
241 static pa_bluetooth_device
* device_create(pa_bluetooth_discovery
*y
, const char *path
) {
242 pa_bluetooth_device
*d
;
247 d
= pa_xnew0(pa_bluetooth_device
, 1);
249 d
->path
= pa_xstrdup(path
);
251 pa_hashmap_put(y
->devices
, d
->path
, d
);
256 pa_bluetooth_device
* pa_bluetooth_discovery_get_device_by_path(pa_bluetooth_discovery
*y
, const char *path
) {
257 pa_bluetooth_device
*d
;
260 pa_assert(PA_REFCNT_VALUE(y
) > 0);
263 if ((d
= pa_hashmap_get(y
->devices
, path
)))
264 if (d
->device_info_valid
== 1)
270 pa_bluetooth_device
* pa_bluetooth_discovery_get_device_by_address(pa_bluetooth_discovery
*y
, const char *remote
, const char *local
) {
271 pa_bluetooth_device
*d
;
275 pa_assert(PA_REFCNT_VALUE(y
) > 0);
279 while ((d
= pa_hashmap_iterate(y
->devices
, &state
, NULL
)))
280 if (pa_streq(d
->address
, remote
) && pa_streq(d
->adapter
->address
, local
))
281 return d
->device_info_valid
== 1 ? d
: NULL
;
286 static void device_free(pa_bluetooth_device
*d
) {
291 for (i
= 0; i
< PA_BLUETOOTH_PROFILE_COUNT
; i
++) {
292 pa_bluetooth_transport
*t
;
294 if (!(t
= d
->transports
[i
]))
297 transport_state_changed(t
, PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED
);
298 pa_bluetooth_transport_free(t
);
305 pa_xfree(d
->address
);
309 static void device_remove(pa_bluetooth_discovery
*y
, const char *path
) {
310 pa_bluetooth_device
*d
;
312 if (!(d
= pa_hashmap_remove(y
->devices
, path
)))
313 pa_log_warn("Unknown device removed %s", path
);
315 pa_log_debug("Device %s removed", path
);
320 static void device_remove_all(pa_bluetooth_discovery
*y
) {
321 pa_bluetooth_device
*d
;
325 while ((d
= pa_hashmap_steal_first(y
->devices
))) {
326 d
->device_info_valid
= -1;
327 pa_hook_fire(&y
->hooks
[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED
], d
);
332 static pa_bluetooth_adapter
* adapter_create(pa_bluetooth_discovery
*y
, const char *path
) {
333 pa_bluetooth_adapter
*a
;
338 a
= pa_xnew0(pa_bluetooth_adapter
, 1);
340 a
->path
= pa_xstrdup(path
);
342 pa_hashmap_put(y
->adapters
, a
->path
, a
);
347 static void adapter_remove_all(pa_bluetooth_discovery
*y
) {
348 pa_bluetooth_adapter
*a
;
352 /* When this function is called all devices have already been freed */
354 while ((a
= pa_hashmap_steal_first(y
->adapters
))) {
356 pa_xfree(a
->address
);
361 pa_hook
* pa_bluetooth_discovery_hook(pa_bluetooth_discovery
*y
, pa_bluetooth_hook_t hook
) {
363 pa_assert(PA_REFCNT_VALUE(y
) > 0);
365 return &y
->hooks
[hook
];
368 static DBusHandlerResult
filter_cb(DBusConnection
*bus
, DBusMessage
*m
, void *userdata
) {
369 pa_bluetooth_discovery
*y
;
374 pa_assert_se(y
= userdata
);
376 dbus_error_init(&err
);
378 if (dbus_message_is_signal(m
, "org.freedesktop.DBus", "NameOwnerChanged")) {
379 const char *name
, *old_owner
, *new_owner
;
381 if (!dbus_message_get_args(m
, &err
,
382 DBUS_TYPE_STRING
, &name
,
383 DBUS_TYPE_STRING
, &old_owner
,
384 DBUS_TYPE_STRING
, &new_owner
,
385 DBUS_TYPE_INVALID
)) {
386 pa_log_error("Failed to parse org.freedesktop.DBus.NameOwnerChanged: %s", err
.message
);
390 if (pa_streq(name
, BLUEZ_SERVICE
)) {
391 if (old_owner
&& *old_owner
) {
392 pa_log_debug("Bluetooth daemon disappeared");
393 device_remove_all(y
);
394 adapter_remove_all(y
);
397 if (new_owner
&& *new_owner
) {
398 pa_log_debug("Bluetooth daemon appeared");
399 /* TODO: get managed objects */
403 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
407 dbus_error_free(&err
);
409 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
412 const char *pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile
) {
414 case PA_BLUETOOTH_PROFILE_A2DP_SINK
:
416 case PA_BLUETOOTH_PROFILE_A2DP_SOURCE
:
417 return "a2dp_source";
418 case PA_BLUETOOTH_PROFILE_OFF
:
425 static DBusMessage
*endpoint_set_configuration(DBusConnection
*conn
, DBusMessage
*m
, void *userdata
) {
426 pa_bluetooth_discovery
*y
= userdata
;
427 pa_bluetooth_device
*d
;
428 pa_bluetooth_transport
*t
;
429 const char *sender
, *path
, *endpoint_path
, *dev_path
= NULL
, *uuid
= NULL
;
430 const uint8_t *config
= NULL
;
432 pa_bluetooth_profile_t p
= PA_BLUETOOTH_PROFILE_OFF
;
433 DBusMessageIter args
, props
;
436 if (!dbus_message_iter_init(m
, &args
) || !pa_streq(dbus_message_get_signature(m
), "oa{sv}")) {
437 pa_log_error("Invalid signature for method SetConfiguration()");
441 dbus_message_iter_get_basic(&args
, &path
);
443 if (pa_hashmap_get(y
->transports
, path
)) {
444 pa_log_error("Endpoint SetConfiguration(): Transport %s is already configured.", path
);
448 pa_assert_se(dbus_message_iter_next(&args
));
450 dbus_message_iter_recurse(&args
, &props
);
451 if (dbus_message_iter_get_arg_type(&props
) != DBUS_TYPE_DICT_ENTRY
)
454 /* Read transport properties */
455 while (dbus_message_iter_get_arg_type(&props
) == DBUS_TYPE_DICT_ENTRY
) {
457 DBusMessageIter value
, entry
;
460 dbus_message_iter_recurse(&props
, &entry
);
461 dbus_message_iter_get_basic(&entry
, &key
);
463 dbus_message_iter_next(&entry
);
464 dbus_message_iter_recurse(&entry
, &value
);
466 var
= dbus_message_iter_get_arg_type(&value
);
468 if (pa_streq(key
, "UUID")) {
469 if (var
!= DBUS_TYPE_STRING
) {
470 pa_log_error("Property %s of wrong type %c", key
, (char)var
);
474 dbus_message_iter_get_basic(&value
, &uuid
);
476 endpoint_path
= dbus_message_get_path(m
);
477 if (pa_streq(endpoint_path
, A2DP_SOURCE_ENDPOINT
)) {
478 if (pa_streq(uuid
, PA_BLUETOOTH_UUID_A2DP_SOURCE
))
479 p
= PA_BLUETOOTH_PROFILE_A2DP_SINK
;
480 } else if (pa_streq(endpoint_path
, A2DP_SINK_ENDPOINT
)) {
481 if (pa_streq(uuid
, PA_BLUETOOTH_UUID_A2DP_SINK
))
482 p
= PA_BLUETOOTH_PROFILE_A2DP_SOURCE
;
485 if (p
== PA_BLUETOOTH_PROFILE_OFF
) {
486 pa_log_error("UUID %s of transport %s incompatible with endpoint %s", uuid
, path
, endpoint_path
);
489 } else if (pa_streq(key
, "Device")) {
490 if (var
!= DBUS_TYPE_OBJECT_PATH
) {
491 pa_log_error("Property %s of wrong type %c", key
, (char)var
);
495 dbus_message_iter_get_basic(&value
, &dev_path
);
496 } else if (pa_streq(key
, "Configuration")) {
497 DBusMessageIter array
;
500 if (var
!= DBUS_TYPE_ARRAY
) {
501 pa_log_error("Property %s of wrong type %c", key
, (char)var
);
505 dbus_message_iter_recurse(&value
, &array
);
506 var
= dbus_message_iter_get_arg_type(&array
);
507 if (var
!= DBUS_TYPE_BYTE
) {
508 pa_log_error("%s is an array of wrong type %c", key
, (char)var
);
512 dbus_message_iter_get_fixed_array(&array
, &config
, &size
);
513 if (size
!= sizeof(a2dp_sbc_t
)) {
514 pa_log_error("Configuration array of invalid size");
518 c
= (a2dp_sbc_t
*) config
;
520 if (c
->frequency
!= SBC_SAMPLING_FREQ_16000
&& c
->frequency
!= SBC_SAMPLING_FREQ_32000
&&
521 c
->frequency
!= SBC_SAMPLING_FREQ_44100
&& c
->frequency
!= SBC_SAMPLING_FREQ_48000
) {
522 pa_log_error("Invalid sampling frequency in configuration");
526 if (c
->channel_mode
!= SBC_CHANNEL_MODE_MONO
&& c
->channel_mode
!= SBC_CHANNEL_MODE_DUAL_CHANNEL
&&
527 c
->channel_mode
!= SBC_CHANNEL_MODE_STEREO
&& c
->channel_mode
!= SBC_CHANNEL_MODE_JOINT_STEREO
) {
528 pa_log_error("Invalid channel mode in configuration");
532 if (c
->allocation_method
!= SBC_ALLOCATION_SNR
&& c
->allocation_method
!= SBC_ALLOCATION_LOUDNESS
) {
533 pa_log_error("Invalid allocation method in configuration");
537 if (c
->subbands
!= SBC_SUBBANDS_4
&& c
->subbands
!= SBC_SUBBANDS_8
) {
538 pa_log_error("Invalid SBC subbands in configuration");
542 if (c
->block_length
!= SBC_BLOCK_LENGTH_4
&& c
->block_length
!= SBC_BLOCK_LENGTH_8
&&
543 c
->block_length
!= SBC_BLOCK_LENGTH_12
&& c
->block_length
!= SBC_BLOCK_LENGTH_16
) {
544 pa_log_error("Invalid block length in configuration");
549 dbus_message_iter_next(&props
);
552 if ((d
= pa_hashmap_get(y
->devices
, dev_path
))) {
553 if (d
->device_info_valid
== -1) {
554 pa_log_error("Information about device %s is invalid", dev_path
);
558 /* InterfacesAdded signal is probably on it's way, device_info_valid is kept as 0. */
559 pa_log_warn("SetConfiguration() received for unknown device %s", dev_path
);
560 d
= device_create(y
, dev_path
);
563 if (d
->transports
[p
] != NULL
) {
564 pa_log_error("Cannot configure transport %s because profile %s is already used", path
, pa_bluetooth_profile_to_string(p
));
568 sender
= dbus_message_get_sender(m
);
570 pa_assert_se(r
= dbus_message_new_method_return(m
));
571 pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y
->connection
), r
, NULL
));
572 dbus_message_unref(r
);
574 d
->transports
[p
] = t
= pa_bluetooth_transport_new(d
, sender
, path
, p
, config
, size
);
575 t
->acquire
= bluez5_transport_acquire_cb
;
576 t
->release
= bluez5_transport_release_cb
;
577 pa_bluetooth_transport_put(t
);
579 pa_log_debug("Transport %s available for profile %s", t
->path
, pa_bluetooth_profile_to_string(t
->profile
));
584 pa_log_error("Endpoint SetConfiguration(): invalid arguments");
587 pa_assert_se(r
= dbus_message_new_error(m
, "org.bluez.Error.InvalidArguments", "Unable to set configuration"));
591 static DBusMessage
*endpoint_select_configuration(DBusConnection
*conn
, DBusMessage
*m
, void *userdata
) {
594 pa_assert_se(r
= dbus_message_new_error(m
, BLUEZ_MEDIA_ENDPOINT_INTERFACE
".Error.NotImplemented",
595 "Method not implemented"));
600 static DBusMessage
*endpoint_clear_configuration(DBusConnection
*conn
, DBusMessage
*m
, void *userdata
) {
603 pa_assert_se(r
= dbus_message_new_error(m
, BLUEZ_MEDIA_ENDPOINT_INTERFACE
".Error.NotImplemented",
604 "Method not implemented"));
609 static DBusMessage
*endpoint_release(DBusConnection
*conn
, DBusMessage
*m
, void *userdata
) {
612 pa_assert_se(r
= dbus_message_new_error(m
, BLUEZ_MEDIA_ENDPOINT_INTERFACE
".Error.NotImplemented",
613 "Method not implemented"));
618 static DBusHandlerResult
endpoint_handler(DBusConnection
*c
, DBusMessage
*m
, void *userdata
) {
619 struct pa_bluetooth_discovery
*y
= userdata
;
620 DBusMessage
*r
= NULL
;
621 const char *path
, *interface
, *member
;
625 path
= dbus_message_get_path(m
);
626 interface
= dbus_message_get_interface(m
);
627 member
= dbus_message_get_member(m
);
629 pa_log_debug("dbus: path=%s, interface=%s, member=%s", path
, interface
, member
);
631 if (!pa_streq(path
, A2DP_SOURCE_ENDPOINT
) && !pa_streq(path
, A2DP_SINK_ENDPOINT
))
632 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
634 if (dbus_message_is_method_call(m
, "org.freedesktop.DBus.Introspectable", "Introspect")) {
635 const char *xml
= ENDPOINT_INTROSPECT_XML
;
637 pa_assert_se(r
= dbus_message_new_method_return(m
));
638 pa_assert_se(dbus_message_append_args(r
, DBUS_TYPE_STRING
, &xml
, DBUS_TYPE_INVALID
));
640 } else if (dbus_message_is_method_call(m
, BLUEZ_MEDIA_ENDPOINT_INTERFACE
, "SetConfiguration"))
641 r
= endpoint_set_configuration(c
, m
, userdata
);
642 else if (dbus_message_is_method_call(m
, BLUEZ_MEDIA_ENDPOINT_INTERFACE
, "SelectConfiguration"))
643 r
= endpoint_select_configuration(c
, m
, userdata
);
644 else if (dbus_message_is_method_call(m
, BLUEZ_MEDIA_ENDPOINT_INTERFACE
, "ClearConfiguration"))
645 r
= endpoint_clear_configuration(c
, m
, userdata
);
646 else if (dbus_message_is_method_call(m
, BLUEZ_MEDIA_ENDPOINT_INTERFACE
, "Release"))
647 r
= endpoint_release(c
, m
, userdata
);
649 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
652 pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y
->connection
), r
, NULL
));
653 dbus_message_unref(r
);
656 return DBUS_HANDLER_RESULT_HANDLED
;
659 static void endpoint_init(pa_bluetooth_discovery
*y
, pa_bluetooth_profile_t profile
) {
660 static const DBusObjectPathVTable vtable_endpoint
= {
661 .message_function
= endpoint_handler
,
667 case PA_BLUETOOTH_PROFILE_A2DP_SINK
:
668 pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y
->connection
), A2DP_SOURCE_ENDPOINT
,
669 &vtable_endpoint
, y
));
671 case PA_BLUETOOTH_PROFILE_A2DP_SOURCE
:
672 pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y
->connection
), A2DP_SINK_ENDPOINT
,
673 &vtable_endpoint
, y
));
676 pa_assert_not_reached();
681 static void endpoint_done(pa_bluetooth_discovery
*y
, pa_bluetooth_profile_t profile
) {
685 case PA_BLUETOOTH_PROFILE_A2DP_SINK
:
686 dbus_connection_unregister_object_path(pa_dbus_connection_get(y
->connection
), A2DP_SOURCE_ENDPOINT
);
688 case PA_BLUETOOTH_PROFILE_A2DP_SOURCE
:
689 dbus_connection_unregister_object_path(pa_dbus_connection_get(y
->connection
), A2DP_SINK_ENDPOINT
);
692 pa_assert_not_reached();
697 pa_bluetooth_discovery
* pa_bluetooth_discovery_get(pa_core
*c
) {
698 pa_bluetooth_discovery
*y
;
700 DBusConnection
*conn
;
703 if ((y
= pa_shared_get(c
, "bluetooth-discovery")))
704 return pa_bluetooth_discovery_ref(y
);
706 y
= pa_xnew0(pa_bluetooth_discovery
, 1);
709 y
->adapters
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
710 y
->devices
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
711 y
->transports
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
713 for (i
= 0; i
< PA_BLUETOOTH_HOOK_MAX
; i
++)
714 pa_hook_init(&y
->hooks
[i
], y
);
716 pa_shared_set(c
, "bluetooth-discovery", y
);
718 dbus_error_init(&err
);
720 if (!(y
->connection
= pa_dbus_bus_get(y
->core
, DBUS_BUS_SYSTEM
, &err
))) {
721 pa_log_error("Failed to get D-Bus connection: %s", err
.message
);
725 conn
= pa_dbus_connection_get(y
->connection
);
727 /* dynamic detection of bluetooth audio devices */
728 if (!dbus_connection_add_filter(conn
, filter_cb
, y
, NULL
)) {
729 pa_log_error("Failed to add filter function");
732 y
->filter_added
= true;
734 if (pa_dbus_add_matches(conn
, &err
,
735 "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'"
736 ",arg0='" BLUEZ_SERVICE
"'",
738 pa_log_error("Failed to add D-Bus matches: %s", err
.message
);
741 y
->matches_added
= true;
743 endpoint_init(y
, PA_BLUETOOTH_PROFILE_A2DP_SINK
);
744 endpoint_init(y
, PA_BLUETOOTH_PROFILE_A2DP_SOURCE
);
749 pa_bluetooth_discovery_unref(y
);
750 dbus_error_free(&err
);
755 pa_bluetooth_discovery
* pa_bluetooth_discovery_ref(pa_bluetooth_discovery
*y
) {
757 pa_assert(PA_REFCNT_VALUE(y
) > 0);
764 void pa_bluetooth_discovery_unref(pa_bluetooth_discovery
*y
) {
766 pa_assert(PA_REFCNT_VALUE(y
) > 0);
768 if (PA_REFCNT_DEC(y
) > 0)
772 device_remove_all(y
);
773 pa_hashmap_free(y
->devices
);
777 adapter_remove_all(y
);
778 pa_hashmap_free(y
->adapters
);
782 pa_assert(pa_hashmap_isempty(y
->transports
));
783 pa_hashmap_free(y
->transports
);
788 if (y
->matches_added
)
789 pa_dbus_remove_matches(pa_dbus_connection_get(y
->connection
),
790 "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',"
791 "arg0='" BLUEZ_SERVICE
"'",
795 dbus_connection_remove_filter(pa_dbus_connection_get(y
->connection
), filter_cb
, y
);
797 endpoint_done(y
, PA_BLUETOOTH_PROFILE_A2DP_SINK
);
798 endpoint_done(y
, PA_BLUETOOTH_PROFILE_A2DP_SOURCE
);
800 pa_dbus_connection_unref(y
->connection
);
803 pa_shared_remove(y
->core
, "bluetooth-discovery");