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