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 "bluez5-util.h"
38 #define BLUEZ_SERVICE "org.bluez"
40 struct pa_bluetooth_discovery
{
44 pa_dbus_connection
*connection
;
47 pa_hook hooks
[PA_BLUETOOTH_HOOK_MAX
];
52 static pa_bluetooth_device
* device_create(pa_bluetooth_discovery
*y
, const char *path
) {
53 pa_bluetooth_device
*d
;
58 d
= pa_xnew0(pa_bluetooth_device
, 1);
60 d
->path
= pa_xstrdup(path
);
62 pa_hashmap_put(y
->devices
, d
->path
, d
);
67 pa_bluetooth_device
* pa_bluetooth_discovery_get_device_by_path(pa_bluetooth_discovery
*y
, const char *path
) {
68 pa_bluetooth_device
*d
;
71 pa_assert(PA_REFCNT_VALUE(y
) > 0);
74 if ((d
= pa_hashmap_get(y
->devices
, path
)))
75 if (d
->device_info_valid
== 1)
81 pa_bluetooth_device
* pa_bluetooth_discovery_get_device_by_address(pa_bluetooth_discovery
*y
, const char *remote
, const char *local
) {
82 pa_bluetooth_device
*d
;
86 pa_assert(PA_REFCNT_VALUE(y
) > 0);
90 while ((d
= pa_hashmap_iterate(y
->devices
, &state
, NULL
)))
91 if (pa_streq(d
->address
, remote
) && pa_streq(d
->adapter
->address
, local
))
92 return d
->device_info_valid
== 1 ? d
: NULL
;
97 static void device_free(pa_bluetooth_device
*d
) {
104 pa_xfree(d
->address
);
108 static void device_remove(pa_bluetooth_discovery
*y
, const char *path
) {
109 pa_bluetooth_device
*d
;
111 if (!(d
= pa_hashmap_remove(y
->devices
, path
)))
112 pa_log_warn("Unknown device removed %s", path
);
114 pa_log_debug("Device %s removed", path
);
119 static void device_remove_all(pa_bluetooth_discovery
*y
) {
120 pa_bluetooth_device
*d
;
124 while ((d
= pa_hashmap_steal_first(y
->devices
))) {
125 d
->device_info_valid
= -1;
126 pa_hook_fire(&y
->hooks
[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED
], d
);
131 static pa_bluetooth_adapter
* adapter_create(pa_bluetooth_discovery
*y
, const char *path
) {
132 pa_bluetooth_adapter
*a
;
137 a
= pa_xnew0(pa_bluetooth_adapter
, 1);
139 a
->path
= pa_xstrdup(path
);
141 pa_hashmap_put(y
->adapters
, a
->path
, a
);
146 static void adapter_remove_all(pa_bluetooth_discovery
*y
) {
147 pa_bluetooth_adapter
*a
;
151 /* When this function is called all devices have already been freed */
153 while ((a
= pa_hashmap_steal_first(y
->adapters
))) {
155 pa_xfree(a
->address
);
160 pa_hook
* pa_bluetooth_discovery_hook(pa_bluetooth_discovery
*y
, pa_bluetooth_hook_t hook
) {
162 pa_assert(PA_REFCNT_VALUE(y
) > 0);
164 return &y
->hooks
[hook
];
167 static DBusHandlerResult
filter_cb(DBusConnection
*bus
, DBusMessage
*m
, void *userdata
) {
168 pa_bluetooth_discovery
*y
;
173 pa_assert_se(y
= userdata
);
175 dbus_error_init(&err
);
177 if (dbus_message_is_signal(m
, "org.freedesktop.DBus", "NameOwnerChanged")) {
178 const char *name
, *old_owner
, *new_owner
;
180 if (!dbus_message_get_args(m
, &err
,
181 DBUS_TYPE_STRING
, &name
,
182 DBUS_TYPE_STRING
, &old_owner
,
183 DBUS_TYPE_STRING
, &new_owner
,
184 DBUS_TYPE_INVALID
)) {
185 pa_log_error("Failed to parse org.freedesktop.DBus.NameOwnerChanged: %s", err
.message
);
189 if (pa_streq(name
, BLUEZ_SERVICE
)) {
190 if (old_owner
&& *old_owner
) {
191 pa_log_debug("Bluetooth daemon disappeared");
192 device_remove_all(y
);
193 adapter_remove_all(y
);
196 if (new_owner
&& *new_owner
) {
197 pa_log_debug("Bluetooth daemon appeared");
198 /* TODO: get managed objects */
202 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
206 dbus_error_free(&err
);
208 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
211 pa_bluetooth_discovery
* pa_bluetooth_discovery_get(pa_core
*c
) {
212 pa_bluetooth_discovery
*y
;
214 DBusConnection
*conn
;
217 if ((y
= pa_shared_get(c
, "bluetooth-discovery")))
218 return pa_bluetooth_discovery_ref(y
);
220 y
= pa_xnew0(pa_bluetooth_discovery
, 1);
223 y
->adapters
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
224 y
->devices
= pa_hashmap_new(pa_idxset_string_hash_func
, pa_idxset_string_compare_func
);
226 for (i
= 0; i
< PA_BLUETOOTH_HOOK_MAX
; i
++)
227 pa_hook_init(&y
->hooks
[i
], y
);
229 pa_shared_set(c
, "bluetooth-discovery", y
);
231 dbus_error_init(&err
);
233 if (!(y
->connection
= pa_dbus_bus_get(y
->core
, DBUS_BUS_SYSTEM
, &err
))) {
234 pa_log_error("Failed to get D-Bus connection: %s", err
.message
);
238 conn
= pa_dbus_connection_get(y
->connection
);
240 /* dynamic detection of bluetooth audio devices */
241 if (!dbus_connection_add_filter(conn
, filter_cb
, y
, NULL
)) {
242 pa_log_error("Failed to add filter function");
245 y
->filter_added
= true;
247 if (pa_dbus_add_matches(conn
, &err
,
248 "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'"
249 ",arg0='" BLUEZ_SERVICE
"'",
251 pa_log_error("Failed to add D-Bus matches: %s", err
.message
);
254 y
->matches_added
= true;
259 pa_bluetooth_discovery_unref(y
);
260 dbus_error_free(&err
);
265 pa_bluetooth_discovery
* pa_bluetooth_discovery_ref(pa_bluetooth_discovery
*y
) {
267 pa_assert(PA_REFCNT_VALUE(y
) > 0);
274 void pa_bluetooth_discovery_unref(pa_bluetooth_discovery
*y
) {
276 pa_assert(PA_REFCNT_VALUE(y
) > 0);
278 if (PA_REFCNT_DEC(y
) > 0)
282 device_remove_all(y
);
283 pa_hashmap_free(y
->devices
);
287 adapter_remove_all(y
);
288 pa_hashmap_free(y
->adapters
);
293 if (y
->matches_added
)
294 pa_dbus_remove_matches(pa_dbus_connection_get(y
->connection
),
295 "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',"
296 "arg0='" BLUEZ_SERVICE
"'",
300 dbus_connection_remove_filter(pa_dbus_connection_get(y
->connection
), filter_cb
, y
);
302 pa_dbus_connection_unref(y
->connection
);
305 pa_shared_remove(y
->core
, "bluetooth-discovery");