2 This file is part of PulseAudio.
4 Copyright 2013 João Paulo Rechi Vita
6 PulseAudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as
8 published by the Free Software Foundation; either version 2.1 of the
9 License, or (at your option) any later version.
11 PulseAudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with PulseAudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
26 #include <pulsecore/core-util.h>
27 #include <pulsecore/dbus-shared.h>
28 #include <pulsecore/shared.h>
30 #include "bluez5-util.h"
32 #include "hfaudioagent.h"
34 #define HFP_AUDIO_CODEC_CVSD 0x01
35 #define HFP_AUDIO_CODEC_MSBC 0x02
37 #define OFONO_SERVICE "org.ofono"
38 #define HF_AUDIO_AGENT_INTERFACE OFONO_SERVICE ".HandsfreeAudioAgent"
39 #define HF_AUDIO_MANAGER_INTERFACE OFONO_SERVICE ".HandsfreeAudioManager"
41 #define HF_AUDIO_AGENT_PATH "/HandsfreeAudioAgent"
43 #define HF_AUDIO_AGENT_XML \
44 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
46 " <interface name=\"org.freedesktop.DBus.Introspectable\">" \
47 " <method name=\"Introspect\">" \
48 " <arg direction=\"out\" type=\"s\" />" \
51 " <interface name=\"org.ofono.HandsfreeAudioAgent\">" \
52 " <method name=\"Release\">" \
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\" />" \
62 typedef struct hf_audio_card {
70 pa_bluetooth_transport *transport;
73 struct hf_audio_agent_data {
75 pa_dbus_connection *connection;
76 pa_bluetooth_discovery *discovery;
80 pa_hashmap *hf_audio_cards;
82 PA_LLIST_HEAD(pa_dbus_pending, pending);
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) {
88 DBusPendingCall *call;
93 pa_assert_se(dbus_connection_send_with_reply(pa_dbus_connection_get(hfdata->connection), m, &call, -1));
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);
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);
105 hfac->path = pa_xstrdup(path);
111 static void hf_audio_card_free(void *data) {
112 hf_audio_card *hfac = data;
116 pa_bluetooth_transport_free(hfac->transport);
117 pa_xfree(hfac->path);
118 pa_xfree(hfac->remote);
119 pa_xfree(hfac->local);
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);
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));
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. */
147 t->codec = hfac->codec;
153 static void hf_audio_agent_transport_release(pa_bluetooth_transport *t) {
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;
160 pa_bluetooth_device *d;
166 pa_log_debug("New HF card found: %s", path);
168 hfac = hf_audio_card_new(hfdata, path);
170 while (dbus_message_iter_get_arg_type(props_i) != DBUS_TYPE_INVALID) {
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);
178 dbus_message_iter_recurse(props_i, &i);
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);
185 dbus_message_iter_get_basic(&i, &key);
186 dbus_message_iter_next(&i);
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);
193 dbus_message_iter_recurse(&i, &value_i);
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);
200 dbus_message_iter_get_basic(&value_i, &value);
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);
207 pa_log_debug("%s: %s", key, value);
209 dbus_message_iter_next(props_i);
212 pa_hashmap_put(hfdata->hf_audio_cards, hfac->path, hfac);
214 d = pa_bluetooth_discovery_get_device_by_address(hfdata->discovery, hfac->remote, hfac->local);
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;
221 d->transports[PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY] = hfac->transport;
223 pa_bluetooth_transport_put(hfac->transport);
225 pa_log_error("Device doesnt exist for %s", path);
233 static void hf_audio_agent_get_cards_reply(DBusPendingCall *pending, void *userdata) {
236 hf_audio_agent_data *hfdata;
237 DBusMessageIter i, array_i, struct_i, props_i;
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));
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));
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);
256 dbus_message_iter_recurse(&i, &array_i);
257 while (dbus_message_iter_get_arg_type(&array_i) != DBUS_TYPE_INVALID) {
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);
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);
271 dbus_message_iter_get_basic(&struct_i, &path);
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);
279 dbus_message_iter_recurse(&struct_i, &props_i);
281 hf_audio_agent_card_found(hfdata, path, &props_i);
283 dbus_message_iter_next(&array_i);
287 dbus_message_unref(r);
289 PA_LLIST_REMOVE(pa_dbus_pending, hfdata->pending, p);
290 pa_dbus_pending_free(p);
293 static void hf_audio_agent_get_cards(hf_audio_agent_data *hfdata) {
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);
302 static void hf_audio_agent_register_reply(DBusPendingCall *pending, void *userdata) {
305 hf_audio_agent_data *hfdata;
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));
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));
317 hfdata->ofono_bus_id = pa_xstrdup(dbus_message_get_sender(r));
319 hf_audio_agent_get_cards(hfdata);
322 dbus_message_unref(r);
324 PA_LLIST_REMOVE(pa_dbus_pending, hfdata->pending, p);
325 pa_dbus_pending_free(p);
328 static void hf_audio_agent_register(hf_audio_agent_data *hfdata) {
330 unsigned char codecs[2];
331 const unsigned char *pcodecs = codecs;
333 const char *path = HF_AUDIO_AGENT_PATH;
337 pa_assert_se(m = dbus_message_new_method_call(OFONO_SERVICE, "/", HF_AUDIO_MANAGER_INTERFACE, "Register"));
339 codecs[ncodecs++] = HFP_AUDIO_CODEC_CVSD;
340 codecs[ncodecs++] = HFP_AUDIO_CODEC_MSBC;
342 pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &pcodecs, ncodecs,
345 pa_bluetooth_dbus_send_and_add_to_pending(hfdata, m, hf_audio_agent_register_reply, NULL);
348 static void hf_audio_agent_unregister(hf_audio_agent_data *hfdata) {
350 const char *path = HF_AUDIO_AGENT_PATH;
353 pa_assert(hfdata->connection);
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));
360 pa_xfree(hfdata->ofono_bus_id);
361 hfdata->ofono_bus_id = NULL;
365 static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *data) {
367 hf_audio_agent_data *hfdata = data;
373 sender = dbus_message_get_sender(m);
374 if (!pa_safe_streq(hfdata->ofono_bus_id, sender) && !pa_streq("org.freedesktop.DBus", sender))
375 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
377 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
380 static DBusMessage *hf_audio_agent_release(DBusConnection *c, DBusMessage *m, void *data) {
383 hf_audio_agent_data *hfdata = data;
387 sender = dbus_message_get_sender(m);
388 if (!pa_streq(hfdata->ofono_bus_id, sender)) {
389 pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.NotAllowed", "Operation is not allowed by this sender"));
393 r = dbus_message_new_error(m, "org.ofono.Error.NotImplemented", "Operation is not implemented");
397 static DBusMessage *hf_audio_agent_new_connection(DBusConnection *c, DBusMessage *m, void *data) {
400 hf_audio_agent_data *hfdata = data;
404 sender = dbus_message_get_sender(m);
405 if (!pa_streq(hfdata->ofono_bus_id, sender)) {
406 pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.NotAllowed", "Operation is not allowed by this sender"));
410 r = dbus_message_new_error(m, "org.ofono.Error.NotImplemented", "Operation is not implemented");
414 static DBusHandlerResult hf_audio_agent_handler(DBusConnection *c, DBusMessage *m, void *data) {
415 hf_audio_agent_data *hfdata = data;
416 DBusMessage *r = NULL;
417 const char *path, *interface, *member;
421 path = dbus_message_get_path(m);
422 interface = dbus_message_get_interface(m);
423 member = dbus_message_get_member(m);
425 if (!pa_streq(path, HF_AUDIO_AGENT_PATH))
426 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
428 pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member);
430 if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
431 const char *xml = HF_AUDIO_AGENT_XML;
433 pa_assert_se(r = dbus_message_new_method_return(m));
434 pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID));
436 } else if (dbus_message_is_method_call(m, HF_AUDIO_AGENT_INTERFACE, "NewConnection"))
437 r = hf_audio_agent_new_connection(c, m, data);
438 else if (dbus_message_is_method_call(m, HF_AUDIO_AGENT_INTERFACE, "Release"))
439 r = hf_audio_agent_release(c, m, data);
441 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
444 pa_assert_se(dbus_connection_send(pa_dbus_connection_get(hfdata->connection), r, NULL));
445 dbus_message_unref(r);
448 return DBUS_HANDLER_RESULT_HANDLED;
451 hf_audio_agent_data *hf_audio_agent_init(pa_core *c) {
452 hf_audio_agent_data *hfdata;
454 static const DBusObjectPathVTable vtable_hf_audio_agent = {
455 .message_function = hf_audio_agent_handler,
460 hfdata = pa_xnew0(hf_audio_agent_data, 1);
462 hfdata->hf_audio_cards = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
463 hfdata->discovery = pa_shared_get(c, "bluetooth-discovery");
465 dbus_error_init(&err);
467 if (!(hfdata->connection = pa_dbus_bus_get(c, DBUS_BUS_SYSTEM, &err))) {
468 pa_log("Failed to get D-Bus connection: %s", err.message);
469 dbus_error_free(&err);
473 /* dynamic detection of handsfree audio cards */
474 if (!dbus_connection_add_filter(pa_dbus_connection_get(hfdata->connection), filter_cb, hfdata, NULL)) {
475 pa_log_error("Failed to add filter function");
476 hf_audio_agent_done(hfdata);
479 hfdata->filter_added = true;
481 if (pa_dbus_add_matches(pa_dbus_connection_get(hfdata->connection), &err,
482 "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',"
483 "arg0='" OFONO_SERVICE "'",
484 "type='signal',sender='" OFONO_SERVICE "',interface='" HF_AUDIO_MANAGER_INTERFACE "',member='CardAdded'",
485 "type='signal',sender='" OFONO_SERVICE "',interface='" HF_AUDIO_MANAGER_INTERFACE "',member='CardRemoved'",
487 pa_log("Failed to add oFono D-Bus matches: %s", err.message);
488 hf_audio_agent_done(hfdata);
492 pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(hfdata->connection), HF_AUDIO_AGENT_PATH,
493 &vtable_hf_audio_agent, hfdata));
495 hf_audio_agent_register(hfdata);
500 void hf_audio_agent_done(hf_audio_agent_data *data) {
501 hf_audio_agent_data *hfdata = data;
505 pa_dbus_free_pending_list(&hfdata->pending);
507 if (hfdata->hf_audio_cards) {
508 pa_hashmap_free(hfdata->hf_audio_cards, hf_audio_card_free);
509 hfdata->hf_audio_cards = NULL;
512 if (hfdata->connection) {
514 pa_dbus_remove_matches(
515 pa_dbus_connection_get(hfdata->connection),
516 "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',"
517 "arg0='" OFONO_SERVICE "'",
518 "type='signal',sender='" OFONO_SERVICE "',interface='" HF_AUDIO_MANAGER_INTERFACE "',member='CardAdded'",
519 "type='signal',sender='" OFONO_SERVICE "',interface='" HF_AUDIO_MANAGER_INTERFACE "',member='CardRemoved'",
522 if (hfdata->filter_added)
523 dbus_connection_remove_filter(pa_dbus_connection_get(hfdata->connection), filter_cb, hfdata);
525 hf_audio_agent_unregister(hfdata);
527 dbus_connection_unregister_object_path(pa_dbus_connection_get(hfdata->connection), HF_AUDIO_AGENT_PATH);
529 pa_dbus_connection_unref(hfdata->connection);
532 if (hfdata->discovery)
533 hfdata->discovery = NULL;