Bluetooth : Support AVC target feature
[platform/upstream/pulseaudio.git] / src / modules / bluetooth / bluez5-util.c
1 /***
2   This file is part of PulseAudio.
3
4   Copyright 2008-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, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <pulse/rtclock.h>
25 #include <pulse/timeval.h>
26 #include <pulse/xmalloc.h>
27
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>
35
36 #include "a2dp-codecs.h"
37
38 #include "bluez5-util.h"
39 #ifdef BLUETOOTH_APTX_SUPPORT
40 #include <dlfcn.h>
41 #endif
42
43 #define WAIT_FOR_PROFILES_TIMEOUT_USEC (3 * PA_USEC_PER_SEC)
44
45 #define BLUEZ_SERVICE "org.bluez"
46 #define BLUEZ_ADAPTER_INTERFACE BLUEZ_SERVICE ".Adapter1"
47 #define BLUEZ_DEVICE_INTERFACE BLUEZ_SERVICE ".Device1"
48 #define BLUEZ_MEDIA_INTERFACE BLUEZ_SERVICE ".Media1"
49 #define BLUEZ_MEDIA_ENDPOINT_INTERFACE BLUEZ_SERVICE ".MediaEndpoint1"
50 #define BLUEZ_MEDIA_TRANSPORT_INTERFACE BLUEZ_SERVICE ".MediaTransport1"
51
52 #define BLUEZ_ERROR_NOT_SUPPORTED "org.bluez.Error.NotSupported"
53
54 #define A2DP_SOURCE_ENDPOINT "/MediaEndpoint/A2DPSource"
55 #define A2DP_SINK_ENDPOINT "/MediaEndpoint/A2DPSink"
56
57 #ifdef TIZEN_BT_A2DP_MULTISTREAM
58 #define A2DP_SINK_ENDPOINT2 "/MediaEndpoint/A2DPSink2"
59 #endif
60
61 #ifdef BLUETOOTH_APTX_SUPPORT
62 #define A2DP_APTX_SOURCE_ENDPOINT "/MediaEndpoint/A2DPSource_aptx"
63 #endif
64
65 #define ENDPOINT_INTROSPECT_XML                                         \
66     DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                           \
67     "<node>"                                                            \
68     " <interface name=\"" BLUEZ_MEDIA_ENDPOINT_INTERFACE "\">"          \
69     "  <method name=\"SetConfiguration\">"                              \
70     "   <arg name=\"transport\" direction=\"in\" type=\"o\"/>"          \
71     "   <arg name=\"properties\" direction=\"in\" type=\"ay\"/>"        \
72     "  </method>"                                                       \
73     "  <method name=\"SelectConfiguration\">"                           \
74     "   <arg name=\"capabilities\" direction=\"in\" type=\"ay\"/>"      \
75     "   <arg name=\"configuration\" direction=\"out\" type=\"ay\"/>"    \
76     "  </method>"                                                       \
77     "  <method name=\"ClearConfiguration\">"                            \
78     "   <arg name=\"transport\" direction=\"in\" type=\"o\"/>"          \
79     "  </method>"                                                       \
80     "  <method name=\"Release\">"                                       \
81     "  </method>"                                                       \
82     "  <method name=\"SuspendMedia\">"                                  \
83     "  </method>"                                                       \
84     " </interface>"                                                     \
85     " <interface name=\"org.freedesktop.DBus.Introspectable\">"         \
86     "  <method name=\"Introspect\">"                                    \
87     "   <arg name=\"data\" type=\"s\" direction=\"out\"/>"              \
88     "  </method>"                                                       \
89     " </interface>"                                                     \
90     "</node>"
91
92 struct pa_bluetooth_discovery {
93     PA_REFCNT_DECLARE;
94
95     pa_core *core;
96     pa_dbus_connection *connection;
97     bool filter_added;
98     bool matches_added;
99     bool objects_listed;
100     pa_hook hooks[PA_BLUETOOTH_HOOK_MAX];
101     pa_hashmap *adapters;
102     pa_hashmap *devices;
103     pa_hashmap *transports;
104
105     int headset_backend;
106     pa_bluetooth_backend *ofono_backend, *native_backend;
107     PA_LLIST_HEAD(pa_dbus_pending, pending);
108 };
109
110 #ifdef BLUETOOTH_APTX_SUPPORT
111 static void *aptx_handle = NULL;
112
113 int pa_unload_aptx(void)
114 {
115     if (aptx_handle == NULL) {
116         pa_log_warn("Unable to unload apt-X library");
117         return -1;
118     }
119
120     dlclose(aptx_handle);
121     aptx_handle = NULL;
122
123     pa_log_debug("unloaded apt-X library successfully");
124     return 0;
125 }
126
127 int pa_load_aptx(const char *aptx_lib_name)
128 {
129     char* lib_path = NULL ;
130
131     if(aptx_lib_name == NULL)
132         return -1;
133
134     lib_path = pa_sprintf_malloc("%s/%s", PA_DLSEARCHPATH, aptx_lib_name);
135
136     if (!lib_path)
137         return -1;
138
139     pa_log_info("aptx_lib_path = [%s]", lib_path);
140
141     aptx_handle = dlopen(lib_path, RTLD_LAZY);
142     if (aptx_handle == NULL) {
143         pa_log_warn("Unable to load apt-X library [%s]", dlerror());
144         pa_xfree(lib_path);
145         return -1;
146     }
147
148     pa_log_debug("loaded apt-X library successfully");
149     pa_xfree(lib_path);
150
151     return 0;
152 }
153
154 void* pa_aptx_get_handle(void)
155 {
156     return aptx_handle;
157 }
158 #endif
159
160 static pa_dbus_pending* send_and_add_to_pending(pa_bluetooth_discovery *y, DBusMessage *m,
161                                                                   DBusPendingCallNotifyFunction func, void *call_data) {
162     pa_dbus_pending *p;
163     DBusPendingCall *call;
164
165     pa_assert(y);
166     pa_assert(m);
167
168     pa_assert_se(dbus_connection_send_with_reply(pa_dbus_connection_get(y->connection), m, &call, -1));
169
170     p = pa_dbus_pending_new(pa_dbus_connection_get(y->connection), m, call, y, call_data);
171     PA_LLIST_PREPEND(pa_dbus_pending, y->pending, p);
172     dbus_pending_call_set_notify(call, func, p, NULL);
173
174     return p;
175 }
176
177 static const char *check_variant_property(DBusMessageIter *i) {
178     const char *key;
179
180     pa_assert(i);
181
182     if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) {
183         pa_log_error("Property name not a string.");
184         return NULL;
185     }
186
187     dbus_message_iter_get_basic(i, &key);
188
189     if (!dbus_message_iter_next(i)) {
190         pa_log_error("Property value missing");
191         return NULL;
192     }
193
194     if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) {
195         pa_log_error("Property value not a variant.");
196         return NULL;
197     }
198
199     return key;
200 }
201
202 pa_bluetooth_transport *pa_bluetooth_transport_new(pa_bluetooth_device *d, const char *owner, const char *path,
203                                                    pa_bluetooth_profile_t p, const uint8_t *config, size_t size) {
204     pa_bluetooth_transport *t;
205
206     t = pa_xnew0(pa_bluetooth_transport, 1);
207     t->device = d;
208     t->owner = pa_xstrdup(owner);
209     t->path = pa_xstrdup(path);
210     t->profile = p;
211     t->config_size = size;
212
213     if (size > 0) {
214         t->config = pa_xnew(uint8_t, size);
215         memcpy(t->config, config, size);
216     }
217
218     return t;
219 }
220
221 static const char *transport_state_to_string(pa_bluetooth_transport_state_t state) {
222     switch(state) {
223         case PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED:
224             return "disconnected";
225         case PA_BLUETOOTH_TRANSPORT_STATE_IDLE:
226             return "idle";
227         case PA_BLUETOOTH_TRANSPORT_STATE_PLAYING:
228             return "playing";
229     }
230
231     return "invalid";
232 }
233
234 static bool device_supports_profile(pa_bluetooth_device *device, pa_bluetooth_profile_t profile) {
235     switch (profile) {
236         case PA_BLUETOOTH_PROFILE_A2DP_SINK:
237             return !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_A2DP_SINK);
238         case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
239             return !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_A2DP_SOURCE);
240         case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT:
241             return !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HSP_HS)
242                 || !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HFP_HF);
243         case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY:
244             return !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HSP_AG)
245                 || !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HFP_AG);
246         case PA_BLUETOOTH_PROFILE_OFF:
247             pa_assert_not_reached();
248     }
249
250     pa_assert_not_reached();
251 }
252
253 static bool device_is_profile_connected(pa_bluetooth_device *device, pa_bluetooth_profile_t profile) {
254     if (device->transports[profile] && device->transports[profile]->state != PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED)
255         return true;
256     else
257         return false;
258 }
259
260 static unsigned device_count_disconnected_profiles(pa_bluetooth_device *device) {
261     pa_bluetooth_profile_t profile;
262     unsigned count = 0;
263
264     for (profile = 0; profile < PA_BLUETOOTH_PROFILE_COUNT; profile++) {
265         if (!device_supports_profile(device, profile))
266             continue;
267
268         if (!device_is_profile_connected(device, profile))
269             count++;
270     }
271
272     return count;
273 }
274
275 static void device_stop_waiting_for_profiles(pa_bluetooth_device *device) {
276     if (!device->wait_for_profiles_timer)
277         return;
278
279     device->discovery->core->mainloop->time_free(device->wait_for_profiles_timer);
280     device->wait_for_profiles_timer = NULL;
281 }
282
283 static void wait_for_profiles_cb(pa_mainloop_api *api, pa_time_event* event, const struct timeval *tv, void *userdata) {
284     pa_bluetooth_device *device = userdata;
285     pa_strbuf *buf;
286     pa_bluetooth_profile_t profile;
287     bool first = true;
288     char *profiles_str;
289
290     device_stop_waiting_for_profiles(device);
291
292     buf = pa_strbuf_new();
293
294     for (profile = 0; profile < PA_BLUETOOTH_PROFILE_COUNT; profile++) {
295         if (device_is_profile_connected(device, profile))
296             continue;
297
298         if (!device_supports_profile(device, profile))
299             continue;
300
301         if (first)
302             first = false;
303         else
304             pa_strbuf_puts(buf, ", ");
305
306         pa_strbuf_puts(buf, pa_bluetooth_profile_to_string(profile));
307     }
308
309     profiles_str = pa_strbuf_to_string_free(buf);
310     pa_log_debug("Timeout expired, and device %s still has disconnected profiles: %s",
311                  device->path, profiles_str);
312     pa_xfree(profiles_str);
313     pa_hook_fire(&device->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED], device);
314 }
315
316 static void device_start_waiting_for_profiles(pa_bluetooth_device *device) {
317     pa_assert(!device->wait_for_profiles_timer);
318     device->wait_for_profiles_timer = pa_core_rttime_new(device->discovery->core,
319                                                          pa_rtclock_now() + WAIT_FOR_PROFILES_TIMEOUT_USEC,
320                                                          wait_for_profiles_cb, device);
321 }
322
323 void pa_bluetooth_transport_set_state(pa_bluetooth_transport *t, pa_bluetooth_transport_state_t state) {
324     bool old_any_connected;
325     unsigned n_disconnected_profiles;
326     bool new_device_appeared;
327     bool device_disconnected;
328
329     pa_assert(t);
330
331     if (t->state == state)
332         return;
333
334     old_any_connected = pa_bluetooth_device_any_transport_connected(t->device);
335
336     pa_log_debug("Transport %s state: %s -> %s",
337                  t->path, transport_state_to_string(t->state), transport_state_to_string(state));
338
339     t->state = state;
340
341     pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED], t);
342
343     /* If there are profiles that are expected to get connected soon (based
344      * on the UUID list), we wait for a bit before announcing the new
345      * device, so that all profiles have time to get connected before the
346      * card object is created. If we didn't wait, the card would always
347      * have only one profile marked as available in the initial state,
348      * which would prevent module-card-restore from restoring the initial
349      * profile properly. */
350
351     n_disconnected_profiles = device_count_disconnected_profiles(t->device);
352
353     new_device_appeared = !old_any_connected && pa_bluetooth_device_any_transport_connected(t->device);
354     device_disconnected = old_any_connected && !pa_bluetooth_device_any_transport_connected(t->device);
355
356     if (new_device_appeared) {
357         if (n_disconnected_profiles > 0)
358             device_start_waiting_for_profiles(t->device);
359         else
360             pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED], t->device);
361         return;
362     }
363
364     if (device_disconnected) {
365         if (t->device->wait_for_profiles_timer) {
366             /* If the timer is still running when the device disconnects, we
367              * never sent the notification of the device getting connected, so
368              * we don't need to send a notification about the disconnection
369              * either. Let's just stop the timer. */
370             device_stop_waiting_for_profiles(t->device);
371         } else
372             pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED], t->device);
373         return;
374     }
375
376     if (n_disconnected_profiles == 0 && t->device->wait_for_profiles_timer) {
377         /* All profiles are now connected, so we can stop the wait timer and
378          * send a notification of the new device. */
379         device_stop_waiting_for_profiles(t->device);
380         pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED], t->device);
381     }
382 }
383
384 void pa_bluetooth_transport_put(pa_bluetooth_transport *t) {
385     pa_assert(t);
386
387     t->device->transports[t->profile] = t;
388     pa_assert_se(pa_hashmap_put(t->device->discovery->transports, t->path, t) >= 0);
389     pa_bluetooth_transport_set_state(t, PA_BLUETOOTH_TRANSPORT_STATE_IDLE);
390 }
391
392 void pa_bluetooth_transport_unlink(pa_bluetooth_transport *t) {
393     pa_assert(t);
394
395     pa_bluetooth_transport_set_state(t, PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED);
396     pa_hashmap_remove(t->device->discovery->transports, t->path);
397     t->device->transports[t->profile] = NULL;
398 }
399
400 void pa_bluetooth_transport_free(pa_bluetooth_transport *t) {
401     pa_assert(t);
402
403     if (t->destroy)
404         t->destroy(t);
405     pa_bluetooth_transport_unlink(t);
406
407     pa_xfree(t->owner);
408     pa_xfree(t->path);
409     pa_xfree(t->config);
410     pa_xfree(t);
411 }
412
413 static int bluez5_transport_acquire_cb(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu) {
414     DBusMessage *m, *r;
415     DBusError err;
416     int ret;
417     uint16_t i, o;
418     const char *method = optional ? "TryAcquire" : "Acquire";
419
420     pa_assert(t);
421     pa_assert(t->device);
422     pa_assert(t->device->discovery);
423
424     pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, BLUEZ_MEDIA_TRANSPORT_INTERFACE, method));
425
426     dbus_error_init(&err);
427
428     r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(t->device->discovery->connection), m, -1, &err);
429     dbus_message_unref(m);
430     m = NULL;
431     if (!r) {
432         if (optional && pa_streq(err.name, "org.bluez.Error.NotAvailable"))
433             pa_log_info("Failed optional acquire of unavailable transport %s", t->path);
434         else
435             pa_log_error("Transport %s() failed for transport %s (%s)", method, t->path, err.message);
436
437         dbus_error_free(&err);
438         return -1;
439     }
440
441     if (!dbus_message_get_args(r, &err, DBUS_TYPE_UNIX_FD, &ret, DBUS_TYPE_UINT16, &i, DBUS_TYPE_UINT16, &o,
442                                DBUS_TYPE_INVALID)) {
443         pa_log_error("Failed to parse %s() reply: %s", method, err.message);
444         dbus_error_free(&err);
445         ret = -1;
446         goto finish;
447     }
448
449     if (imtu)
450         *imtu = i;
451
452     if (omtu)
453         *omtu = o;
454
455 finish:
456     dbus_message_unref(r);
457     return ret;
458 }
459
460 static void bluez5_transport_release_cb(pa_bluetooth_transport *t) {
461     DBusMessage *m, *r;
462     DBusError err;
463
464     pa_assert(t);
465     pa_assert(t->device);
466     pa_assert(t->device->discovery);
467
468     dbus_error_init(&err);
469
470     if (t->state <= PA_BLUETOOTH_TRANSPORT_STATE_IDLE) {
471         pa_log_info("Transport %s auto-released by BlueZ or already released", t->path);
472         return;
473     }
474
475     pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, BLUEZ_MEDIA_TRANSPORT_INTERFACE, "Release"));
476     r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(t->device->discovery->connection), m, -1, &err);
477     dbus_message_unref(m);
478     m = NULL;
479     if (r) {
480         dbus_message_unref(r);
481         r = NULL;
482     }
483
484     if (dbus_error_is_set(&err)) {
485         pa_log_error("Failed to release transport %s: %s", t->path, err.message);
486         dbus_error_free(&err);
487     } else
488         pa_log_info("Transport %s released", t->path);
489 }
490
491 bool pa_bluetooth_device_any_transport_connected(const pa_bluetooth_device *d) {
492     unsigned i;
493
494     pa_assert(d);
495
496     if (!d->valid)
497         return false;
498
499     for (i = 0; i < PA_BLUETOOTH_PROFILE_COUNT; i++)
500         if (d->transports[i] && d->transports[i]->state != PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED)
501             return true;
502
503     return false;
504 }
505
506 #ifdef __TIZEN_BT__
507 bool pa_bluetooth_device_sink_transport_connected(const pa_bluetooth_device *d) {
508     unsigned i;
509
510     pa_assert(d);
511
512     if (!d->valid)
513         return false;
514
515     for (i = 0; i < PA_BLUETOOTH_PROFILE_COUNT; i++)
516         if (d->transports[i] &&
517              d->transports[i]->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK &&
518               d->transports[i]->state != PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED)
519             return true;
520
521     return false;
522 }
523
524 bool pa_bluetooth_device_source_transport_connected(const pa_bluetooth_device *d) {
525     unsigned i;
526
527     pa_assert(d);
528
529     if (!d->valid)
530         return false;
531
532     for (i = 0; i < PA_BLUETOOTH_PROFILE_COUNT; i++)
533         if (d->transports[i] &&
534              d->transports[i]->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE &&
535               d->transports[i]->state != PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED)
536             return true;
537
538     return false;
539 }
540 #endif
541
542 static int transport_state_from_string(const char* value, pa_bluetooth_transport_state_t *state) {
543     pa_assert(value);
544     pa_assert(state);
545
546     if (pa_streq(value, "idle"))
547         *state = PA_BLUETOOTH_TRANSPORT_STATE_IDLE;
548     else if (pa_streq(value, "pending") || pa_streq(value, "active"))
549         *state = PA_BLUETOOTH_TRANSPORT_STATE_PLAYING;
550     else
551         return -1;
552
553     return 0;
554 }
555
556 static void parse_transport_property(pa_bluetooth_transport *t, DBusMessageIter *i) {
557     const char *key;
558     DBusMessageIter variant_i;
559
560     key = check_variant_property(i);
561     if (key == NULL)
562         return;
563
564     dbus_message_iter_recurse(i, &variant_i);
565
566     switch (dbus_message_iter_get_arg_type(&variant_i)) {
567
568         case DBUS_TYPE_STRING: {
569
570             const char *value;
571             dbus_message_iter_get_basic(&variant_i, &value);
572
573             if (pa_streq(key, "State")) {
574                 pa_bluetooth_transport_state_t state;
575
576                 if (transport_state_from_string(value, &state) < 0) {
577                     pa_log_error("Invalid state received: %s", value);
578                     return;
579                 }
580
581                 pa_bluetooth_transport_set_state(t, state);
582             }
583
584             break;
585         }
586     }
587
588     return;
589 }
590
591 static int parse_transport_properties(pa_bluetooth_transport *t, DBusMessageIter *i) {
592     DBusMessageIter element_i;
593
594     dbus_message_iter_recurse(i, &element_i);
595
596     while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
597         DBusMessageIter dict_i;
598
599         dbus_message_iter_recurse(&element_i, &dict_i);
600
601         parse_transport_property(t, &dict_i);
602
603         dbus_message_iter_next(&element_i);
604     }
605
606     return 0;
607 }
608
609 static pa_bluetooth_device* device_create(pa_bluetooth_discovery *y, const char *path) {
610     pa_bluetooth_device *d;
611
612     pa_assert(y);
613     pa_assert(path);
614
615     d = pa_xnew0(pa_bluetooth_device, 1);
616     d->discovery = y;
617     d->path = pa_xstrdup(path);
618     d->uuids = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, pa_xfree);
619
620     pa_hashmap_put(y->devices, d->path, d);
621
622     return d;
623 }
624
625 pa_bluetooth_device* pa_bluetooth_discovery_get_device_by_path(pa_bluetooth_discovery *y, const char *path) {
626     pa_bluetooth_device *d;
627
628     pa_assert(y);
629     pa_assert(PA_REFCNT_VALUE(y) > 0);
630     pa_assert(path);
631
632     if ((d = pa_hashmap_get(y->devices, path)) && d->valid)
633         return d;
634
635     return NULL;
636 }
637
638 pa_bluetooth_device* pa_bluetooth_discovery_get_device_by_address(pa_bluetooth_discovery *y, const char *remote, const char *local) {
639     pa_bluetooth_device *d;
640     void *state = NULL;
641
642     pa_assert(y);
643     pa_assert(PA_REFCNT_VALUE(y) > 0);
644     pa_assert(remote);
645     pa_assert(local);
646
647     while ((d = pa_hashmap_iterate(y->devices, &state, NULL)))
648         if (d->valid && pa_streq(d->address, remote) && pa_streq(d->adapter->address, local))
649             return d;
650
651     return NULL;
652 }
653
654 static void device_free(pa_bluetooth_device *d) {
655     unsigned i;
656
657     pa_assert(d);
658
659     device_stop_waiting_for_profiles(d);
660
661     for (i = 0; i < PA_BLUETOOTH_PROFILE_COUNT; i++) {
662         pa_bluetooth_transport *t;
663
664         if (!(t = d->transports[i]))
665             continue;
666
667         pa_bluetooth_transport_free(t);
668     }
669
670     if (d->uuids)
671         pa_hashmap_free(d->uuids);
672
673     pa_xfree(d->path);
674     pa_xfree(d->alias);
675     pa_xfree(d->address);
676     pa_xfree(d->adapter_path);
677     pa_xfree(d);
678 }
679
680 static void device_remove(pa_bluetooth_discovery *y, const char *path) {
681     pa_bluetooth_device *d;
682
683     if (!(d = pa_hashmap_remove(y->devices, path)))
684         pa_log_warn("Unknown device removed %s", path);
685     else {
686         pa_log_debug("Device %s removed", path);
687         device_free(d);
688     }
689 }
690
691 static void device_set_valid(pa_bluetooth_device *device, bool valid) {
692     bool old_any_connected;
693
694     pa_assert(device);
695
696     if (valid == device->valid)
697         return;
698
699     old_any_connected = pa_bluetooth_device_any_transport_connected(device);
700     device->valid = valid;
701
702     if (pa_bluetooth_device_any_transport_connected(device) != old_any_connected)
703         pa_hook_fire(&device->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED], device);
704 }
705
706 static void device_update_valid(pa_bluetooth_device *d) {
707     pa_assert(d);
708
709     if (!d->properties_received) {
710         pa_assert(!d->valid);
711         return;
712     }
713
714     /* Check if mandatory properties are set. */
715     if (!d->address || !d->adapter_path || !d->alias) {
716         device_set_valid(d, false);
717         return;
718     }
719
720     if (!d->adapter || !d->adapter->valid) {
721         device_set_valid(d, false);
722         return;
723     }
724
725     device_set_valid(d, true);
726 }
727
728 static void device_set_adapter(pa_bluetooth_device *device, pa_bluetooth_adapter *adapter) {
729     pa_assert(device);
730
731     if (adapter == device->adapter)
732         return;
733
734     device->adapter = adapter;
735
736     device_update_valid(device);
737 }
738
739 static pa_bluetooth_adapter* adapter_create(pa_bluetooth_discovery *y, const char *path) {
740     pa_bluetooth_adapter *a;
741
742     pa_assert(y);
743     pa_assert(path);
744
745     a = pa_xnew0(pa_bluetooth_adapter, 1);
746     a->discovery = y;
747     a->path = pa_xstrdup(path);
748
749     pa_hashmap_put(y->adapters, a->path, a);
750
751     return a;
752 }
753
754 static void adapter_free(pa_bluetooth_adapter *a) {
755     pa_bluetooth_device *d;
756     void *state;
757
758     pa_assert(a);
759     pa_assert(a->discovery);
760
761     PA_HASHMAP_FOREACH(d, a->discovery->devices, state)
762         if (d->adapter == a)
763             device_set_adapter(d, NULL);
764
765     pa_xfree(a->path);
766     pa_xfree(a->address);
767     pa_xfree(a);
768 }
769
770 static void adapter_remove(pa_bluetooth_discovery *y, const char *path) {
771     pa_bluetooth_adapter *a;
772
773     if (!(a = pa_hashmap_remove(y->adapters, path)))
774         pa_log_warn("Unknown adapter removed %s", path);
775     else {
776         pa_log_debug("Adapter %s removed", path);
777         adapter_free(a);
778     }
779 }
780
781 static void parse_device_property(pa_bluetooth_device *d, DBusMessageIter *i) {
782     const char *key;
783     DBusMessageIter variant_i;
784
785     pa_assert(d);
786
787     key = check_variant_property(i);
788     if (key == NULL) {
789         pa_log_error("Received invalid property for device %s", d->path);
790         return;
791     }
792
793     dbus_message_iter_recurse(i, &variant_i);
794
795     switch (dbus_message_iter_get_arg_type(&variant_i)) {
796
797         case DBUS_TYPE_STRING: {
798             const char *value;
799             dbus_message_iter_get_basic(&variant_i, &value);
800
801             if (pa_streq(key, "Alias")) {
802                 pa_xfree(d->alias);
803                 d->alias = pa_xstrdup(value);
804                 pa_log_debug("%s: %s", key, value);
805             } else if (pa_streq(key, "Address")) {
806                 if (d->properties_received) {
807                     pa_log_warn("Device property 'Address' expected to be constant but changed for %s, ignoring", d->path);
808                     return;
809                 }
810
811                 if (d->address) {
812                     pa_log_warn("Device %s: Received a duplicate 'Address' property, ignoring", d->path);
813                     return;
814                 }
815
816                 d->address = pa_xstrdup(value);
817                 pa_log_debug("%s: %s", key, value);
818             }
819
820             break;
821         }
822
823         case DBUS_TYPE_OBJECT_PATH: {
824             const char *value;
825             dbus_message_iter_get_basic(&variant_i, &value);
826
827             if (pa_streq(key, "Adapter")) {
828
829                 if (d->properties_received) {
830                     pa_log_warn("Device property 'Adapter' expected to be constant but changed for %s, ignoring", d->path);
831                     return;
832                 }
833
834                 if (d->adapter_path) {
835                     pa_log_warn("Device %s: Received a duplicate 'Adapter' property, ignoring", d->path);
836                     return;
837                 }
838
839                 d->adapter_path = pa_xstrdup(value);
840                 pa_log_debug("%s: %s", key, value);
841             }
842
843             break;
844         }
845
846         case DBUS_TYPE_UINT32: {
847             uint32_t value;
848             dbus_message_iter_get_basic(&variant_i, &value);
849
850             if (pa_streq(key, "Class")) {
851                 d->class_of_device = value;
852                 pa_log_debug("%s: %d", key, value);
853             }
854
855             break;
856         }
857
858         case DBUS_TYPE_ARRAY: {
859             DBusMessageIter ai;
860             dbus_message_iter_recurse(&variant_i, &ai);
861
862             if (dbus_message_iter_get_arg_type(&ai) == DBUS_TYPE_STRING && pa_streq(key, "UUIDs")) {
863                 /* bluetoothd never removes UUIDs from a device object so we
864                  * don't need to check for disappeared UUIDs here. */
865                 while (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_INVALID) {
866                     const char *value;
867                     char *uuid;
868
869                     dbus_message_iter_get_basic(&ai, &value);
870
871                     if (pa_hashmap_get(d->uuids, value)) {
872                         dbus_message_iter_next(&ai);
873                         continue;
874                     }
875
876                     uuid = pa_xstrdup(value);
877                     pa_hashmap_put(d->uuids, uuid, uuid);
878
879                     pa_log_debug("%s: %s", key, value);
880                     dbus_message_iter_next(&ai);
881                 }
882             }
883
884             break;
885         }
886     }
887 }
888
889 static void parse_device_properties(pa_bluetooth_device *d, DBusMessageIter *i) {
890     DBusMessageIter element_i;
891
892     dbus_message_iter_recurse(i, &element_i);
893
894     while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
895         DBusMessageIter dict_i;
896
897         dbus_message_iter_recurse(&element_i, &dict_i);
898         parse_device_property(d, &dict_i);
899         dbus_message_iter_next(&element_i);
900     }
901
902     if (!d->properties_received) {
903         d->properties_received = true;
904         device_update_valid(d);
905
906         if (!d->address || !d->adapter_path || !d->alias)
907             pa_log_error("Non-optional information missing for device %s", d->path);
908     }
909 }
910
911 static void parse_adapter_properties(pa_bluetooth_adapter *a, DBusMessageIter *i, bool is_property_change) {
912     DBusMessageIter element_i;
913
914     pa_assert(a);
915
916     dbus_message_iter_recurse(i, &element_i);
917
918     while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
919         DBusMessageIter dict_i, variant_i;
920         const char *key;
921
922         dbus_message_iter_recurse(&element_i, &dict_i);
923
924         key = check_variant_property(&dict_i);
925         if (key == NULL) {
926             pa_log_error("Received invalid property for adapter %s", a->path);
927             return;
928         }
929
930         dbus_message_iter_recurse(&dict_i, &variant_i);
931
932         if (dbus_message_iter_get_arg_type(&variant_i) == DBUS_TYPE_STRING && pa_streq(key, "Address")) {
933             const char *value;
934
935             if (is_property_change) {
936                 pa_log_warn("Adapter property 'Address' expected to be constant but changed for %s, ignoring", a->path);
937                 return;
938             }
939
940             if (a->address) {
941                 pa_log_warn("Adapter %s received a duplicate 'Address' property, ignoring", a->path);
942                 return;
943             }
944
945             dbus_message_iter_get_basic(&variant_i, &value);
946             a->address = pa_xstrdup(value);
947             a->valid = true;
948         }
949
950         dbus_message_iter_next(&element_i);
951     }
952 }
953
954 static void register_endpoint_reply(DBusPendingCall *pending, void *userdata) {
955     DBusMessage *r;
956     pa_dbus_pending *p;
957     pa_bluetooth_discovery *y;
958     char *endpoint;
959
960     pa_assert(pending);
961     pa_assert_se(p = userdata);
962     pa_assert_se(y = p->context_data);
963     pa_assert_se(endpoint = p->call_data);
964     pa_assert_se(r = dbus_pending_call_steal_reply(pending));
965
966     if (dbus_message_is_error(r, BLUEZ_ERROR_NOT_SUPPORTED)) {
967         pa_log_info("Couldn't register endpoint %s because it is disabled in BlueZ", endpoint);
968         goto finish;
969     }
970
971     if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
972         pa_log_error(BLUEZ_MEDIA_INTERFACE ".RegisterEndpoint() failed: %s: %s", dbus_message_get_error_name(r),
973                      pa_dbus_get_error_message(r));
974         goto finish;
975     }
976
977 finish:
978     dbus_message_unref(r);
979
980     PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
981     pa_dbus_pending_free(p);
982
983     pa_xfree(endpoint);
984 }
985
986 static void register_endpoint(pa_bluetooth_discovery *y, const char *path, const char *endpoint, const char *uuid) {
987     DBusMessage *m;
988     DBusMessageIter i, d;
989     uint8_t codec = 0;
990
991 #ifdef BLUETOOTH_APTX_SUPPORT
992     if(pa_streq(endpoint,A2DP_APTX_SOURCE_ENDPOINT))
993         codec = A2DP_CODEC_VENDOR;
994 #endif
995     pa_log_debug("Registering %s on adapter %s", endpoint, path);
996
997     pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, path, BLUEZ_MEDIA_INTERFACE, "RegisterEndpoint"));
998
999     dbus_message_iter_init_append(m, &i);
1000     pa_assert_se(dbus_message_iter_append_basic(&i, DBUS_TYPE_OBJECT_PATH, &endpoint));
1001     dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING
1002                                          DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &d);
1003     pa_dbus_append_basic_variant_dict_entry(&d, "UUID", DBUS_TYPE_STRING, &uuid);
1004     pa_dbus_append_basic_variant_dict_entry(&d, "Codec", DBUS_TYPE_BYTE, &codec);
1005
1006     if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SOURCE) || pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK)) {
1007 #ifdef __TIZEN_BT__
1008     if (codec == A2DP_CODEC_SBC) {
1009         a2dp_sbc_t capabilities;
1010         capabilities.channel_mode = SBC_CHANNEL_MODE_MONO | SBC_CHANNEL_MODE_DUAL_CHANNEL | SBC_CHANNEL_MODE_STEREO |
1011                                     SBC_CHANNEL_MODE_JOINT_STEREO;
1012         capabilities.frequency = SBC_SAMPLING_FREQ_16000 | SBC_SAMPLING_FREQ_32000 | SBC_SAMPLING_FREQ_44100 |
1013                                  SBC_SAMPLING_FREQ_48000;
1014         capabilities.allocation_method = SBC_ALLOCATION_SNR | SBC_ALLOCATION_LOUDNESS;
1015         capabilities.subbands = SBC_SUBBANDS_4 | SBC_SUBBANDS_8;
1016         capabilities.block_length = SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 | SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16;
1017         capabilities.min_bitpool = MIN_BITPOOL;
1018         capabilities.max_bitpool = MAX_BITPOOL;
1019         pa_dbus_append_basic_array_variant_dict_entry(&d, "Capabilities", DBUS_TYPE_BYTE, &capabilities, sizeof(capabilities));
1020     }
1021 #ifdef BLUETOOTH_APTX_SUPPORT
1022     if (codec == A2DP_CODEC_VENDOR ) {
1023         /* aptx */
1024         a2dp_aptx_t capabilities;
1025         capabilities.vendor_id[0] = APTX_VENDOR_ID0;
1026         capabilities.vendor_id[1] = APTX_VENDOR_ID1;
1027         capabilities.vendor_id[2] = APTX_VENDOR_ID2;
1028         capabilities.vendor_id[3] = APTX_VENDOR_ID3;
1029         capabilities.codec_id[0] = APTX_CODEC_ID0;
1030         capabilities.codec_id[1] = APTX_CODEC_ID1;
1031         capabilities.channel_mode= APTX_CHANNEL_MODE_STEREO;
1032         capabilities.frequency= APTX_SAMPLING_FREQ_44100;
1033         pa_dbus_append_basic_array_variant_dict_entry(&d, "Capabilities", DBUS_TYPE_BYTE, &capabilities, sizeof(capabilities));
1034     }
1035 #endif /* BLUETOOTH_APTX_SUPPORT */
1036 #else
1037         a2dp_sbc_t capabilities;
1038         capabilities.channel_mode = SBC_CHANNEL_MODE_MONO | SBC_CHANNEL_MODE_DUAL_CHANNEL | SBC_CHANNEL_MODE_STEREO |
1039                                     SBC_CHANNEL_MODE_JOINT_STEREO;
1040         capabilities.frequency = SBC_SAMPLING_FREQ_16000 | SBC_SAMPLING_FREQ_32000 | SBC_SAMPLING_FREQ_44100 |
1041                                  SBC_SAMPLING_FREQ_48000;
1042         capabilities.allocation_method = SBC_ALLOCATION_SNR | SBC_ALLOCATION_LOUDNESS;
1043         capabilities.subbands = SBC_SUBBANDS_4 | SBC_SUBBANDS_8;
1044         capabilities.block_length = SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 | SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16;
1045         capabilities.min_bitpool = MIN_BITPOOL;
1046         capabilities.max_bitpool = MAX_BITPOOL;
1047         pa_dbus_append_basic_array_variant_dict_entry(&d, "Capabilities", DBUS_TYPE_BYTE, &capabilities, sizeof(capabilities));
1048 #endif /* __TIZEN_BT__ */
1049     }
1050
1051     dbus_message_iter_close_container(&i, &d);
1052
1053     send_and_add_to_pending(y, m, register_endpoint_reply, pa_xstrdup(endpoint));
1054 }
1055
1056 static void parse_interfaces_and_properties(pa_bluetooth_discovery *y, DBusMessageIter *dict_i) {
1057     DBusMessageIter element_i;
1058     const char *path;
1059     void *state;
1060     pa_bluetooth_device *d;
1061
1062     pa_assert(dbus_message_iter_get_arg_type(dict_i) == DBUS_TYPE_OBJECT_PATH);
1063     dbus_message_iter_get_basic(dict_i, &path);
1064
1065     pa_assert_se(dbus_message_iter_next(dict_i));
1066     pa_assert(dbus_message_iter_get_arg_type(dict_i) == DBUS_TYPE_ARRAY);
1067
1068     dbus_message_iter_recurse(dict_i, &element_i);
1069
1070     while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
1071         DBusMessageIter iface_i;
1072         const char *interface;
1073
1074         dbus_message_iter_recurse(&element_i, &iface_i);
1075
1076         pa_assert(dbus_message_iter_get_arg_type(&iface_i) == DBUS_TYPE_STRING);
1077         dbus_message_iter_get_basic(&iface_i, &interface);
1078
1079         pa_assert_se(dbus_message_iter_next(&iface_i));
1080         pa_assert(dbus_message_iter_get_arg_type(&iface_i) == DBUS_TYPE_ARRAY);
1081
1082         if (pa_streq(interface, BLUEZ_ADAPTER_INTERFACE)) {
1083             pa_bluetooth_adapter *a;
1084
1085             if ((a = pa_hashmap_get(y->adapters, path))) {
1086                 pa_log_error("Found duplicated D-Bus path for adapter %s", path);
1087                 return;
1088             } else
1089                 a = adapter_create(y, path);
1090
1091             pa_log_debug("Adapter %s found", path);
1092
1093             parse_adapter_properties(a, &iface_i, false);
1094
1095             if (!a->valid)
1096                 return;
1097
1098             register_endpoint(y, path, A2DP_SOURCE_ENDPOINT, PA_BLUETOOTH_UUID_A2DP_SOURCE);
1099             register_endpoint(y, path, A2DP_SINK_ENDPOINT, PA_BLUETOOTH_UUID_A2DP_SINK);
1100 #ifdef TIZEN_BT_A2DP_MULTISTREAM
1101             register_endpoint(y, path, A2DP_SINK_ENDPOINT2, PA_BLUETOOTH_UUID_A2DP_SINK);
1102 #endif
1103
1104 #ifdef BLUETOOTH_APTX_SUPPORT
1105             if (aptx_handle)
1106                 register_endpoint(y, path, A2DP_APTX_SOURCE_ENDPOINT, PA_BLUETOOTH_UUID_A2DP_SOURCE);
1107 #endif
1108         } else if (pa_streq(interface, BLUEZ_DEVICE_INTERFACE)) {
1109
1110             if ((d = pa_hashmap_get(y->devices, path))) {
1111                 if (d->properties_received) {
1112                     pa_log_error("Found duplicated D-Bus path for device %s", path);
1113                     return;
1114                 }
1115             } else
1116                 d = device_create(y, path);
1117
1118             pa_log_debug("Device %s found", d->path);
1119
1120             parse_device_properties(d, &iface_i);
1121
1122         } else
1123             pa_log_debug("Unknown interface %s found, skipping", interface);
1124
1125         dbus_message_iter_next(&element_i);
1126     }
1127
1128     PA_HASHMAP_FOREACH(d, y->devices, state) {
1129         if (d->properties_received && !d->tried_to_link_with_adapter) {
1130             if (d->adapter_path) {
1131                 device_set_adapter(d, pa_hashmap_get(d->discovery->adapters, d->adapter_path));
1132
1133                 if (!d->adapter)
1134                     pa_log("Device %s points to a nonexistent adapter %s.", d->path, d->adapter_path);
1135                 else if (!d->adapter->valid)
1136                     pa_log("Device %s points to an invalid adapter %s.", d->path, d->adapter_path);
1137             }
1138
1139             d->tried_to_link_with_adapter = true;
1140         }
1141     }
1142
1143     return;
1144 }
1145
1146 void pa_bluetooth_discovery_set_ofono_running(pa_bluetooth_discovery *y, bool is_running) {
1147     pa_assert(y);
1148
1149     pa_log_debug("oFono is running: %s", pa_yes_no(is_running));
1150     if (y->headset_backend != HEADSET_BACKEND_AUTO)
1151         return;
1152
1153     /* If ofono starts running, all devices that might be connected to the HS role
1154      * need to be disconnected, so that the devices can be handled by ofono */
1155     if (is_running) {
1156         void *state;
1157         pa_bluetooth_device *d;
1158
1159         PA_HASHMAP_FOREACH(d, y->devices, state) {
1160             if (device_supports_profile(d, PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY)) {
1161                 DBusMessage *m;
1162
1163                 pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, d->path, "org.bluez.Device1", "Disconnect"));
1164                 dbus_message_set_no_reply(m, true);
1165                 dbus_connection_send(pa_dbus_connection_get(y->connection), m, NULL);
1166                 dbus_message_unref(m);
1167             }
1168         }
1169     }
1170
1171     pa_bluetooth_native_backend_enable_hs_role(y->native_backend, !is_running);
1172 }
1173
1174 static void get_managed_objects_reply(DBusPendingCall *pending, void *userdata) {
1175     pa_dbus_pending *p;
1176     pa_bluetooth_discovery *y;
1177     DBusMessage *r;
1178     DBusMessageIter arg_i, element_i;
1179
1180     pa_assert_se(p = userdata);
1181     pa_assert_se(y = p->context_data);
1182     pa_assert_se(r = dbus_pending_call_steal_reply(pending));
1183
1184     if (dbus_message_is_error(r, DBUS_ERROR_UNKNOWN_METHOD)) {
1185         pa_log_warn("BlueZ D-Bus ObjectManager not available");
1186         goto finish;
1187     }
1188
1189     if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
1190         pa_log_error("GetManagedObjects() failed: %s: %s", dbus_message_get_error_name(r), pa_dbus_get_error_message(r));
1191         goto finish;
1192     }
1193
1194     if (!dbus_message_iter_init(r, &arg_i) || !pa_streq(dbus_message_get_signature(r), "a{oa{sa{sv}}}")) {
1195         pa_log_error("Invalid reply signature for GetManagedObjects()");
1196         goto finish;
1197     }
1198
1199     dbus_message_iter_recurse(&arg_i, &element_i);
1200     while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
1201         DBusMessageIter dict_i;
1202
1203         dbus_message_iter_recurse(&element_i, &dict_i);
1204
1205         parse_interfaces_and_properties(y, &dict_i);
1206
1207         dbus_message_iter_next(&element_i);
1208     }
1209
1210     y->objects_listed = true;
1211
1212     if (!y->native_backend && y->headset_backend != HEADSET_BACKEND_OFONO)
1213         y->native_backend = pa_bluetooth_native_backend_new(y->core, y, (y->headset_backend == HEADSET_BACKEND_NATIVE));
1214     if (!y->ofono_backend && y->headset_backend != HEADSET_BACKEND_NATIVE)
1215         y->ofono_backend = pa_bluetooth_ofono_backend_new(y->core, y);
1216
1217 finish:
1218     dbus_message_unref(r);
1219
1220     PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
1221     pa_dbus_pending_free(p);
1222 }
1223
1224 static void get_managed_objects(pa_bluetooth_discovery *y) {
1225     DBusMessage *m;
1226
1227     pa_assert(y);
1228
1229     pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, "/", "org.freedesktop.DBus.ObjectManager",
1230                                                   "GetManagedObjects"));
1231     send_and_add_to_pending(y, m, get_managed_objects_reply, NULL);
1232 }
1233
1234 pa_hook* pa_bluetooth_discovery_hook(pa_bluetooth_discovery *y, pa_bluetooth_hook_t hook) {
1235     pa_assert(y);
1236     pa_assert(PA_REFCNT_VALUE(y) > 0);
1237
1238     return &y->hooks[hook];
1239 }
1240
1241 static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *userdata) {
1242     pa_bluetooth_discovery *y;
1243     DBusError err;
1244
1245     pa_assert(bus);
1246     pa_assert(m);
1247     pa_assert_se(y = userdata);
1248
1249     dbus_error_init(&err);
1250
1251     if (dbus_message_is_signal(m, "org.freedesktop.DBus", "NameOwnerChanged")) {
1252         const char *name, *old_owner, *new_owner;
1253
1254         if (!dbus_message_get_args(m, &err,
1255                                    DBUS_TYPE_STRING, &name,
1256                                    DBUS_TYPE_STRING, &old_owner,
1257                                    DBUS_TYPE_STRING, &new_owner,
1258                                    DBUS_TYPE_INVALID)) {
1259             pa_log_error("Failed to parse org.freedesktop.DBus.NameOwnerChanged: %s", err.message);
1260             goto fail;
1261         }
1262
1263         if (pa_streq(name, BLUEZ_SERVICE)) {
1264             if (old_owner && *old_owner) {
1265                 pa_log_debug("Bluetooth daemon disappeared");
1266                 pa_hashmap_remove_all(y->devices);
1267                 pa_hashmap_remove_all(y->adapters);
1268                 y->objects_listed = false;
1269                 if (y->ofono_backend) {
1270                     pa_bluetooth_ofono_backend_free(y->ofono_backend);
1271                     y->ofono_backend = NULL;
1272                 }
1273                 if (y->native_backend) {
1274                     pa_bluetooth_native_backend_free(y->native_backend);
1275                     y->native_backend = NULL;
1276                 }
1277             }
1278
1279             if (new_owner && *new_owner) {
1280                 pa_log_debug("Bluetooth daemon appeared");
1281                 get_managed_objects(y);
1282             }
1283         }
1284
1285         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1286     } else if (dbus_message_is_signal(m, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded")) {
1287         DBusMessageIter arg_i;
1288 #ifndef __TIZEN_BT__
1289         if (!y->objects_listed)
1290             return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; /* No reply received yet from GetManagedObjects */
1291 #endif
1292         if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "oa{sa{sv}}")) {
1293             pa_log_error("Invalid signature found in InterfacesAdded");
1294             goto fail;
1295         }
1296
1297         parse_interfaces_and_properties(y, &arg_i);
1298
1299         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1300     } else if (dbus_message_is_signal(m, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved")) {
1301         const char *p;
1302         DBusMessageIter arg_i;
1303         DBusMessageIter element_i;
1304
1305         if (!y->objects_listed)
1306             return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; /* No reply received yet from GetManagedObjects */
1307
1308         if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "oas")) {
1309             pa_log_error("Invalid signature found in InterfacesRemoved");
1310             goto fail;
1311         }
1312
1313         dbus_message_iter_get_basic(&arg_i, &p);
1314
1315         pa_assert_se(dbus_message_iter_next(&arg_i));
1316         pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_ARRAY);
1317
1318         dbus_message_iter_recurse(&arg_i, &element_i);
1319
1320         while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_STRING) {
1321             const char *iface;
1322
1323             dbus_message_iter_get_basic(&element_i, &iface);
1324
1325             if (pa_streq(iface, BLUEZ_DEVICE_INTERFACE))
1326                 device_remove(y, p);
1327             else if (pa_streq(iface, BLUEZ_ADAPTER_INTERFACE))
1328                 adapter_remove(y, p);
1329
1330             dbus_message_iter_next(&element_i);
1331         }
1332
1333         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1334
1335     } else if (dbus_message_is_signal(m, "org.freedesktop.DBus.Properties", "PropertiesChanged")) {
1336         DBusMessageIter arg_i;
1337         const char *iface;
1338
1339 #ifndef __TIZEN_BT__
1340         if (!y->objects_listed)
1341             return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; /* No reply received yet from GetManagedObjects */
1342 #endif
1343
1344         if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "sa{sv}as")) {
1345             pa_log_error("Invalid signature found in PropertiesChanged");
1346             goto fail;
1347         }
1348
1349         dbus_message_iter_get_basic(&arg_i, &iface);
1350
1351         pa_assert_se(dbus_message_iter_next(&arg_i));
1352         pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_ARRAY);
1353
1354         if (pa_streq(iface, BLUEZ_ADAPTER_INTERFACE)) {
1355             pa_bluetooth_adapter *a;
1356
1357             pa_log_debug("Properties changed in adapter %s", dbus_message_get_path(m));
1358
1359             if (!(a = pa_hashmap_get(y->adapters, dbus_message_get_path(m)))) {
1360                 pa_log_warn("Properties changed in unknown adapter");
1361                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1362             }
1363
1364             parse_adapter_properties(a, &arg_i, true);
1365
1366         } else if (pa_streq(iface, BLUEZ_DEVICE_INTERFACE)) {
1367             pa_bluetooth_device *d;
1368
1369             pa_log_debug("Properties changed in device %s", dbus_message_get_path(m));
1370
1371             if (!(d = pa_hashmap_get(y->devices, dbus_message_get_path(m)))) {
1372                 pa_log_warn("Properties changed in unknown device");
1373                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1374             }
1375
1376             if (!d->properties_received)
1377                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1378
1379             parse_device_properties(d, &arg_i);
1380         } else if (pa_streq(iface, BLUEZ_MEDIA_TRANSPORT_INTERFACE)) {
1381             pa_bluetooth_transport *t;
1382
1383             pa_log_debug("Properties changed in transport %s", dbus_message_get_path(m));
1384
1385             if (!(t = pa_hashmap_get(y->transports, dbus_message_get_path(m))))
1386                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1387
1388             parse_transport_properties(t, &arg_i);
1389         }
1390
1391         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1392 #ifdef __TIZEN_BT__
1393     } else if (dbus_message_is_signal(m, "org.bluez.ag_agent", "SuspendMedia")) {
1394         pa_bluetooth_transport *t;
1395
1396         pa_log_debug("Signal from ag-agent to Suspend Media");
1397
1398         if (!(t = pa_hashmap_first(y->transports)))
1399             return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1400
1401         pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_SCO_STATE_CHANGED], t);
1402
1403         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1404 #endif
1405 #ifdef TIZEN_BT_AVC_TARGET
1406     } else if (dbus_message_is_signal(m, "org.projectx.bt_event", "AvcModeChanged")) {
1407         pa_bluetooth_transport *t;
1408         dbus_uint32_t mode = PA_BLUETOOTH_AVC_OFF;
1409
1410         if (!dbus_message_get_args(m, &err,
1411             DBUS_TYPE_UINT32, &mode,
1412             DBUS_TYPE_INVALID)) {
1413             pa_log_error("Failed to parse org.projectx.bt_event.AvcModeChanged: %s", err.message);
1414             goto fail;
1415         }
1416
1417         if (!(t = pa_hashmap_first(y->transports)))
1418             return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1419
1420         pa_log_debug("Previous mode [%d], New mode [%d]", t->avc_mode, mode);
1421
1422         t->avc_mode = mode;
1423
1424         pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_AVC_MODE_CHANGED], t);
1425
1426         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1427 #endif
1428     }
1429
1430 fail:
1431     dbus_error_free(&err);
1432
1433     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1434 }
1435
1436 static uint8_t a2dp_default_bitpool(uint8_t freq, uint8_t mode) {
1437     /* These bitpool values were chosen based on the A2DP spec recommendation */
1438     switch (freq) {
1439         case SBC_SAMPLING_FREQ_16000:
1440         case SBC_SAMPLING_FREQ_32000:
1441             return 53;
1442
1443         case SBC_SAMPLING_FREQ_44100:
1444
1445             switch (mode) {
1446                 case SBC_CHANNEL_MODE_MONO:
1447                 case SBC_CHANNEL_MODE_DUAL_CHANNEL:
1448                     return 31;
1449
1450                 case SBC_CHANNEL_MODE_STEREO:
1451                 case SBC_CHANNEL_MODE_JOINT_STEREO:
1452 #if defined(__TIZEN_BT__) && defined(ADJUST_ANDROID_BITPOOL)
1453                     return 35;
1454 #else
1455                     return 53;
1456 #endif
1457             }
1458
1459             pa_log_warn("Invalid channel mode %u", mode);
1460             return 53;
1461
1462         case SBC_SAMPLING_FREQ_48000:
1463
1464             switch (mode) {
1465                 case SBC_CHANNEL_MODE_MONO:
1466                 case SBC_CHANNEL_MODE_DUAL_CHANNEL:
1467                     return 29;
1468
1469                 case SBC_CHANNEL_MODE_STEREO:
1470                 case SBC_CHANNEL_MODE_JOINT_STEREO:
1471                     return 51;
1472             }
1473
1474             pa_log_warn("Invalid channel mode %u", mode);
1475             return 51;
1476     }
1477
1478     pa_log_warn("Invalid sampling freq %u", freq);
1479     return 53;
1480 }
1481
1482 #ifdef BLUETOOTH_APTX_SUPPORT
1483 static DBusMessage *endpoint_select_configuration_for_aptx(DBusConnection *c, DBusMessage *m, void *userdata) {
1484     a2dp_aptx_t *cap;
1485     a2dp_aptx_t config;
1486     uint8_t *pconf = (uint8_t *) &config;
1487     int size;
1488     DBusMessage *r;
1489     DBusError e;
1490
1491     dbus_error_init(&e);
1492
1493     if (!dbus_message_get_args(m, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &cap, &size, DBUS_TYPE_INVALID)) {
1494         pa_log("org.bluez.MediaEndpoint.SelectConfiguration: %s", e.message);
1495         dbus_error_free(&e);
1496         goto fail;
1497     }
1498
1499     pa_assert(size == sizeof(config));
1500
1501     memset(&config, 0, sizeof(config));
1502
1503     if (cap->vendor_id[0] == APTX_VENDOR_ID0 &&
1504         cap->vendor_id[1] == APTX_VENDOR_ID1 &&
1505         cap->vendor_id[2] == APTX_VENDOR_ID2 &&
1506         cap->vendor_id[3] == APTX_VENDOR_ID3 &&
1507          cap->codec_id[0] == APTX_CODEC_ID0  &&
1508          cap->codec_id[1] == APTX_CODEC_ID1  )
1509         pa_log_debug("A2DP_CODEC_NON_A2DP and this is APTX Codec");
1510     else {
1511         pa_log_debug("A2DP_CODEC_NON_A2DP but this is not APTX Codec");
1512         goto fail;
1513     }
1514
1515     memcpy(&config,cap, sizeof(config));
1516
1517 /* The below code shuld be re-written by aptx */
1518 /* And we should configure pulseaudio freq */
1519
1520     if (cap->frequency & APTX_SAMPLING_FREQ_44100)
1521         config.frequency = APTX_SAMPLING_FREQ_44100;
1522     else if (cap->frequency & APTX_SAMPLING_FREQ_48000)
1523         config.frequency = APTX_SAMPLING_FREQ_48000;
1524     else if (cap->frequency & APTX_SAMPLING_FREQ_32000)
1525         config.frequency = APTX_SAMPLING_FREQ_32000;
1526     else if (cap->frequency & APTX_SAMPLING_FREQ_16000)
1527         config.frequency = APTX_SAMPLING_FREQ_16000;
1528     else {
1529         pa_log_error("No aptx supported frequencies");
1530         goto fail;
1531     }
1532
1533     if (cap->channel_mode & APTX_CHANNEL_MODE_JOINT_STEREO)
1534         config.channel_mode = APTX_CHANNEL_MODE_STEREO;
1535     else if (cap->channel_mode & APTX_CHANNEL_MODE_STEREO)
1536         config.channel_mode = APTX_CHANNEL_MODE_STEREO;
1537     else if (cap->channel_mode & APTX_CHANNEL_MODE_DUAL_CHANNEL)
1538         config.channel_mode = APTX_CHANNEL_MODE_STEREO;
1539     else {
1540         pa_log_error("No aptx supported channel modes");
1541         goto fail;
1542     }
1543
1544     pa_assert_se(r = dbus_message_new_method_return(m));
1545
1546     pa_assert_se(dbus_message_append_args(
1547                                      r,
1548                                      DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &pconf, size,
1549                                      DBUS_TYPE_INVALID));
1550
1551     return r;
1552
1553 fail:
1554     pa_assert_se(r = (dbus_message_new_error(m, "org.bluez.MediaEndpoint.Error.InvalidArguments",
1555                                                         "Unable to select configuration")));
1556     return r;
1557 }
1558 #endif
1559
1560 const char *pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile) {
1561     switch(profile) {
1562         case PA_BLUETOOTH_PROFILE_A2DP_SINK:
1563             return "a2dp_sink";
1564         case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
1565             return "a2dp_source";
1566         case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT:
1567             return "headset_head_unit";
1568         case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY:
1569             return "headset_audio_gateway";
1570         case PA_BLUETOOTH_PROFILE_OFF:
1571             return "off";
1572     }
1573
1574     return NULL;
1575 }
1576
1577 static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) {
1578     pa_bluetooth_discovery *y = userdata;
1579     pa_bluetooth_device *d;
1580     pa_bluetooth_transport *t;
1581     const char *sender, *path, *endpoint_path, *dev_path = NULL, *uuid = NULL;
1582 #ifdef __TIZEN_BT__
1583     uint8_t codec = 0;
1584 #endif
1585     const uint8_t *config = NULL;
1586     int size = 0;
1587     pa_bluetooth_profile_t p = PA_BLUETOOTH_PROFILE_OFF;
1588     DBusMessageIter args, props;
1589     DBusMessage *r;
1590
1591     if (!dbus_message_iter_init(m, &args) || !pa_streq(dbus_message_get_signature(m), "oa{sv}")) {
1592         pa_log_error("Invalid signature for method SetConfiguration()");
1593         goto fail2;
1594     }
1595
1596     dbus_message_iter_get_basic(&args, &path);
1597
1598     if (pa_hashmap_get(y->transports, path)) {
1599         pa_log_error("Endpoint SetConfiguration(): Transport %s is already configured.", path);
1600         goto fail2;
1601     }
1602
1603     pa_assert_se(dbus_message_iter_next(&args));
1604
1605     dbus_message_iter_recurse(&args, &props);
1606     if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY)
1607         goto fail;
1608
1609     /* Read transport properties */
1610     while (dbus_message_iter_get_arg_type(&props) == DBUS_TYPE_DICT_ENTRY) {
1611         const char *key;
1612         DBusMessageIter value, entry;
1613         int var;
1614
1615         dbus_message_iter_recurse(&props, &entry);
1616         dbus_message_iter_get_basic(&entry, &key);
1617
1618         dbus_message_iter_next(&entry);
1619         dbus_message_iter_recurse(&entry, &value);
1620
1621         var = dbus_message_iter_get_arg_type(&value);
1622
1623         if (pa_streq(key, "UUID")) {
1624             if (var != DBUS_TYPE_STRING) {
1625                 pa_log_error("Property %s of wrong type %c", key, (char)var);
1626                 goto fail;
1627             }
1628
1629             dbus_message_iter_get_basic(&value, &uuid);
1630
1631             endpoint_path = dbus_message_get_path(m);
1632 #ifdef __TIZEN_BT__
1633             pa_assert_se(endpoint_path);
1634 #endif
1635
1636 #ifdef BLUETOOTH_APTX_SUPPORT
1637             if (pa_streq(endpoint_path, A2DP_SOURCE_ENDPOINT) ||
1638                 pa_streq(endpoint_path, A2DP_APTX_SOURCE_ENDPOINT)) {
1639                 if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SOURCE))
1640                     p = PA_BLUETOOTH_PROFILE_A2DP_SINK;
1641             }
1642 #else
1643             if (pa_streq(endpoint_path, A2DP_SOURCE_ENDPOINT)) {
1644                 if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SOURCE))
1645                     p = PA_BLUETOOTH_PROFILE_A2DP_SINK;
1646             }
1647 #endif
1648             else if (pa_streq(endpoint_path, A2DP_SINK_ENDPOINT)) {
1649                 if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK))
1650                     p = PA_BLUETOOTH_PROFILE_A2DP_SOURCE;
1651             }
1652 #ifdef TIZEN_BT_A2DP_MULTISTREAM
1653             else if (pa_streq(endpoint_path, A2DP_SINK_ENDPOINT2)) {
1654                 if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK))
1655                     p = PA_BLUETOOTH_PROFILE_A2DP_SOURCE;
1656             }
1657 #endif
1658
1659             if (p == PA_BLUETOOTH_PROFILE_OFF) {
1660                 pa_log_error("UUID %s of transport %s incompatible with endpoint %s", uuid, path, endpoint_path);
1661                 goto fail;
1662             }
1663 #ifdef __TIZEN_BT__
1664         } else if (pa_streq(key, "Codec")) {
1665             if (var != DBUS_TYPE_BYTE) {
1666                 pa_log_error("Property %s of wrong type %c", key, (char)var);
1667                 goto fail;
1668             }
1669
1670             dbus_message_iter_get_basic(&value, &codec);
1671 #endif
1672         } else if (pa_streq(key, "Device")) {
1673             if (var != DBUS_TYPE_OBJECT_PATH) {
1674                 pa_log_error("Property %s of wrong type %c", key, (char)var);
1675                 goto fail;
1676             }
1677
1678             dbus_message_iter_get_basic(&value, &dev_path);
1679         } else if (pa_streq(key, "Configuration")) {
1680             DBusMessageIter array;
1681 #ifndef BLUETOOTH_APTX_SUPPORT
1682             a2dp_sbc_t *c;
1683 #endif
1684
1685             if (var != DBUS_TYPE_ARRAY) {
1686                 pa_log_error("Property %s of wrong type %c", key, (char)var);
1687                 goto fail;
1688             }
1689
1690             dbus_message_iter_recurse(&value, &array);
1691             var = dbus_message_iter_get_arg_type(&array);
1692             if (var != DBUS_TYPE_BYTE) {
1693                 pa_log_error("%s is an array of wrong type %c", key, (char)var);
1694                 goto fail;
1695             }
1696
1697             dbus_message_iter_get_fixed_array(&array, &config, &size);
1698 #ifndef BLUETOOTH_APTX_SUPPORT
1699             if (size != sizeof(a2dp_sbc_t)) {
1700                 pa_log_error("Configuration array of invalid size");
1701                 goto fail;
1702             }
1703
1704             c = (a2dp_sbc_t *) config;
1705
1706             if (c->frequency != SBC_SAMPLING_FREQ_16000 && c->frequency != SBC_SAMPLING_FREQ_32000 &&
1707                 c->frequency != SBC_SAMPLING_FREQ_44100 && c->frequency != SBC_SAMPLING_FREQ_48000) {
1708                 pa_log_error("Invalid sampling frequency in configuration");
1709                 goto fail;
1710             }
1711
1712             if (c->channel_mode != SBC_CHANNEL_MODE_MONO && c->channel_mode != SBC_CHANNEL_MODE_DUAL_CHANNEL &&
1713                 c->channel_mode != SBC_CHANNEL_MODE_STEREO && c->channel_mode != SBC_CHANNEL_MODE_JOINT_STEREO) {
1714                 pa_log_error("Invalid channel mode in configuration");
1715                 goto fail;
1716             }
1717
1718             if (c->allocation_method != SBC_ALLOCATION_SNR && c->allocation_method != SBC_ALLOCATION_LOUDNESS) {
1719                 pa_log_error("Invalid allocation method in configuration");
1720                 goto fail;
1721             }
1722
1723             if (c->subbands != SBC_SUBBANDS_4 && c->subbands != SBC_SUBBANDS_8) {
1724                 pa_log_error("Invalid SBC subbands in configuration");
1725                 goto fail;
1726             }
1727
1728             if (c->block_length != SBC_BLOCK_LENGTH_4 && c->block_length != SBC_BLOCK_LENGTH_8 &&
1729                 c->block_length != SBC_BLOCK_LENGTH_12 && c->block_length != SBC_BLOCK_LENGTH_16) {
1730                 pa_log_error("Invalid block length in configuration");
1731                 goto fail;
1732             }
1733 #endif
1734         }
1735
1736         dbus_message_iter_next(&props);
1737     }
1738
1739     if ((d = pa_hashmap_get(y->devices, dev_path))) {
1740         if (!d->valid) {
1741             pa_log_error("Information about device %s is invalid", dev_path);
1742             goto fail2;
1743         }
1744     } else {
1745         /* InterfacesAdded signal is probably on its way, device_info_valid is kept as 0. */
1746         pa_log_warn("SetConfiguration() received for unknown device %s", dev_path);
1747         d = device_create(y, dev_path);
1748     }
1749
1750     if (d->transports[p] != NULL) {
1751         pa_log_error("Cannot configure transport %s because profile %s is already used", path, pa_bluetooth_profile_to_string(p));
1752         goto fail2;
1753     }
1754
1755     sender = dbus_message_get_sender(m);
1756
1757     pa_assert_se(r = dbus_message_new_method_return(m));
1758     pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), r, NULL));
1759     dbus_message_unref(r);
1760
1761     t = pa_bluetooth_transport_new(d, sender, path, p, config, size);
1762 #ifdef __TIZEN_BT__
1763     t->codec = codec;
1764     d->transports[p] = t;
1765 #endif
1766     t->acquire = bluez5_transport_acquire_cb;
1767     t->release = bluez5_transport_release_cb;
1768     pa_bluetooth_transport_put(t);
1769
1770     pa_log_debug("Transport %s available for profile %s", t->path, pa_bluetooth_profile_to_string(t->profile));
1771
1772     return NULL;
1773
1774 fail:
1775     pa_log_error("Endpoint SetConfiguration(): invalid arguments");
1776
1777 fail2:
1778     pa_assert_se(r = dbus_message_new_error(m, "org.bluez.Error.InvalidArguments", "Unable to set configuration"));
1779     return r;
1780 }
1781
1782 static DBusMessage *endpoint_select_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) {
1783     pa_bluetooth_discovery *y = userdata;
1784     a2dp_sbc_t *cap, config;
1785     uint8_t *pconf = (uint8_t *) &config;
1786     int i, size;
1787     DBusMessage *r;
1788     DBusError err;
1789
1790     static const struct {
1791         uint32_t rate;
1792         uint8_t cap;
1793     } freq_table[] = {
1794         { 16000U, SBC_SAMPLING_FREQ_16000 },
1795         { 32000U, SBC_SAMPLING_FREQ_32000 },
1796         { 44100U, SBC_SAMPLING_FREQ_44100 },
1797         { 48000U, SBC_SAMPLING_FREQ_48000 }
1798     };
1799
1800 #ifdef BLUETOOTH_APTX_SUPPORT
1801     if (dbus_message_has_path(m, A2DP_APTX_SOURCE_ENDPOINT))
1802         return endpoint_select_configuration_for_aptx(conn ,m ,userdata);
1803 #endif
1804
1805     dbus_error_init(&err);
1806
1807     if (!dbus_message_get_args(m, &err, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &cap, &size, DBUS_TYPE_INVALID)) {
1808         pa_log_error("Endpoint SelectConfiguration(): %s", err.message);
1809         dbus_error_free(&err);
1810         goto fail;
1811     }
1812
1813     if (size != sizeof(config)) {
1814         pa_log_error("Capabilities array has invalid size");
1815         goto fail;
1816     }
1817
1818     pa_zero(config);
1819
1820     /* Find the lowest freq that is at least as high as the requested sampling rate */
1821     for (i = 0; (unsigned) i < PA_ELEMENTSOF(freq_table); i++)
1822         if (freq_table[i].rate >= y->core->default_sample_spec.rate && (cap->frequency & freq_table[i].cap)) {
1823             config.frequency = freq_table[i].cap;
1824             break;
1825         }
1826
1827     if ((unsigned) i == PA_ELEMENTSOF(freq_table)) {
1828         for (--i; i >= 0; i--) {
1829             if (cap->frequency & freq_table[i].cap) {
1830                 config.frequency = freq_table[i].cap;
1831                 break;
1832             }
1833         }
1834
1835         if (i < 0) {
1836             pa_log_error("Not suitable sample rate");
1837             goto fail;
1838         }
1839     }
1840
1841     pa_assert((unsigned) i < PA_ELEMENTSOF(freq_table));
1842
1843     if (y->core->default_sample_spec.channels <= 1) {
1844         if (cap->channel_mode & SBC_CHANNEL_MODE_MONO)
1845             config.channel_mode = SBC_CHANNEL_MODE_MONO;
1846         else if (cap->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)
1847             config.channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO;
1848         else if (cap->channel_mode & SBC_CHANNEL_MODE_STEREO)
1849             config.channel_mode = SBC_CHANNEL_MODE_STEREO;
1850         else if (cap->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL)
1851             config.channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL;
1852         else {
1853             pa_log_error("No supported channel modes");
1854             goto fail;
1855         }
1856     }
1857
1858     if (y->core->default_sample_spec.channels >= 2) {
1859         if (cap->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)
1860             config.channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO;
1861         else if (cap->channel_mode & SBC_CHANNEL_MODE_STEREO)
1862             config.channel_mode = SBC_CHANNEL_MODE_STEREO;
1863         else if (cap->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL)
1864             config.channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL;
1865         else if (cap->channel_mode & SBC_CHANNEL_MODE_MONO)
1866             config.channel_mode = SBC_CHANNEL_MODE_MONO;
1867         else {
1868             pa_log_error("No supported channel modes");
1869             goto fail;
1870         }
1871     }
1872
1873     if (cap->block_length & SBC_BLOCK_LENGTH_16)
1874         config.block_length = SBC_BLOCK_LENGTH_16;
1875     else if (cap->block_length & SBC_BLOCK_LENGTH_12)
1876         config.block_length = SBC_BLOCK_LENGTH_12;
1877     else if (cap->block_length & SBC_BLOCK_LENGTH_8)
1878         config.block_length = SBC_BLOCK_LENGTH_8;
1879     else if (cap->block_length & SBC_BLOCK_LENGTH_4)
1880         config.block_length = SBC_BLOCK_LENGTH_4;
1881     else {
1882         pa_log_error("No supported block lengths");
1883         goto fail;
1884     }
1885
1886     if (cap->subbands & SBC_SUBBANDS_8)
1887         config.subbands = SBC_SUBBANDS_8;
1888     else if (cap->subbands & SBC_SUBBANDS_4)
1889         config.subbands = SBC_SUBBANDS_4;
1890     else {
1891         pa_log_error("No supported subbands");
1892         goto fail;
1893     }
1894
1895     if (cap->allocation_method & SBC_ALLOCATION_LOUDNESS)
1896         config.allocation_method = SBC_ALLOCATION_LOUDNESS;
1897     else if (cap->allocation_method & SBC_ALLOCATION_SNR)
1898         config.allocation_method = SBC_ALLOCATION_SNR;
1899
1900     config.min_bitpool = (uint8_t) PA_MAX(MIN_BITPOOL, cap->min_bitpool);
1901     config.max_bitpool = (uint8_t) PA_MIN(a2dp_default_bitpool(config.frequency, config.channel_mode), cap->max_bitpool);
1902
1903     if (config.min_bitpool > config.max_bitpool)
1904         goto fail;
1905
1906     pa_assert_se(r = dbus_message_new_method_return(m));
1907     pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &pconf, size, DBUS_TYPE_INVALID));
1908
1909     return r;
1910
1911 fail:
1912     pa_assert_se(r = dbus_message_new_error(m, "org.bluez.Error.InvalidArguments", "Unable to select configuration"));
1913     return r;
1914 }
1915
1916 static DBusMessage *endpoint_clear_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) {
1917     pa_bluetooth_discovery *y = userdata;
1918     pa_bluetooth_transport *t;
1919     DBusMessage *r;
1920     DBusError err;
1921     const char *path;
1922
1923     dbus_error_init(&err);
1924
1925     if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
1926         pa_log_error("Endpoint ClearConfiguration(): %s", err.message);
1927         dbus_error_free(&err);
1928         goto fail;
1929     }
1930
1931     if ((t = pa_hashmap_get(y->transports, path))) {
1932         pa_log_debug("Clearing transport %s profile %s", t->path, pa_bluetooth_profile_to_string(t->profile));
1933         pa_bluetooth_transport_free(t);
1934     }
1935
1936     pa_assert_se(r = dbus_message_new_method_return(m));
1937
1938     return r;
1939
1940 fail:
1941     pa_assert_se(r = dbus_message_new_error(m, "org.bluez.Error.InvalidArguments", "Unable to clear configuration"));
1942     return r;
1943 }
1944
1945 static DBusMessage *endpoint_release(DBusConnection *conn, DBusMessage *m, void *userdata) {
1946     DBusMessage *r;
1947
1948     pa_assert_se(r = dbus_message_new_error(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE ".Error.NotImplemented",
1949                                             "Method not implemented"));
1950
1951     return r;
1952 }
1953
1954 #ifdef __TIZEN_BT__
1955 static DBusMessage *endpoint_suspend_media(DBusConnection *conn, DBusMessage *m, void *userdata) {
1956     pa_bluetooth_discovery *y = userdata;
1957     pa_bluetooth_transport *t;
1958     DBusMessage *r;
1959
1960     pa_log_debug("dbus Call from to Suspend Media");
1961     if (!(t = pa_hashmap_first(y->transports)))
1962         goto fail;
1963
1964     pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_SCO_STATE_CHANGED], t);
1965
1966     pa_assert_se(r = dbus_message_new_method_return(m));
1967
1968     return r;
1969
1970 fail:
1971     pa_assert_se(r = dbus_message_new_error(m, "org.bluez.Error.InvalidArguments", "Unable to clear configuration"));
1972     return r;
1973 }
1974
1975 #ifdef TIZEN_BT_AVC_TARGET
1976 int pa_bluetooth_transport_get_avc_mode(pa_bluetooth_transport *t, unsigned int *avc_mode)
1977 {
1978     DBusMessage *m, *r;
1979     DBusError err;
1980     int ret = 0;
1981     uint32_t mode = 0;
1982
1983     pa_assert(t);
1984     pa_assert(t->device);
1985     pa_assert(t->device->discovery);
1986
1987     pa_assert_se(m = dbus_message_new_method_call("org.projectx.bt", "/org/projectx/bt_service", "org.projectx.bt", "get_avc_mode"));
1988
1989     dbus_error_init(&err);
1990
1991     r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(t->device->discovery->connection), m, -1, &err);
1992     dbus_message_unref(m);
1993
1994     m = NULL;
1995     if (!r) {
1996         dbus_error_free(&err);
1997         return -1;
1998     }
1999
2000     if (!dbus_message_get_args(r, &err, DBUS_TYPE_UINT32, &mode, DBUS_TYPE_INVALID)) {
2001         pa_log_error("Failed to parse reply: %s", err.message);
2002         dbus_error_free(&err);
2003         ret = -1;
2004         goto finish;
2005     }
2006
2007     *avc_mode = mode;
2008
2009 finish:
2010     dbus_message_unref(r);
2011     return ret;
2012 }
2013 #endif
2014 #endif
2015
2016 static DBusHandlerResult endpoint_handler(DBusConnection *c, DBusMessage *m, void *userdata) {
2017     struct pa_bluetooth_discovery *y = userdata;
2018     DBusMessage *r = NULL;
2019     const char *path, *interface, *member;
2020
2021     pa_assert(y);
2022
2023     path = dbus_message_get_path(m);
2024     interface = dbus_message_get_interface(m);
2025     member = dbus_message_get_member(m);
2026
2027     pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member);
2028
2029 #ifdef __TIZEN_BT__
2030     pa_assert_se(path);
2031 #endif
2032
2033 #ifdef BLUETOOTH_APTX_SUPPORT
2034 #ifdef TIZEN_BT_A2DP_MULTISTREAM
2035     if (!pa_streq(path, A2DP_SOURCE_ENDPOINT) && !pa_streq(path, A2DP_SINK_ENDPOINT) && !pa_streq(path, A2DP_SINK_ENDPOINT2)
2036          && !pa_streq(path,A2DP_APTX_SOURCE_ENDPOINT))
2037         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2038 #else
2039      if (!pa_streq(path, A2DP_SOURCE_ENDPOINT) && !pa_streq(path, A2DP_SINK_ENDPOINT) && !pa_streq(path,A2DP_APTX_SOURCE_ENDPOINT))
2040         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2041 #endif
2042 #else
2043 #ifdef TIZEN_BT_A2DP_MULTISTREAM
2044     if (!pa_streq(path, A2DP_SOURCE_ENDPOINT) && !pa_streq(path, A2DP_SINK_ENDPOINT) && !pa_streq(path, A2DP_SINK_ENDPOINT2))
2045         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2046 #else
2047     if (!pa_streq(path, A2DP_SOURCE_ENDPOINT) && !pa_streq(path, A2DP_SINK_ENDPOINT))
2048         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2049 #endif
2050 #endif /* BLUETOOTH_APTX_SUPPORT */
2051
2052     if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
2053         const char *xml = ENDPOINT_INTROSPECT_XML;
2054
2055         pa_assert_se(r = dbus_message_new_method_return(m));
2056         pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID));
2057
2058     } else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "SetConfiguration"))
2059         r = endpoint_set_configuration(c, m, userdata);
2060     else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "SelectConfiguration"))
2061         r = endpoint_select_configuration(c, m, userdata);
2062     else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "ClearConfiguration"))
2063         r = endpoint_clear_configuration(c, m, userdata);
2064     else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "Release"))
2065         r = endpoint_release(c, m, userdata);
2066 #ifdef __TIZEN_BT__
2067     else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "SuspendMedia"))
2068         endpoint_suspend_media(c, m, userdata);
2069 #endif
2070     else
2071         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2072
2073     if (r) {
2074         pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), r, NULL));
2075         dbus_message_unref(r);
2076     }
2077
2078     return DBUS_HANDLER_RESULT_HANDLED;
2079 }
2080
2081 static void endpoint_init(pa_bluetooth_discovery *y, pa_bluetooth_profile_t profile) {
2082     static const DBusObjectPathVTable vtable_endpoint = {
2083         .message_function = endpoint_handler,
2084     };
2085
2086     pa_assert(y);
2087
2088     switch(profile) {
2089         case PA_BLUETOOTH_PROFILE_A2DP_SINK:
2090             pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection), A2DP_SOURCE_ENDPOINT,
2091                                                               &vtable_endpoint, y));
2092 #ifdef BLUETOOTH_APTX_SUPPORT
2093             pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection), A2DP_APTX_SOURCE_ENDPOINT,
2094                                                               &vtable_endpoint, y));
2095 #endif
2096             break;
2097         case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
2098             pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection), A2DP_SINK_ENDPOINT,
2099                                                               &vtable_endpoint, y));
2100 #ifdef TIZEN_BT_A2DP_MULTISTREAM
2101             pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection), A2DP_SINK_ENDPOINT2,
2102                                                             &vtable_endpoint, y));
2103 #endif
2104             break;
2105         default:
2106             pa_assert_not_reached();
2107             break;
2108     }
2109 }
2110
2111 static void endpoint_done(pa_bluetooth_discovery *y, pa_bluetooth_profile_t profile) {
2112     pa_assert(y);
2113
2114     switch(profile) {
2115         case PA_BLUETOOTH_PROFILE_A2DP_SINK:
2116             dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), A2DP_SOURCE_ENDPOINT);
2117 #ifdef BLUETOOTH_APTX_SUPPORT
2118             dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), A2DP_APTX_SOURCE_ENDPOINT);
2119 #endif
2120             break;
2121         case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
2122             dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), A2DP_SINK_ENDPOINT);
2123 #ifdef TIZEN_BT_A2DP_MULTISTREAM
2124             dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), A2DP_SINK_ENDPOINT2);
2125             break;
2126 #endif
2127         default:
2128             pa_assert_not_reached();
2129             break;
2130     }
2131 }
2132
2133 pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c, int headset_backend) {
2134     pa_bluetooth_discovery *y;
2135     DBusError err;
2136     DBusConnection *conn;
2137     unsigned i;
2138
2139     y = pa_xnew0(pa_bluetooth_discovery, 1);
2140     PA_REFCNT_INIT(y);
2141     y->core = c;
2142     y->headset_backend = headset_backend;
2143     y->adapters = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
2144                                       (pa_free_cb_t) adapter_free);
2145     y->devices = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
2146                                      (pa_free_cb_t) device_free);
2147     y->transports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
2148     PA_LLIST_HEAD_INIT(pa_dbus_pending, y->pending);
2149
2150     for (i = 0; i < PA_BLUETOOTH_HOOK_MAX; i++)
2151         pa_hook_init(&y->hooks[i], y);
2152
2153     pa_shared_set(c, "bluetooth-discovery", y);
2154
2155     dbus_error_init(&err);
2156
2157     if (!(y->connection = pa_dbus_bus_get(y->core, DBUS_BUS_SYSTEM, &err))) {
2158         pa_log_error("Failed to get D-Bus connection: %s", err.message);
2159         goto fail;
2160     }
2161
2162     conn = pa_dbus_connection_get(y->connection);
2163
2164     /* dynamic detection of bluetooth audio devices */
2165     if (!dbus_connection_add_filter(conn, filter_cb, y, NULL)) {
2166         pa_log_error("Failed to add filter function");
2167         goto fail;
2168     }
2169     y->filter_added = true;
2170
2171     if (pa_dbus_add_matches(conn, &err,
2172             "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'"
2173             ",arg0='" BLUEZ_SERVICE "'",
2174             "type='signal',sender='" BLUEZ_SERVICE "',interface='org.freedesktop.DBus.ObjectManager',member='InterfacesAdded'",
2175             "type='signal',sender='" BLUEZ_SERVICE "',interface='org.freedesktop.DBus.ObjectManager',"
2176             "member='InterfacesRemoved'",
2177             "type='signal',sender='" BLUEZ_SERVICE "',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'"
2178             ",arg0='" BLUEZ_ADAPTER_INTERFACE "'",
2179             "type='signal',sender='" BLUEZ_SERVICE "',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'"
2180             ",arg0='" BLUEZ_DEVICE_INTERFACE "'",
2181             "type='signal',sender='" BLUEZ_SERVICE "',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'"
2182             ",arg0='" BLUEZ_MEDIA_TRANSPORT_INTERFACE "'",
2183 #ifdef __TIZEN_BT__
2184             "type='signal',interface='org.bluez.ag_agent',member='SuspendMedia'",
2185 #ifdef TIZEN_BT_AVC_TARGET
2186             "type='signal',interface='org.projectx.bt_event',member='AvcModeChanged',path='/org/projectx/bt/avc_mode'",
2187 #endif
2188 #endif
2189             NULL) < 0) {
2190         pa_log_error("Failed to add D-Bus matches: %s", err.message);
2191         goto fail;
2192     }
2193     y->matches_added = true;
2194
2195     endpoint_init(y, PA_BLUETOOTH_PROFILE_A2DP_SINK);
2196     endpoint_init(y, PA_BLUETOOTH_PROFILE_A2DP_SOURCE);
2197
2198 #ifndef __TIZEN_BT__
2199     y->hf_audio_agent = hf_audio_agent_init(c);
2200 #endif
2201
2202     get_managed_objects(y);
2203
2204     return y;
2205
2206 fail:
2207     pa_bluetooth_discovery_unref(y);
2208     dbus_error_free(&err);
2209
2210     return NULL;
2211 }
2212
2213 pa_bluetooth_discovery* pa_bluetooth_discovery_ref(pa_bluetooth_discovery *y) {
2214     pa_assert(y);
2215     pa_assert(PA_REFCNT_VALUE(y) > 0);
2216
2217     PA_REFCNT_INC(y);
2218
2219     return y;
2220 }
2221
2222 void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) {
2223     pa_assert(y);
2224     pa_assert(PA_REFCNT_VALUE(y) > 0);
2225
2226     if (PA_REFCNT_DEC(y) > 0)
2227         return;
2228
2229     pa_dbus_free_pending_list(&y->pending);
2230
2231     if (y->ofono_backend)
2232         pa_bluetooth_ofono_backend_free(y->ofono_backend);
2233     if (y->native_backend)
2234         pa_bluetooth_native_backend_free(y->native_backend);
2235
2236     if (y->adapters)
2237         pa_hashmap_free(y->adapters);
2238
2239     if (y->devices)
2240         pa_hashmap_free(y->devices);
2241
2242     if (y->transports) {
2243         pa_assert(pa_hashmap_isempty(y->transports));
2244         pa_hashmap_free(y->transports);
2245     }
2246
2247 #ifndef __TIZEN_BT__
2248     if (y->hf_audio_agent)
2249         hf_audio_agent_done(y->hf_audio_agent);
2250 #endif
2251
2252     if (y->connection) {
2253
2254         if (y->matches_added)
2255             pa_dbus_remove_matches(pa_dbus_connection_get(y->connection),
2256                 "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',"
2257                 "arg0='" BLUEZ_SERVICE "'",
2258                 "type='signal',sender='" BLUEZ_SERVICE "',interface='org.freedesktop.DBus.ObjectManager',"
2259                 "member='InterfacesAdded'",
2260                 "type='signal',sender='" BLUEZ_SERVICE "',interface='org.freedesktop.DBus.ObjectManager',"
2261                 "member='InterfacesRemoved'",
2262                 "type='signal',sender='" BLUEZ_SERVICE "',interface='org.freedesktop.DBus.Properties',"
2263                 "member='PropertiesChanged',arg0='" BLUEZ_ADAPTER_INTERFACE "'",
2264                 "type='signal',sender='" BLUEZ_SERVICE "',interface='org.freedesktop.DBus.Properties',"
2265                 "member='PropertiesChanged',arg0='" BLUEZ_DEVICE_INTERFACE "'",
2266                 "type='signal',sender='" BLUEZ_SERVICE "',interface='org.freedesktop.DBus.Properties',"
2267                 "member='PropertiesChanged',arg0='" BLUEZ_MEDIA_TRANSPORT_INTERFACE "'",
2268 #ifdef __TIZEN_BT__
2269                  "type='signal',interface='org.bluez.ag_agent',member='SuspendMedia'",
2270 #ifdef TIZEN_BT_AVC_TARGET
2271                  "type='signal',interface='org.projectx.bt_event',member='AvcModeChanged',path='/org/projectx/bt/avc_mode'",
2272 #endif
2273 #endif
2274                 NULL);
2275
2276         if (y->filter_added)
2277             dbus_connection_remove_filter(pa_dbus_connection_get(y->connection), filter_cb, y);
2278
2279         endpoint_done(y, PA_BLUETOOTH_PROFILE_A2DP_SINK);
2280         endpoint_done(y, PA_BLUETOOTH_PROFILE_A2DP_SOURCE);
2281
2282         pa_dbus_connection_unref(y->connection);
2283     }
2284
2285     pa_shared_remove(y->core, "bluetooth-discovery");
2286     pa_xfree(y);
2287 }