bluetooth: Track oFono service
[platform/upstream/pulseaudio.git] / src / modules / bluetooth / hfaudioagent-ofono.c
1 /***
2   This file is part of PulseAudio.
3
4   Copyright 2013 João Paulo Rechi Vita
5
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.
10
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.
15
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
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <pulsecore/core-util.h>
27 #include <pulsecore/dbus-shared.h>
28 #include <pulsecore/shared.h>
29
30 #include "bluez5-util.h"
31
32 #include "hfaudioagent.h"
33
34 #define HFP_AUDIO_CODEC_CVSD    0x01
35 #define HFP_AUDIO_CODEC_MSBC    0x02
36
37 #define OFONO_SERVICE "org.ofono"
38 #define HF_AUDIO_AGENT_INTERFACE OFONO_SERVICE ".HandsfreeAudioAgent"
39 #define HF_AUDIO_MANAGER_INTERFACE OFONO_SERVICE ".HandsfreeAudioManager"
40
41 #define HF_AUDIO_AGENT_PATH "/HandsfreeAudioAgent"
42
43 #define HF_AUDIO_AGENT_XML                                          \
44     DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
45     "<node>"                                                        \
46     "  <interface name=\"org.freedesktop.DBus.Introspectable\">"    \
47     "    <method name=\"Introspect\">"                              \
48     "      <arg direction=\"out\" type=\"s\" />"                    \
49     "    </method>"                                                 \
50     "  </interface>"                                                \
51     "  <interface name=\"org.ofono.HandsfreeAudioAgent\">"          \
52     "    <method name=\"Release\">"                                 \
53     "    </method>"                                                 \
54     "    <method name=\"NewConnection\">"                           \
55     "      <arg direction=\"in\"  type=\"o\" name=\"card_path\" />" \
56     "      <arg direction=\"in\"  type=\"h\" name=\"sco_fd\" />"    \
57     "      <arg direction=\"in\"  type=\"y\" name=\"codec\" />"     \
58     "    </method>"                                                 \
59     "  </interface>"                                                \
60     "</node>"
61
62 typedef struct hf_audio_card {
63     char *path;
64     char *remote;
65     char *local;
66
67     int fd;
68     uint8_t codec;
69
70     pa_bluetooth_transport *transport;
71 } hf_audio_card;
72
73 struct hf_audio_agent_data {
74     pa_core *core;
75     pa_dbus_connection *connection;
76     pa_bluetooth_discovery *discovery;
77
78     bool filter_added;
79     char *ofono_bus_id;
80     pa_hashmap *hf_audio_cards;
81
82     PA_LLIST_HEAD(pa_dbus_pending, pending);
83 };
84
85 static pa_dbus_pending* pa_bluetooth_dbus_send_and_add_to_pending(hf_audio_agent_data *hfdata, DBusMessage *m,
86                                                                   DBusPendingCallNotifyFunction func, void *call_data) {
87     pa_dbus_pending *p;
88     DBusPendingCall *call;
89
90     pa_assert(hfdata);
91     pa_assert(m);
92
93     pa_assert_se(dbus_connection_send_with_reply(pa_dbus_connection_get(hfdata->connection), m, &call, -1));
94
95     p = pa_dbus_pending_new(pa_dbus_connection_get(hfdata->connection), m, call, hfdata, call_data);
96     PA_LLIST_PREPEND(pa_dbus_pending, hfdata->pending, p);
97     dbus_pending_call_set_notify(call, func, p, NULL);
98
99     return p;
100 }
101
102 static hf_audio_card *hf_audio_card_new(hf_audio_agent_data *hfdata, const char *path) {
103     hf_audio_card *hfac = pa_xnew0(hf_audio_card, 1);
104
105     hfac->path = pa_xstrdup(path);
106     hfac->fd = -1;
107
108     return hfac;
109 }
110
111 static void hf_audio_card_free(void *data) {
112     hf_audio_card *hfac = data;
113
114     pa_assert(hfac);
115
116     pa_bluetooth_transport_free(hfac->transport);
117     pa_xfree(hfac->path);
118     pa_xfree(hfac->remote);
119     pa_xfree(hfac->local);
120     pa_xfree(hfac);
121 }
122
123 static int hf_audio_agent_transport_acquire(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu) {
124     hf_audio_agent_data *hfdata = t->userdata;
125     hf_audio_card *hfac = pa_hashmap_get(hfdata->hf_audio_cards, t->path);
126
127     if (!optional) {
128         DBusMessage *m;
129
130         pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, "org.ofono.HandsfreeAudioCard", "Connect"));
131         pa_assert_se(dbus_connection_send(pa_dbus_connection_get(hfdata->connection), m, NULL));
132
133         return -1;
134     }
135
136     /* The correct block size should take into account the SCO MTU from
137      * the Bluetooth adapter and (for adapters in the USB bus) the MxPS
138      * value from the Isoc USB endpoint in use by btusb and should be
139      * made available to userspace by the Bluetooth kernel subsystem.
140      * Meanwhile the empiric value 48 will be used. */
141     if (imtu)
142         *imtu = 48;
143     if (omtu)
144         *omtu = 48;
145
146     if (hfac) {
147         t->codec = hfac->codec;
148         return hfac->fd;
149     } else
150         return -1;
151 }
152
153 static void hf_audio_agent_transport_release(pa_bluetooth_transport *t) {
154 }
155
156 static void hf_audio_agent_card_found(hf_audio_agent_data *hfdata, const char *path, DBusMessageIter *props_i) {
157     DBusMessageIter i, value_i;
158     const char *key, *value;
159     hf_audio_card *hfac;
160     pa_bluetooth_device *d;
161
162     pa_assert(hfdata);
163     pa_assert(path);
164     pa_assert(props_i);
165
166     pa_log_debug("New HF card found: %s", path);
167
168     hfac = hf_audio_card_new(hfdata, path);
169
170     while (dbus_message_iter_get_arg_type(props_i) != DBUS_TYPE_INVALID) {
171         char c;
172
173         if ((c = dbus_message_iter_get_arg_type(props_i)) != DBUS_TYPE_DICT_ENTRY) {
174             pa_log_error("Invalid properties for %s: expected \'e\', received \'%c\'", path, c);
175             goto fail;
176         }
177
178         dbus_message_iter_recurse(props_i, &i);
179
180         if ((c = dbus_message_iter_get_arg_type(&i)) != DBUS_TYPE_STRING) {
181             pa_log_error("Invalid properties for %s: expected \'s\', received \'%c\'", path, c);
182             goto fail;
183         }
184
185         dbus_message_iter_get_basic(&i, &key);
186         dbus_message_iter_next(&i);
187
188         if ((c = dbus_message_iter_get_arg_type(&i)) != DBUS_TYPE_VARIANT) {
189             pa_log_error("Invalid properties for %s: expected \'v\', received \'%c\'", path, c);
190             goto fail;
191         }
192
193         dbus_message_iter_recurse(&i, &value_i);
194
195         if ((c = dbus_message_iter_get_arg_type(&value_i)) != DBUS_TYPE_STRING) {
196             pa_log_error("Invalid properties for %s: expected \'s\', received \'%c\'", path, c);
197             goto fail;
198         }
199
200         dbus_message_iter_get_basic(&value_i, &value);
201
202         if (pa_streq(key, "RemoteAddress"))
203             hfac->remote = pa_xstrdup(value);
204         else if (pa_streq(key, "LocalAddress"))
205             hfac->local = pa_xstrdup(value);
206
207         pa_log_debug("%s: %s", key, value);
208
209         dbus_message_iter_next(props_i);
210     }
211
212     pa_hashmap_put(hfdata->hf_audio_cards, hfac->path, hfac);
213
214     d = pa_bluetooth_discovery_get_device_by_address(hfdata->discovery, hfac->remote, hfac->local);
215     if (d) {
216         hfac->transport = pa_bluetooth_transport_new(d, hfdata->ofono_bus_id, path, PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY, NULL, 0);
217         hfac->transport->acquire = hf_audio_agent_transport_acquire;
218         hfac->transport->release = hf_audio_agent_transport_release;
219         hfac->transport->userdata = hfdata;
220
221         d->transports[PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY] = hfac->transport;
222
223         pa_bluetooth_transport_put(hfac->transport);
224     } else
225         pa_log_error("Device doesnt exist for %s", path);
226
227     return;
228
229 fail:
230     pa_xfree(hfac);
231 }
232
233 static void hf_audio_agent_get_cards_reply(DBusPendingCall *pending, void *userdata) {
234     DBusMessage *r;
235     pa_dbus_pending *p;
236     hf_audio_agent_data *hfdata;
237     DBusMessageIter i, array_i, struct_i, props_i;
238     char c;
239
240     pa_assert_se(p = userdata);
241     pa_assert_se(hfdata = p->context_data);
242     pa_assert_se(r = dbus_pending_call_steal_reply(pending));
243
244     if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
245         pa_log_error("Failed to get a list of handsfree audio cards from ofono: %s: %s",
246                      dbus_message_get_error_name(r), pa_dbus_get_error_message(r));
247         goto finish;
248     }
249
250     dbus_message_iter_init(r, &i);
251     if ((c = dbus_message_iter_get_arg_type(&i)) != DBUS_TYPE_ARRAY) {
252         pa_log_error("Invalid arguments in GetCards() reply: expected \'a\', received \'%c\'", c);
253         goto finish;
254     }
255
256     dbus_message_iter_recurse(&i, &array_i);
257     while (dbus_message_iter_get_arg_type(&array_i) != DBUS_TYPE_INVALID) {
258         const char *path;
259
260         if ((c = dbus_message_iter_get_arg_type(&array_i)) != DBUS_TYPE_STRUCT) {
261             pa_log_error("Invalid arguments in GetCards() reply: expected \'r\', received \'%c\'", c);
262             goto finish;
263         }
264
265         dbus_message_iter_recurse(&array_i, &struct_i);
266         if ((c = dbus_message_iter_get_arg_type(&struct_i)) != DBUS_TYPE_OBJECT_PATH) {
267             pa_log_error("Invalid arguments in GetCards() reply: expected \'o\', received \'%c\'", c);
268             goto finish;
269         }
270
271         dbus_message_iter_get_basic(&struct_i, &path);
272
273         dbus_message_iter_next(&struct_i);
274         if ((c = dbus_message_iter_get_arg_type(&struct_i)) != DBUS_TYPE_ARRAY) {
275             pa_log_error("Invalid arguments in GetCards() reply: expected \'a\', received \'%c\'", c);
276             goto finish;
277         }
278
279         dbus_message_iter_recurse(&struct_i, &props_i);
280
281         hf_audio_agent_card_found(hfdata, path, &props_i);
282
283         dbus_message_iter_next(&array_i);
284     }
285
286 finish:
287     dbus_message_unref(r);
288
289     PA_LLIST_REMOVE(pa_dbus_pending, hfdata->pending, p);
290     pa_dbus_pending_free(p);
291 }
292
293 static void hf_audio_agent_get_cards(hf_audio_agent_data *hfdata) {
294     DBusMessage *m;
295
296     pa_assert(hfdata);
297
298     pa_assert_se(m = dbus_message_new_method_call(OFONO_SERVICE, "/", HF_AUDIO_MANAGER_INTERFACE, "GetCards"));
299     pa_bluetooth_dbus_send_and_add_to_pending(hfdata, m, hf_audio_agent_get_cards_reply, NULL);
300 }
301
302 static void hf_audio_agent_register_reply(DBusPendingCall *pending, void *userdata) {
303     DBusMessage *r;
304     pa_dbus_pending *p;
305     hf_audio_agent_data *hfdata;
306
307     pa_assert_se(p = userdata);
308     pa_assert_se(hfdata = p->context_data);
309     pa_assert_se(r = dbus_pending_call_steal_reply(pending));
310
311     if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
312         pa_log_error("Failed to register as a handsfree audio agent with ofono: %s: %s",
313                      dbus_message_get_error_name(r), pa_dbus_get_error_message(r));
314         goto finish;
315     }
316
317     hfdata->ofono_bus_id = pa_xstrdup(dbus_message_get_sender(r));
318
319     hf_audio_agent_get_cards(hfdata);
320
321 finish:
322     dbus_message_unref(r);
323
324     PA_LLIST_REMOVE(pa_dbus_pending, hfdata->pending, p);
325     pa_dbus_pending_free(p);
326 }
327
328 static void hf_audio_agent_register(hf_audio_agent_data *hfdata) {
329     DBusMessage *m;
330     unsigned char codecs[2];
331     const unsigned char *pcodecs = codecs;
332     int ncodecs = 0;
333     const char *path = HF_AUDIO_AGENT_PATH;
334
335     pa_assert(hfdata);
336
337     pa_assert_se(m = dbus_message_new_method_call(OFONO_SERVICE, "/", HF_AUDIO_MANAGER_INTERFACE, "Register"));
338
339     codecs[ncodecs++] = HFP_AUDIO_CODEC_CVSD;
340     codecs[ncodecs++] = HFP_AUDIO_CODEC_MSBC;
341
342     pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &pcodecs, ncodecs,
343                                           DBUS_TYPE_INVALID));
344
345     pa_bluetooth_dbus_send_and_add_to_pending(hfdata, m, hf_audio_agent_register_reply, NULL);
346 }
347
348 static void hf_audio_agent_unregister(hf_audio_agent_data *hfdata) {
349     DBusMessage *m;
350     const char *path = HF_AUDIO_AGENT_PATH;
351
352     pa_assert(hfdata);
353     pa_assert(hfdata->connection);
354
355     if (hfdata->ofono_bus_id) {
356         pa_assert_se(m = dbus_message_new_method_call(hfdata->ofono_bus_id, "/", HF_AUDIO_MANAGER_INTERFACE, "Unregister"));
357         pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID));
358         pa_assert_se(dbus_connection_send(pa_dbus_connection_get(hfdata->connection), m, NULL));
359
360         pa_xfree(hfdata->ofono_bus_id);
361         hfdata->ofono_bus_id = NULL;
362     }
363 }
364
365 static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *data) {
366     const char *sender;
367     DBusError err;
368     hf_audio_agent_data *hfdata = data;
369
370     pa_assert(bus);
371     pa_assert(m);
372     pa_assert(hfdata);
373
374     sender = dbus_message_get_sender(m);
375     if (!pa_safe_streq(hfdata->ofono_bus_id, sender) && !pa_streq("org.freedesktop.DBus", sender))
376         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
377
378     dbus_error_init(&err);
379
380     if (dbus_message_is_signal(m, "org.freedesktop.DBus", "NameOwnerChanged")) {
381         const char *name, *old_owner, *new_owner;
382
383         if (!dbus_message_get_args(m, &err,
384                                    DBUS_TYPE_STRING, &name,
385                                    DBUS_TYPE_STRING, &old_owner,
386                                    DBUS_TYPE_STRING, &new_owner,
387                                    DBUS_TYPE_INVALID)) {
388             pa_log_error("Failed to parse org.freedesktop.DBus.NameOwnerChanged: %s", err.message);
389             goto fail;
390         }
391
392         if (pa_streq(name, OFONO_SERVICE)) {
393
394             if (old_owner && *old_owner) {
395                 pa_log_debug("oFono disappeared");
396
397                 if (hfdata->hf_audio_cards) {
398                     pa_hashmap_free(hfdata->hf_audio_cards);
399                     hfdata->hf_audio_cards = NULL;
400                 }
401
402                 if(hfdata->ofono_bus_id) {
403                     pa_xfree(hfdata->ofono_bus_id);
404                     hfdata->ofono_bus_id = NULL;
405                 }
406             }
407
408             if (new_owner && *new_owner) {
409                 pa_log_debug("oFono appeared");
410                 hf_audio_agent_register(hfdata);
411             }
412         }
413
414     }
415
416 fail:
417     dbus_error_free(&err);
418     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
419 }
420
421 static DBusMessage *hf_audio_agent_release(DBusConnection *c, DBusMessage *m, void *data) {
422     DBusMessage *r;
423     const char *sender;
424     hf_audio_agent_data *hfdata = data;
425
426     pa_assert(hfdata);
427
428     sender = dbus_message_get_sender(m);
429     if (!pa_streq(hfdata->ofono_bus_id, sender)) {
430         pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.NotAllowed", "Operation is not allowed by this sender"));
431         return r;
432     }
433
434     pa_log_debug("HF audio agent has been unregistered by oFono (%s)", hfdata->ofono_bus_id);
435
436     if (hfdata->hf_audio_cards) {
437         pa_hashmap_free(hfdata->hf_audio_cards);
438         hfdata->hf_audio_cards = NULL;
439     }
440
441     if(hfdata->ofono_bus_id) {
442         pa_xfree(hfdata->ofono_bus_id);
443         hfdata->ofono_bus_id = NULL;
444     }
445
446     pa_assert_se(r = dbus_message_new_method_return(m));
447
448     return r;
449 }
450
451 static DBusMessage *hf_audio_agent_new_connection(DBusConnection *c, DBusMessage *m, void *data) {
452     DBusMessage *r;
453     const char *sender;
454     hf_audio_agent_data *hfdata = data;
455
456     pa_assert(hfdata);
457
458     sender = dbus_message_get_sender(m);
459     if (!pa_streq(hfdata->ofono_bus_id, sender)) {
460         pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.NotAllowed", "Operation is not allowed by this sender"));
461         return r;
462     }
463
464     r = dbus_message_new_error(m, "org.ofono.Error.NotImplemented", "Operation is not implemented");
465     return r;
466 }
467
468 static DBusHandlerResult hf_audio_agent_handler(DBusConnection *c, DBusMessage *m, void *data) {
469     hf_audio_agent_data *hfdata = data;
470     DBusMessage *r = NULL;
471     const char *path, *interface, *member;
472
473     pa_assert(hfdata);
474
475     path = dbus_message_get_path(m);
476     interface = dbus_message_get_interface(m);
477     member = dbus_message_get_member(m);
478
479     if (!pa_streq(path, HF_AUDIO_AGENT_PATH))
480         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
481
482     pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member);
483
484     if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
485         const char *xml = HF_AUDIO_AGENT_XML;
486
487         pa_assert_se(r = dbus_message_new_method_return(m));
488         pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID));
489
490     } else if (dbus_message_is_method_call(m, HF_AUDIO_AGENT_INTERFACE, "NewConnection"))
491         r = hf_audio_agent_new_connection(c, m, data);
492     else if (dbus_message_is_method_call(m, HF_AUDIO_AGENT_INTERFACE, "Release"))
493         r = hf_audio_agent_release(c, m, data);
494     else
495         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
496
497     if (r) {
498         pa_assert_se(dbus_connection_send(pa_dbus_connection_get(hfdata->connection), r, NULL));
499         dbus_message_unref(r);
500     }
501
502     return DBUS_HANDLER_RESULT_HANDLED;
503 }
504
505 hf_audio_agent_data *hf_audio_agent_init(pa_core *c) {
506     hf_audio_agent_data *hfdata;
507     DBusError err;
508     static const DBusObjectPathVTable vtable_hf_audio_agent = {
509         .message_function = hf_audio_agent_handler,
510     };
511
512     pa_assert(c);
513
514     hfdata = pa_xnew0(hf_audio_agent_data, 1);
515     hfdata->core = c;
516     hfdata->hf_audio_cards = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func,
517                                                  NULL, hf_audio_card_free);
518     hfdata->discovery = pa_shared_get(c, "bluetooth-discovery");
519
520     dbus_error_init(&err);
521
522     if (!(hfdata->connection = pa_dbus_bus_get(c, DBUS_BUS_SYSTEM, &err))) {
523         pa_log("Failed to get D-Bus connection: %s", err.message);
524         dbus_error_free(&err);
525         return NULL;
526     }
527
528     /* dynamic detection of handsfree audio cards */
529     if (!dbus_connection_add_filter(pa_dbus_connection_get(hfdata->connection), filter_cb, hfdata, NULL)) {
530         pa_log_error("Failed to add filter function");
531         hf_audio_agent_done(hfdata);
532         return NULL;
533     }
534     hfdata->filter_added = true;
535
536     if (pa_dbus_add_matches(pa_dbus_connection_get(hfdata->connection), &err,
537             "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',"
538             "arg0='" OFONO_SERVICE "'",
539             "type='signal',sender='" OFONO_SERVICE "',interface='" HF_AUDIO_MANAGER_INTERFACE "',member='CardAdded'",
540             "type='signal',sender='" OFONO_SERVICE "',interface='" HF_AUDIO_MANAGER_INTERFACE "',member='CardRemoved'",
541             NULL) < 0) {
542         pa_log("Failed to add oFono D-Bus matches: %s", err.message);
543         hf_audio_agent_done(hfdata);
544         return NULL;
545     }
546
547     pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(hfdata->connection), HF_AUDIO_AGENT_PATH,
548                                                       &vtable_hf_audio_agent, hfdata));
549
550     hf_audio_agent_register(hfdata);
551
552     return hfdata;
553 }
554
555 void hf_audio_agent_done(hf_audio_agent_data *data) {
556     hf_audio_agent_data *hfdata = data;
557
558     pa_assert(hfdata);
559
560     pa_dbus_free_pending_list(&hfdata->pending);
561
562     if (hfdata->hf_audio_cards) {
563         pa_hashmap_free(hfdata->hf_audio_cards);
564         hfdata->hf_audio_cards = NULL;
565     }
566
567     if (hfdata->connection) {
568
569         pa_dbus_remove_matches(
570             pa_dbus_connection_get(hfdata->connection),
571             "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',"
572             "arg0='" OFONO_SERVICE "'",
573             "type='signal',sender='" OFONO_SERVICE "',interface='" HF_AUDIO_MANAGER_INTERFACE "',member='CardAdded'",
574             "type='signal',sender='" OFONO_SERVICE "',interface='" HF_AUDIO_MANAGER_INTERFACE "',member='CardRemoved'",
575             NULL);
576
577         if (hfdata->filter_added)
578             dbus_connection_remove_filter(pa_dbus_connection_get(hfdata->connection), filter_cb, hfdata);
579
580         hf_audio_agent_unregister(hfdata);
581
582         dbus_connection_unregister_object_path(pa_dbus_connection_get(hfdata->connection), HF_AUDIO_AGENT_PATH);
583
584         pa_dbus_connection_unref(hfdata->connection);
585     }
586
587     if (hfdata->discovery)
588         hfdata->discovery = NULL;
589
590     pa_xfree(hfdata);
591 }