153a5a1b3Sopenharmony_ci/*** 253a5a1b3Sopenharmony_ci This file is part of PulseAudio. 353a5a1b3Sopenharmony_ci 453a5a1b3Sopenharmony_ci Copyright 2013 João Paulo Rechi Vita 553a5a1b3Sopenharmony_ci 653a5a1b3Sopenharmony_ci PulseAudio is free software; you can redistribute it and/or modify 753a5a1b3Sopenharmony_ci it under the terms of the GNU Lesser General Public License as 853a5a1b3Sopenharmony_ci published by the Free Software Foundation; either version 2.1 of the 953a5a1b3Sopenharmony_ci License, or (at your option) any later version. 1053a5a1b3Sopenharmony_ci 1153a5a1b3Sopenharmony_ci PulseAudio is distributed in the hope that it will be useful, but 1253a5a1b3Sopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 1353a5a1b3Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1453a5a1b3Sopenharmony_ci General Public License for more details. 1553a5a1b3Sopenharmony_ci 1653a5a1b3Sopenharmony_ci You should have received a copy of the GNU Lesser General Public 1753a5a1b3Sopenharmony_ci License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 1853a5a1b3Sopenharmony_ci***/ 1953a5a1b3Sopenharmony_ci 2053a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H 2153a5a1b3Sopenharmony_ci#include <config.h> 2253a5a1b3Sopenharmony_ci#endif 2353a5a1b3Sopenharmony_ci 2453a5a1b3Sopenharmony_ci#include <errno.h> 2553a5a1b3Sopenharmony_ci#include <poll.h> 2653a5a1b3Sopenharmony_ci 2753a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h> 2853a5a1b3Sopenharmony_ci#include <pulsecore/dbus-shared.h> 2953a5a1b3Sopenharmony_ci#include <pulsecore/shared.h> 3053a5a1b3Sopenharmony_ci#include <pulsecore/core-error.h> 3153a5a1b3Sopenharmony_ci 3253a5a1b3Sopenharmony_ci#include "bluez5-util.h" 3353a5a1b3Sopenharmony_ci 3453a5a1b3Sopenharmony_ci#define HFP_AUDIO_CODEC_CVSD 0x01 3553a5a1b3Sopenharmony_ci#define HFP_AUDIO_CODEC_MSBC 0x02 3653a5a1b3Sopenharmony_ci 3753a5a1b3Sopenharmony_ci#define OFONO_SERVICE "org.ofono" 3853a5a1b3Sopenharmony_ci#define HF_AUDIO_AGENT_INTERFACE OFONO_SERVICE ".HandsfreeAudioAgent" 3953a5a1b3Sopenharmony_ci#define HF_AUDIO_MANAGER_INTERFACE OFONO_SERVICE ".HandsfreeAudioManager" 4053a5a1b3Sopenharmony_ci 4153a5a1b3Sopenharmony_ci#define HF_AUDIO_AGENT_PATH "/HandsfreeAudioAgent" 4253a5a1b3Sopenharmony_ci 4353a5a1b3Sopenharmony_ci#define HF_AUDIO_AGENT_XML \ 4453a5a1b3Sopenharmony_ci DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ 4553a5a1b3Sopenharmony_ci "<node>" \ 4653a5a1b3Sopenharmony_ci " <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">" \ 4753a5a1b3Sopenharmony_ci " <method name=\"Introspect\">" \ 4853a5a1b3Sopenharmony_ci " <arg direction=\"out\" type=\"s\" />" \ 4953a5a1b3Sopenharmony_ci " </method>" \ 5053a5a1b3Sopenharmony_ci " </interface>" \ 5153a5a1b3Sopenharmony_ci " <interface name=\"" HF_AUDIO_AGENT_INTERFACE "\">" \ 5253a5a1b3Sopenharmony_ci " <method name=\"Release\">" \ 5353a5a1b3Sopenharmony_ci " </method>" \ 5453a5a1b3Sopenharmony_ci " <method name=\"NewConnection\">" \ 5553a5a1b3Sopenharmony_ci " <arg direction=\"in\" type=\"o\" name=\"card_path\" />" \ 5653a5a1b3Sopenharmony_ci " <arg direction=\"in\" type=\"h\" name=\"sco_fd\" />" \ 5753a5a1b3Sopenharmony_ci " <arg direction=\"in\" type=\"y\" name=\"codec\" />" \ 5853a5a1b3Sopenharmony_ci " </method>" \ 5953a5a1b3Sopenharmony_ci " </interface>" \ 6053a5a1b3Sopenharmony_ci "</node>" 6153a5a1b3Sopenharmony_ci 6253a5a1b3Sopenharmony_cistruct hf_audio_card { 6353a5a1b3Sopenharmony_ci pa_bluetooth_backend *backend; 6453a5a1b3Sopenharmony_ci char *path; 6553a5a1b3Sopenharmony_ci char *remote_address; 6653a5a1b3Sopenharmony_ci char *local_address; 6753a5a1b3Sopenharmony_ci 6853a5a1b3Sopenharmony_ci bool connecting; 6953a5a1b3Sopenharmony_ci int fd; 7053a5a1b3Sopenharmony_ci int (*acquire)(struct hf_audio_card *card); 7153a5a1b3Sopenharmony_ci 7253a5a1b3Sopenharmony_ci pa_bluetooth_transport *transport; 7353a5a1b3Sopenharmony_ci pa_hook_slot *device_unlink_slot; 7453a5a1b3Sopenharmony_ci}; 7553a5a1b3Sopenharmony_ci 7653a5a1b3Sopenharmony_cistruct pa_bluetooth_backend { 7753a5a1b3Sopenharmony_ci pa_core *core; 7853a5a1b3Sopenharmony_ci pa_bluetooth_discovery *discovery; 7953a5a1b3Sopenharmony_ci pa_dbus_connection *connection; 8053a5a1b3Sopenharmony_ci pa_hashmap *cards; 8153a5a1b3Sopenharmony_ci char *ofono_bus_id; 8253a5a1b3Sopenharmony_ci 8353a5a1b3Sopenharmony_ci PA_LLIST_HEAD(pa_dbus_pending, pending); 8453a5a1b3Sopenharmony_ci}; 8553a5a1b3Sopenharmony_ci 8653a5a1b3Sopenharmony_cistatic ssize_t sco_transport_write(pa_bluetooth_transport *t, int fd, const void* buffer, size_t size, size_t write_mtu) { 8753a5a1b3Sopenharmony_ci ssize_t l = 0; 8853a5a1b3Sopenharmony_ci size_t written = 0; 8953a5a1b3Sopenharmony_ci size_t write_size; 9053a5a1b3Sopenharmony_ci 9153a5a1b3Sopenharmony_ci pa_assert(t); 9253a5a1b3Sopenharmony_ci 9353a5a1b3Sopenharmony_ci /* since SCO setup is symmetric, fix write MTU to be size of last read packet */ 9453a5a1b3Sopenharmony_ci if (t->last_read_size) 9553a5a1b3Sopenharmony_ci write_mtu = PA_MIN(t->last_read_size, write_mtu); 9653a5a1b3Sopenharmony_ci 9753a5a1b3Sopenharmony_ci /* if encoder buffer has less data than required to make complete packet */ 9853a5a1b3Sopenharmony_ci if (size < write_mtu) 9953a5a1b3Sopenharmony_ci return 0; 10053a5a1b3Sopenharmony_ci 10153a5a1b3Sopenharmony_ci /* write out MTU sized chunks only */ 10253a5a1b3Sopenharmony_ci while (written < size) { 10353a5a1b3Sopenharmony_ci write_size = PA_MIN(size - written, write_mtu); 10453a5a1b3Sopenharmony_ci if (write_size < write_mtu) 10553a5a1b3Sopenharmony_ci break; 10653a5a1b3Sopenharmony_ci l = pa_write(fd, buffer + written, write_size, &t->stream_write_type); 10753a5a1b3Sopenharmony_ci if (l < 0) 10853a5a1b3Sopenharmony_ci break; 10953a5a1b3Sopenharmony_ci written += l; 11053a5a1b3Sopenharmony_ci } 11153a5a1b3Sopenharmony_ci 11253a5a1b3Sopenharmony_ci if (l < 0) { 11353a5a1b3Sopenharmony_ci if (errno == EAGAIN) { 11453a5a1b3Sopenharmony_ci /* Hmm, apparently the socket was not writable, give up for now */ 11553a5a1b3Sopenharmony_ci pa_log_debug("Got EAGAIN on write() after POLLOUT, probably there is a temporary connection loss."); 11653a5a1b3Sopenharmony_ci /* Drain write buffer */ 11753a5a1b3Sopenharmony_ci written = size; 11853a5a1b3Sopenharmony_ci } else if (errno == EINVAL && t->last_read_size == 0) { 11953a5a1b3Sopenharmony_ci /* Likely write_link_mtu is still wrong, retry after next successful read */ 12053a5a1b3Sopenharmony_ci pa_log_debug("got write EINVAL, next successful read should fix MTU"); 12153a5a1b3Sopenharmony_ci /* Drain write buffer */ 12253a5a1b3Sopenharmony_ci written = size; 12353a5a1b3Sopenharmony_ci } else { 12453a5a1b3Sopenharmony_ci pa_log_error("Failed to write data to socket: %s", pa_cstrerror(errno)); 12553a5a1b3Sopenharmony_ci /* Report error from write call */ 12653a5a1b3Sopenharmony_ci return -1; 12753a5a1b3Sopenharmony_ci } 12853a5a1b3Sopenharmony_ci } 12953a5a1b3Sopenharmony_ci 13053a5a1b3Sopenharmony_ci /* if too much data left discard it all */ 13153a5a1b3Sopenharmony_ci if (size - written >= write_mtu) { 13253a5a1b3Sopenharmony_ci pa_log_warn("Wrote memory block to socket only partially! %lu written, discarding pending write size %lu larger than write_mtu %lu", 13353a5a1b3Sopenharmony_ci written, size, write_mtu); 13453a5a1b3Sopenharmony_ci /* Drain write buffer */ 13553a5a1b3Sopenharmony_ci written = size; 13653a5a1b3Sopenharmony_ci } 13753a5a1b3Sopenharmony_ci 13853a5a1b3Sopenharmony_ci return written; 13953a5a1b3Sopenharmony_ci} 14053a5a1b3Sopenharmony_ci 14153a5a1b3Sopenharmony_cistatic pa_dbus_pending* hf_dbus_send_and_add_to_pending(pa_bluetooth_backend *backend, DBusMessage *m, 14253a5a1b3Sopenharmony_ci DBusPendingCallNotifyFunction func, void *call_data) { 14353a5a1b3Sopenharmony_ci pa_dbus_pending *p; 14453a5a1b3Sopenharmony_ci DBusPendingCall *call; 14553a5a1b3Sopenharmony_ci 14653a5a1b3Sopenharmony_ci pa_assert(backend); 14753a5a1b3Sopenharmony_ci pa_assert(m); 14853a5a1b3Sopenharmony_ci 14953a5a1b3Sopenharmony_ci pa_assert_se(dbus_connection_send_with_reply(pa_dbus_connection_get(backend->connection), m, &call, -1)); 15053a5a1b3Sopenharmony_ci 15153a5a1b3Sopenharmony_ci p = pa_dbus_pending_new(pa_dbus_connection_get(backend->connection), m, call, backend, call_data); 15253a5a1b3Sopenharmony_ci PA_LLIST_PREPEND(pa_dbus_pending, backend->pending, p); 15353a5a1b3Sopenharmony_ci dbus_pending_call_set_notify(call, func, p, NULL); 15453a5a1b3Sopenharmony_ci 15553a5a1b3Sopenharmony_ci return p; 15653a5a1b3Sopenharmony_ci} 15753a5a1b3Sopenharmony_ci 15853a5a1b3Sopenharmony_cistatic DBusMessage *card_send(struct hf_audio_card *card, const char *method, DBusError *err) 15953a5a1b3Sopenharmony_ci{ 16053a5a1b3Sopenharmony_ci pa_bluetooth_transport *t = card->transport; 16153a5a1b3Sopenharmony_ci DBusMessage *m, *r; 16253a5a1b3Sopenharmony_ci 16353a5a1b3Sopenharmony_ci pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, "org.ofono.HandsfreeAudioCard", method)); 16453a5a1b3Sopenharmony_ci r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(card->backend->connection), m, -1, err); 16553a5a1b3Sopenharmony_ci dbus_message_unref(m); 16653a5a1b3Sopenharmony_ci 16753a5a1b3Sopenharmony_ci return r; 16853a5a1b3Sopenharmony_ci} 16953a5a1b3Sopenharmony_ci 17053a5a1b3Sopenharmony_cistatic int card_connect(struct hf_audio_card *card) { 17153a5a1b3Sopenharmony_ci DBusMessage *r; 17253a5a1b3Sopenharmony_ci DBusError err; 17353a5a1b3Sopenharmony_ci 17453a5a1b3Sopenharmony_ci if (card->connecting) 17553a5a1b3Sopenharmony_ci return -EAGAIN; 17653a5a1b3Sopenharmony_ci 17753a5a1b3Sopenharmony_ci card->connecting = true; 17853a5a1b3Sopenharmony_ci 17953a5a1b3Sopenharmony_ci dbus_error_init(&err); 18053a5a1b3Sopenharmony_ci r = card_send(card, "Connect", &err); 18153a5a1b3Sopenharmony_ci 18253a5a1b3Sopenharmony_ci if (!r) { 18353a5a1b3Sopenharmony_ci pa_log_error("Failed to connect %s: %s", err.name, err.message); 18453a5a1b3Sopenharmony_ci card->connecting = false; 18553a5a1b3Sopenharmony_ci dbus_error_free(&err); 18653a5a1b3Sopenharmony_ci return -1; 18753a5a1b3Sopenharmony_ci } 18853a5a1b3Sopenharmony_ci 18953a5a1b3Sopenharmony_ci dbus_message_unref(r); 19053a5a1b3Sopenharmony_ci 19153a5a1b3Sopenharmony_ci if (card->connecting) 19253a5a1b3Sopenharmony_ci return -EAGAIN; 19353a5a1b3Sopenharmony_ci 19453a5a1b3Sopenharmony_ci return 0; 19553a5a1b3Sopenharmony_ci} 19653a5a1b3Sopenharmony_ci 19753a5a1b3Sopenharmony_cistatic int card_acquire(struct hf_audio_card *card) { 19853a5a1b3Sopenharmony_ci int fd; 19953a5a1b3Sopenharmony_ci uint8_t codec; 20053a5a1b3Sopenharmony_ci DBusMessage *r; 20153a5a1b3Sopenharmony_ci DBusError err; 20253a5a1b3Sopenharmony_ci 20353a5a1b3Sopenharmony_ci /* Try acquiring the stream first which was introduced in 1.21 */ 20453a5a1b3Sopenharmony_ci dbus_error_init(&err); 20553a5a1b3Sopenharmony_ci r = card_send(card, "Acquire", &err); 20653a5a1b3Sopenharmony_ci 20753a5a1b3Sopenharmony_ci if (!r) { 20853a5a1b3Sopenharmony_ci if (!pa_streq(err.name, DBUS_ERROR_UNKNOWN_METHOD)) { 20953a5a1b3Sopenharmony_ci pa_log_error("Failed to acquire %s: %s", err.name, err.message); 21053a5a1b3Sopenharmony_ci dbus_error_free(&err); 21153a5a1b3Sopenharmony_ci return -1; 21253a5a1b3Sopenharmony_ci } 21353a5a1b3Sopenharmony_ci dbus_error_free(&err); 21453a5a1b3Sopenharmony_ci /* Fallback to Connect as this might be an old version of ofono */ 21553a5a1b3Sopenharmony_ci card->acquire = card_connect; 21653a5a1b3Sopenharmony_ci return card_connect(card); 21753a5a1b3Sopenharmony_ci } 21853a5a1b3Sopenharmony_ci 21953a5a1b3Sopenharmony_ci if ((dbus_message_get_args(r, NULL, DBUS_TYPE_UNIX_FD, &fd, 22053a5a1b3Sopenharmony_ci DBUS_TYPE_BYTE, &codec, 22153a5a1b3Sopenharmony_ci DBUS_TYPE_INVALID) == true)) { 22253a5a1b3Sopenharmony_ci dbus_message_unref(r); 22353a5a1b3Sopenharmony_ci 22453a5a1b3Sopenharmony_ci if (codec == HFP_AUDIO_CODEC_CVSD) { 22553a5a1b3Sopenharmony_ci pa_bluetooth_transport_reconfigure(card->transport, pa_bluetooth_get_hf_codec("CVSD"), sco_transport_write, NULL); 22653a5a1b3Sopenharmony_ci } else if (codec == HFP_AUDIO_CODEC_MSBC) { 22753a5a1b3Sopenharmony_ci /* oFono is expected to set up socket BT_VOICE_TRANSPARENT option */ 22853a5a1b3Sopenharmony_ci pa_bluetooth_transport_reconfigure(card->transport, pa_bluetooth_get_hf_codec("mSBC"), sco_transport_write, NULL); 22953a5a1b3Sopenharmony_ci } else { 23053a5a1b3Sopenharmony_ci pa_assert_fp(codec != HFP_AUDIO_CODEC_CVSD && codec != HFP_AUDIO_CODEC_MSBC); 23153a5a1b3Sopenharmony_ci pa_log_error("Invalid codec: %u", codec); 23253a5a1b3Sopenharmony_ci /* shutdown to make sure connection is dropped immediately */ 23353a5a1b3Sopenharmony_ci shutdown(fd, SHUT_RDWR); 23453a5a1b3Sopenharmony_ci close(fd); 23553a5a1b3Sopenharmony_ci return -1; 23653a5a1b3Sopenharmony_ci } 23753a5a1b3Sopenharmony_ci 23853a5a1b3Sopenharmony_ci card->fd = fd; 23953a5a1b3Sopenharmony_ci return 0; 24053a5a1b3Sopenharmony_ci } 24153a5a1b3Sopenharmony_ci 24253a5a1b3Sopenharmony_ci pa_log_error("Unable to acquire"); 24353a5a1b3Sopenharmony_ci dbus_message_unref(r); 24453a5a1b3Sopenharmony_ci return -1; 24553a5a1b3Sopenharmony_ci} 24653a5a1b3Sopenharmony_ci 24753a5a1b3Sopenharmony_cistatic void hf_audio_agent_card_removed(pa_bluetooth_backend *backend, const char *path); 24853a5a1b3Sopenharmony_ci 24953a5a1b3Sopenharmony_cistatic pa_hook_result_t device_unlink_cb(pa_bluetooth_discovery *y, const pa_bluetooth_device *d, struct hf_audio_card *card) { 25053a5a1b3Sopenharmony_ci pa_assert(d); 25153a5a1b3Sopenharmony_ci pa_assert(card); 25253a5a1b3Sopenharmony_ci 25353a5a1b3Sopenharmony_ci if (d != card->transport->device) 25453a5a1b3Sopenharmony_ci return PA_HOOK_OK; 25553a5a1b3Sopenharmony_ci 25653a5a1b3Sopenharmony_ci hf_audio_agent_card_removed(card->backend, card->path); 25753a5a1b3Sopenharmony_ci 25853a5a1b3Sopenharmony_ci return PA_HOOK_OK; 25953a5a1b3Sopenharmony_ci} 26053a5a1b3Sopenharmony_ci 26153a5a1b3Sopenharmony_cistatic struct hf_audio_card *hf_audio_card_new(pa_bluetooth_backend *backend, const char *path) { 26253a5a1b3Sopenharmony_ci struct hf_audio_card *card = pa_xnew0(struct hf_audio_card, 1); 26353a5a1b3Sopenharmony_ci 26453a5a1b3Sopenharmony_ci card->path = pa_xstrdup(path); 26553a5a1b3Sopenharmony_ci card->backend = backend; 26653a5a1b3Sopenharmony_ci card->fd = -1; 26753a5a1b3Sopenharmony_ci card->acquire = card_acquire; 26853a5a1b3Sopenharmony_ci 26953a5a1b3Sopenharmony_ci card->device_unlink_slot = pa_hook_connect(pa_bluetooth_discovery_hook(backend->discovery, PA_BLUETOOTH_HOOK_DEVICE_UNLINK), 27053a5a1b3Sopenharmony_ci PA_HOOK_NORMAL, (pa_hook_cb_t) device_unlink_cb, card); 27153a5a1b3Sopenharmony_ci 27253a5a1b3Sopenharmony_ci return card; 27353a5a1b3Sopenharmony_ci} 27453a5a1b3Sopenharmony_ci 27553a5a1b3Sopenharmony_cistatic void hf_audio_card_free(struct hf_audio_card *card) { 27653a5a1b3Sopenharmony_ci pa_assert(card); 27753a5a1b3Sopenharmony_ci 27853a5a1b3Sopenharmony_ci if (card->device_unlink_slot) 27953a5a1b3Sopenharmony_ci pa_hook_slot_free(card->device_unlink_slot); 28053a5a1b3Sopenharmony_ci 28153a5a1b3Sopenharmony_ci if (card->transport) 28253a5a1b3Sopenharmony_ci pa_bluetooth_transport_free(card->transport); 28353a5a1b3Sopenharmony_ci 28453a5a1b3Sopenharmony_ci pa_xfree(card->path); 28553a5a1b3Sopenharmony_ci pa_xfree(card->remote_address); 28653a5a1b3Sopenharmony_ci pa_xfree(card->local_address); 28753a5a1b3Sopenharmony_ci pa_xfree(card); 28853a5a1b3Sopenharmony_ci} 28953a5a1b3Sopenharmony_ci 29053a5a1b3Sopenharmony_cistatic int socket_accept(int sock) 29153a5a1b3Sopenharmony_ci{ 29253a5a1b3Sopenharmony_ci char c; 29353a5a1b3Sopenharmony_ci struct pollfd pfd; 29453a5a1b3Sopenharmony_ci 29553a5a1b3Sopenharmony_ci if (sock < 0) 29653a5a1b3Sopenharmony_ci return -ENOTCONN; 29753a5a1b3Sopenharmony_ci 29853a5a1b3Sopenharmony_ci memset(&pfd, 0, sizeof(pfd)); 29953a5a1b3Sopenharmony_ci pfd.fd = sock; 30053a5a1b3Sopenharmony_ci pfd.events = POLLOUT; 30153a5a1b3Sopenharmony_ci 30253a5a1b3Sopenharmony_ci if (poll(&pfd, 1, 0) < 0) 30353a5a1b3Sopenharmony_ci return -errno; 30453a5a1b3Sopenharmony_ci 30553a5a1b3Sopenharmony_ci /* 30653a5a1b3Sopenharmony_ci * If socket already writable then it is not in defer setup state, 30753a5a1b3Sopenharmony_ci * otherwise it needs to be read to authorize the connection. 30853a5a1b3Sopenharmony_ci */ 30953a5a1b3Sopenharmony_ci if ((pfd.revents & POLLOUT)) 31053a5a1b3Sopenharmony_ci return 0; 31153a5a1b3Sopenharmony_ci 31253a5a1b3Sopenharmony_ci /* Enable socket by reading 1 byte */ 31353a5a1b3Sopenharmony_ci if (read(sock, &c, 1) < 0) 31453a5a1b3Sopenharmony_ci return -errno; 31553a5a1b3Sopenharmony_ci 31653a5a1b3Sopenharmony_ci return 0; 31753a5a1b3Sopenharmony_ci} 31853a5a1b3Sopenharmony_ci 31953a5a1b3Sopenharmony_cistatic int hf_audio_agent_transport_acquire(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu) { 32053a5a1b3Sopenharmony_ci struct hf_audio_card *card = t->userdata; 32153a5a1b3Sopenharmony_ci int err; 32253a5a1b3Sopenharmony_ci 32353a5a1b3Sopenharmony_ci pa_assert(card); 32453a5a1b3Sopenharmony_ci 32553a5a1b3Sopenharmony_ci if (!optional && card->fd < 0) { 32653a5a1b3Sopenharmony_ci err = card->acquire(card); 32753a5a1b3Sopenharmony_ci if (err < 0) 32853a5a1b3Sopenharmony_ci return err; 32953a5a1b3Sopenharmony_ci } 33053a5a1b3Sopenharmony_ci 33153a5a1b3Sopenharmony_ci /* The correct block size should take into account the SCO MTU from 33253a5a1b3Sopenharmony_ci * the Bluetooth adapter and (for adapters in the USB bus) the MxPS 33353a5a1b3Sopenharmony_ci * value from the Isoc USB endpoint in use by btusb and should be 33453a5a1b3Sopenharmony_ci * made available to userspace by the Bluetooth kernel subsystem. 33553a5a1b3Sopenharmony_ci * 33653a5a1b3Sopenharmony_ci * Set initial MTU to max known payload length of HCI packet 33753a5a1b3Sopenharmony_ci * in USB Alternate Setting 5 (144 bytes) 33853a5a1b3Sopenharmony_ci * See also pa_bluetooth_transport::last_read_size handling 33953a5a1b3Sopenharmony_ci * and comment about MTU size in bt_prepare_encoder_buffer() 34053a5a1b3Sopenharmony_ci */ 34153a5a1b3Sopenharmony_ci if (imtu) 34253a5a1b3Sopenharmony_ci *imtu = 144; 34353a5a1b3Sopenharmony_ci if (omtu) 34453a5a1b3Sopenharmony_ci *omtu = 144; 34553a5a1b3Sopenharmony_ci 34653a5a1b3Sopenharmony_ci err = socket_accept(card->fd); 34753a5a1b3Sopenharmony_ci if (err < 0) { 34853a5a1b3Sopenharmony_ci pa_log_error("Deferred setup failed on fd %d: %s", card->fd, pa_cstrerror(-err)); 34953a5a1b3Sopenharmony_ci return -1; 35053a5a1b3Sopenharmony_ci } 35153a5a1b3Sopenharmony_ci 35253a5a1b3Sopenharmony_ci return card->fd; 35353a5a1b3Sopenharmony_ci} 35453a5a1b3Sopenharmony_ci 35553a5a1b3Sopenharmony_cistatic void hf_audio_agent_transport_release(pa_bluetooth_transport *t) { 35653a5a1b3Sopenharmony_ci struct hf_audio_card *card = t->userdata; 35753a5a1b3Sopenharmony_ci 35853a5a1b3Sopenharmony_ci pa_assert(card); 35953a5a1b3Sopenharmony_ci 36053a5a1b3Sopenharmony_ci if (card->fd < 0) { 36153a5a1b3Sopenharmony_ci pa_log_info("Transport %s already released", t->path); 36253a5a1b3Sopenharmony_ci return; 36353a5a1b3Sopenharmony_ci } 36453a5a1b3Sopenharmony_ci 36553a5a1b3Sopenharmony_ci /* shutdown to make sure connection is dropped immediately */ 36653a5a1b3Sopenharmony_ci shutdown(card->fd, SHUT_RDWR); 36753a5a1b3Sopenharmony_ci close(card->fd); 36853a5a1b3Sopenharmony_ci card->fd = -1; 36953a5a1b3Sopenharmony_ci} 37053a5a1b3Sopenharmony_ci 37153a5a1b3Sopenharmony_cistatic void hf_audio_agent_card_found(pa_bluetooth_backend *backend, const char *path, DBusMessageIter *props_i) { 37253a5a1b3Sopenharmony_ci DBusMessageIter i, value_i; 37353a5a1b3Sopenharmony_ci const char *key, *value; 37453a5a1b3Sopenharmony_ci struct hf_audio_card *card; 37553a5a1b3Sopenharmony_ci pa_bluetooth_device *d; 37653a5a1b3Sopenharmony_ci pa_bluetooth_profile_t p = PA_BLUETOOTH_PROFILE_HFP_AG; 37753a5a1b3Sopenharmony_ci 37853a5a1b3Sopenharmony_ci pa_assert(backend); 37953a5a1b3Sopenharmony_ci pa_assert(path); 38053a5a1b3Sopenharmony_ci pa_assert(props_i); 38153a5a1b3Sopenharmony_ci 38253a5a1b3Sopenharmony_ci pa_log_debug("New HF card found: %s", path); 38353a5a1b3Sopenharmony_ci 38453a5a1b3Sopenharmony_ci card = hf_audio_card_new(backend, path); 38553a5a1b3Sopenharmony_ci 38653a5a1b3Sopenharmony_ci while (dbus_message_iter_get_arg_type(props_i) != DBUS_TYPE_INVALID) { 38753a5a1b3Sopenharmony_ci char c; 38853a5a1b3Sopenharmony_ci 38953a5a1b3Sopenharmony_ci dbus_message_iter_recurse(props_i, &i); 39053a5a1b3Sopenharmony_ci 39153a5a1b3Sopenharmony_ci dbus_message_iter_get_basic(&i, &key); 39253a5a1b3Sopenharmony_ci dbus_message_iter_next(&i); 39353a5a1b3Sopenharmony_ci dbus_message_iter_recurse(&i, &value_i); 39453a5a1b3Sopenharmony_ci 39553a5a1b3Sopenharmony_ci if ((c = dbus_message_iter_get_arg_type(&value_i)) != DBUS_TYPE_STRING) { 39653a5a1b3Sopenharmony_ci pa_log_error("Invalid properties for %s: expected 's', received '%c'", path, c); 39753a5a1b3Sopenharmony_ci goto fail; 39853a5a1b3Sopenharmony_ci } 39953a5a1b3Sopenharmony_ci 40053a5a1b3Sopenharmony_ci dbus_message_iter_get_basic(&value_i, &value); 40153a5a1b3Sopenharmony_ci 40253a5a1b3Sopenharmony_ci if (pa_streq(key, "RemoteAddress")) { 40353a5a1b3Sopenharmony_ci pa_xfree(card->remote_address); 40453a5a1b3Sopenharmony_ci card->remote_address = pa_xstrdup(value); 40553a5a1b3Sopenharmony_ci } else if (pa_streq(key, "LocalAddress")) { 40653a5a1b3Sopenharmony_ci pa_xfree(card->local_address); 40753a5a1b3Sopenharmony_ci card->local_address = pa_xstrdup(value); 40853a5a1b3Sopenharmony_ci } else if (pa_streq(key, "Type")) { 40953a5a1b3Sopenharmony_ci if (pa_streq(value, "gateway")) 41053a5a1b3Sopenharmony_ci p = PA_BLUETOOTH_PROFILE_HFP_HF; 41153a5a1b3Sopenharmony_ci } 41253a5a1b3Sopenharmony_ci 41353a5a1b3Sopenharmony_ci pa_log_debug("%s: %s", key, value); 41453a5a1b3Sopenharmony_ci 41553a5a1b3Sopenharmony_ci dbus_message_iter_next(props_i); 41653a5a1b3Sopenharmony_ci } 41753a5a1b3Sopenharmony_ci 41853a5a1b3Sopenharmony_ci d = pa_bluetooth_discovery_get_device_by_address(backend->discovery, card->remote_address, card->local_address); 41953a5a1b3Sopenharmony_ci if (!d) { 42053a5a1b3Sopenharmony_ci pa_log_error("Device doesn't exist for %s", path); 42153a5a1b3Sopenharmony_ci goto fail; 42253a5a1b3Sopenharmony_ci } 42353a5a1b3Sopenharmony_ci 42453a5a1b3Sopenharmony_ci card->transport = pa_bluetooth_transport_new(d, backend->ofono_bus_id, path, p, NULL, 0); 42553a5a1b3Sopenharmony_ci card->transport->acquire = hf_audio_agent_transport_acquire; 42653a5a1b3Sopenharmony_ci card->transport->release = hf_audio_agent_transport_release; 42753a5a1b3Sopenharmony_ci card->transport->userdata = card; 42853a5a1b3Sopenharmony_ci pa_bluetooth_transport_reconfigure(card->transport, pa_bluetooth_get_hf_codec("CVSD"), sco_transport_write, NULL); 42953a5a1b3Sopenharmony_ci 43053a5a1b3Sopenharmony_ci pa_bluetooth_transport_put(card->transport); 43153a5a1b3Sopenharmony_ci pa_hashmap_put(backend->cards, card->path, card); 43253a5a1b3Sopenharmony_ci 43353a5a1b3Sopenharmony_ci return; 43453a5a1b3Sopenharmony_ci 43553a5a1b3Sopenharmony_cifail: 43653a5a1b3Sopenharmony_ci hf_audio_card_free(card); 43753a5a1b3Sopenharmony_ci} 43853a5a1b3Sopenharmony_ci 43953a5a1b3Sopenharmony_cistatic void hf_audio_agent_card_removed(pa_bluetooth_backend *backend, const char *path) { 44053a5a1b3Sopenharmony_ci struct hf_audio_card *card; 44153a5a1b3Sopenharmony_ci 44253a5a1b3Sopenharmony_ci pa_assert(backend); 44353a5a1b3Sopenharmony_ci pa_assert(path); 44453a5a1b3Sopenharmony_ci 44553a5a1b3Sopenharmony_ci pa_log_debug("HF card removed: %s", path); 44653a5a1b3Sopenharmony_ci 44753a5a1b3Sopenharmony_ci card = pa_hashmap_remove(backend->cards, path); 44853a5a1b3Sopenharmony_ci if (!card) 44953a5a1b3Sopenharmony_ci return; 45053a5a1b3Sopenharmony_ci 45153a5a1b3Sopenharmony_ci hf_audio_card_free(card); 45253a5a1b3Sopenharmony_ci} 45353a5a1b3Sopenharmony_ci 45453a5a1b3Sopenharmony_cistatic void hf_audio_agent_get_cards_reply(DBusPendingCall *pending, void *userdata) { 45553a5a1b3Sopenharmony_ci DBusMessage *r; 45653a5a1b3Sopenharmony_ci pa_dbus_pending *p; 45753a5a1b3Sopenharmony_ci pa_bluetooth_backend *backend; 45853a5a1b3Sopenharmony_ci DBusMessageIter i, array_i, struct_i, props_i; 45953a5a1b3Sopenharmony_ci 46053a5a1b3Sopenharmony_ci pa_assert_se(p = userdata); 46153a5a1b3Sopenharmony_ci pa_assert_se(backend = p->context_data); 46253a5a1b3Sopenharmony_ci pa_assert_se(r = dbus_pending_call_steal_reply(pending)); 46353a5a1b3Sopenharmony_ci 46453a5a1b3Sopenharmony_ci if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) { 46553a5a1b3Sopenharmony_ci pa_log_error("Failed to get a list of handsfree audio cards from ofono: %s: %s", 46653a5a1b3Sopenharmony_ci dbus_message_get_error_name(r), pa_dbus_get_error_message(r)); 46753a5a1b3Sopenharmony_ci goto finish; 46853a5a1b3Sopenharmony_ci } 46953a5a1b3Sopenharmony_ci 47053a5a1b3Sopenharmony_ci if (!dbus_message_iter_init(r, &i) || !pa_streq(dbus_message_get_signature(r), "a(oa{sv})")) { 47153a5a1b3Sopenharmony_ci pa_log_error("Invalid arguments in GetCards() reply"); 47253a5a1b3Sopenharmony_ci goto finish; 47353a5a1b3Sopenharmony_ci } 47453a5a1b3Sopenharmony_ci 47553a5a1b3Sopenharmony_ci dbus_message_iter_recurse(&i, &array_i); 47653a5a1b3Sopenharmony_ci while (dbus_message_iter_get_arg_type(&array_i) != DBUS_TYPE_INVALID) { 47753a5a1b3Sopenharmony_ci const char *path; 47853a5a1b3Sopenharmony_ci 47953a5a1b3Sopenharmony_ci dbus_message_iter_recurse(&array_i, &struct_i); 48053a5a1b3Sopenharmony_ci dbus_message_iter_get_basic(&struct_i, &path); 48153a5a1b3Sopenharmony_ci dbus_message_iter_next(&struct_i); 48253a5a1b3Sopenharmony_ci 48353a5a1b3Sopenharmony_ci dbus_message_iter_recurse(&struct_i, &props_i); 48453a5a1b3Sopenharmony_ci 48553a5a1b3Sopenharmony_ci hf_audio_agent_card_found(backend, path, &props_i); 48653a5a1b3Sopenharmony_ci 48753a5a1b3Sopenharmony_ci dbus_message_iter_next(&array_i); 48853a5a1b3Sopenharmony_ci } 48953a5a1b3Sopenharmony_ci 49053a5a1b3Sopenharmony_cifinish: 49153a5a1b3Sopenharmony_ci dbus_message_unref(r); 49253a5a1b3Sopenharmony_ci 49353a5a1b3Sopenharmony_ci PA_LLIST_REMOVE(pa_dbus_pending, backend->pending, p); 49453a5a1b3Sopenharmony_ci pa_dbus_pending_free(p); 49553a5a1b3Sopenharmony_ci} 49653a5a1b3Sopenharmony_ci 49753a5a1b3Sopenharmony_cistatic void hf_audio_agent_get_cards(pa_bluetooth_backend *hf) { 49853a5a1b3Sopenharmony_ci DBusMessage *m; 49953a5a1b3Sopenharmony_ci 50053a5a1b3Sopenharmony_ci pa_assert(hf); 50153a5a1b3Sopenharmony_ci 50253a5a1b3Sopenharmony_ci pa_assert_se(m = dbus_message_new_method_call(OFONO_SERVICE, "/", HF_AUDIO_MANAGER_INTERFACE, "GetCards")); 50353a5a1b3Sopenharmony_ci hf_dbus_send_and_add_to_pending(hf, m, hf_audio_agent_get_cards_reply, NULL); 50453a5a1b3Sopenharmony_ci} 50553a5a1b3Sopenharmony_ci 50653a5a1b3Sopenharmony_cistatic void ofono_bus_id_destroy(pa_bluetooth_backend *backend) { 50753a5a1b3Sopenharmony_ci pa_hashmap_remove_all(backend->cards); 50853a5a1b3Sopenharmony_ci 50953a5a1b3Sopenharmony_ci if (backend->ofono_bus_id) { 51053a5a1b3Sopenharmony_ci pa_xfree(backend->ofono_bus_id); 51153a5a1b3Sopenharmony_ci backend->ofono_bus_id = NULL; 51253a5a1b3Sopenharmony_ci pa_bluetooth_discovery_set_ofono_running(backend->discovery, false); 51353a5a1b3Sopenharmony_ci } 51453a5a1b3Sopenharmony_ci} 51553a5a1b3Sopenharmony_ci 51653a5a1b3Sopenharmony_cistatic void hf_audio_agent_register_reply(DBusPendingCall *pending, void *userdata) { 51753a5a1b3Sopenharmony_ci DBusMessage *r; 51853a5a1b3Sopenharmony_ci pa_dbus_pending *p; 51953a5a1b3Sopenharmony_ci pa_bluetooth_backend *backend; 52053a5a1b3Sopenharmony_ci 52153a5a1b3Sopenharmony_ci pa_assert_se(p = userdata); 52253a5a1b3Sopenharmony_ci pa_assert_se(backend = p->context_data); 52353a5a1b3Sopenharmony_ci pa_assert_se(r = dbus_pending_call_steal_reply(pending)); 52453a5a1b3Sopenharmony_ci 52553a5a1b3Sopenharmony_ci if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) { 52653a5a1b3Sopenharmony_ci pa_log_info("Failed to register as a handsfree audio agent with ofono: %s: %s", 52753a5a1b3Sopenharmony_ci dbus_message_get_error_name(r), pa_dbus_get_error_message(r)); 52853a5a1b3Sopenharmony_ci goto finish; 52953a5a1b3Sopenharmony_ci } 53053a5a1b3Sopenharmony_ci 53153a5a1b3Sopenharmony_ci backend->ofono_bus_id = pa_xstrdup(dbus_message_get_sender(r)); 53253a5a1b3Sopenharmony_ci 53353a5a1b3Sopenharmony_ci hf_audio_agent_get_cards(backend); 53453a5a1b3Sopenharmony_ci 53553a5a1b3Sopenharmony_cifinish: 53653a5a1b3Sopenharmony_ci dbus_message_unref(r); 53753a5a1b3Sopenharmony_ci 53853a5a1b3Sopenharmony_ci PA_LLIST_REMOVE(pa_dbus_pending, backend->pending, p); 53953a5a1b3Sopenharmony_ci pa_dbus_pending_free(p); 54053a5a1b3Sopenharmony_ci 54153a5a1b3Sopenharmony_ci pa_bluetooth_discovery_set_ofono_running(backend->discovery, backend->ofono_bus_id != NULL); 54253a5a1b3Sopenharmony_ci} 54353a5a1b3Sopenharmony_ci 54453a5a1b3Sopenharmony_cistatic void hf_audio_agent_register(pa_bluetooth_backend *hf) { 54553a5a1b3Sopenharmony_ci DBusMessage *m; 54653a5a1b3Sopenharmony_ci uint8_t codecs[2]; 54753a5a1b3Sopenharmony_ci const uint8_t *pcodecs = codecs; 54853a5a1b3Sopenharmony_ci int ncodecs = 0; 54953a5a1b3Sopenharmony_ci const char *path = HF_AUDIO_AGENT_PATH; 55053a5a1b3Sopenharmony_ci 55153a5a1b3Sopenharmony_ci pa_assert(hf); 55253a5a1b3Sopenharmony_ci 55353a5a1b3Sopenharmony_ci pa_assert_se(m = dbus_message_new_method_call(OFONO_SERVICE, "/", HF_AUDIO_MANAGER_INTERFACE, "Register")); 55453a5a1b3Sopenharmony_ci 55553a5a1b3Sopenharmony_ci codecs[ncodecs++] = HFP_AUDIO_CODEC_CVSD; 55653a5a1b3Sopenharmony_ci if (pa_bluetooth_discovery_get_enable_msbc(hf->discovery)) 55753a5a1b3Sopenharmony_ci codecs[ncodecs++] = HFP_AUDIO_CODEC_MSBC; 55853a5a1b3Sopenharmony_ci 55953a5a1b3Sopenharmony_ci pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &pcodecs, ncodecs, 56053a5a1b3Sopenharmony_ci DBUS_TYPE_INVALID)); 56153a5a1b3Sopenharmony_ci 56253a5a1b3Sopenharmony_ci hf_dbus_send_and_add_to_pending(hf, m, hf_audio_agent_register_reply, NULL); 56353a5a1b3Sopenharmony_ci} 56453a5a1b3Sopenharmony_ci 56553a5a1b3Sopenharmony_cistatic void hf_audio_agent_unregister(pa_bluetooth_backend *backend) { 56653a5a1b3Sopenharmony_ci DBusMessage *m; 56753a5a1b3Sopenharmony_ci const char *path = HF_AUDIO_AGENT_PATH; 56853a5a1b3Sopenharmony_ci 56953a5a1b3Sopenharmony_ci pa_assert(backend); 57053a5a1b3Sopenharmony_ci pa_assert(backend->connection); 57153a5a1b3Sopenharmony_ci 57253a5a1b3Sopenharmony_ci if (backend->ofono_bus_id) { 57353a5a1b3Sopenharmony_ci pa_assert_se(m = dbus_message_new_method_call(backend->ofono_bus_id, "/", HF_AUDIO_MANAGER_INTERFACE, "Unregister")); 57453a5a1b3Sopenharmony_ci pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)); 57553a5a1b3Sopenharmony_ci pa_assert_se(dbus_connection_send(pa_dbus_connection_get(backend->connection), m, NULL)); 57653a5a1b3Sopenharmony_ci 57753a5a1b3Sopenharmony_ci ofono_bus_id_destroy(backend); 57853a5a1b3Sopenharmony_ci } 57953a5a1b3Sopenharmony_ci} 58053a5a1b3Sopenharmony_ci 58153a5a1b3Sopenharmony_cistatic DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *data) { 58253a5a1b3Sopenharmony_ci const char *sender; 58353a5a1b3Sopenharmony_ci DBusError err; 58453a5a1b3Sopenharmony_ci pa_bluetooth_backend *backend = data; 58553a5a1b3Sopenharmony_ci 58653a5a1b3Sopenharmony_ci pa_assert(bus); 58753a5a1b3Sopenharmony_ci pa_assert(m); 58853a5a1b3Sopenharmony_ci pa_assert(backend); 58953a5a1b3Sopenharmony_ci 59053a5a1b3Sopenharmony_ci sender = dbus_message_get_sender(m); 59153a5a1b3Sopenharmony_ci if (!pa_safe_streq(backend->ofono_bus_id, sender) && !pa_streq(DBUS_SERVICE_DBUS, sender)) 59253a5a1b3Sopenharmony_ci return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 59353a5a1b3Sopenharmony_ci 59453a5a1b3Sopenharmony_ci dbus_error_init(&err); 59553a5a1b3Sopenharmony_ci 59653a5a1b3Sopenharmony_ci if (dbus_message_is_signal(m, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) { 59753a5a1b3Sopenharmony_ci const char *name, *old_owner, *new_owner; 59853a5a1b3Sopenharmony_ci 59953a5a1b3Sopenharmony_ci if (!dbus_message_get_args(m, &err, 60053a5a1b3Sopenharmony_ci DBUS_TYPE_STRING, &name, 60153a5a1b3Sopenharmony_ci DBUS_TYPE_STRING, &old_owner, 60253a5a1b3Sopenharmony_ci DBUS_TYPE_STRING, &new_owner, 60353a5a1b3Sopenharmony_ci DBUS_TYPE_INVALID)) { 60453a5a1b3Sopenharmony_ci pa_log_error("Failed to parse " DBUS_INTERFACE_DBUS ".NameOwnerChanged: %s", err.message); 60553a5a1b3Sopenharmony_ci goto fail; 60653a5a1b3Sopenharmony_ci } 60753a5a1b3Sopenharmony_ci 60853a5a1b3Sopenharmony_ci if (pa_streq(name, OFONO_SERVICE)) { 60953a5a1b3Sopenharmony_ci 61053a5a1b3Sopenharmony_ci if (old_owner && *old_owner) { 61153a5a1b3Sopenharmony_ci pa_log_debug("oFono disappeared"); 61253a5a1b3Sopenharmony_ci ofono_bus_id_destroy(backend); 61353a5a1b3Sopenharmony_ci } 61453a5a1b3Sopenharmony_ci 61553a5a1b3Sopenharmony_ci if (new_owner && *new_owner) { 61653a5a1b3Sopenharmony_ci pa_log_debug("oFono appeared"); 61753a5a1b3Sopenharmony_ci hf_audio_agent_register(backend); 61853a5a1b3Sopenharmony_ci } 61953a5a1b3Sopenharmony_ci } 62053a5a1b3Sopenharmony_ci 62153a5a1b3Sopenharmony_ci } else if (dbus_message_is_signal(m, "org.ofono.HandsfreeAudioManager", "CardAdded")) { 62253a5a1b3Sopenharmony_ci const char *p; 62353a5a1b3Sopenharmony_ci DBusMessageIter arg_i, props_i; 62453a5a1b3Sopenharmony_ci 62553a5a1b3Sopenharmony_ci if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "oa{sv}")) { 62653a5a1b3Sopenharmony_ci pa_log_error("Failed to parse org.ofono.HandsfreeAudioManager.CardAdded"); 62753a5a1b3Sopenharmony_ci goto fail; 62853a5a1b3Sopenharmony_ci } 62953a5a1b3Sopenharmony_ci 63053a5a1b3Sopenharmony_ci dbus_message_iter_get_basic(&arg_i, &p); 63153a5a1b3Sopenharmony_ci 63253a5a1b3Sopenharmony_ci pa_assert_se(dbus_message_iter_next(&arg_i)); 63353a5a1b3Sopenharmony_ci pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_ARRAY); 63453a5a1b3Sopenharmony_ci 63553a5a1b3Sopenharmony_ci dbus_message_iter_recurse(&arg_i, &props_i); 63653a5a1b3Sopenharmony_ci 63753a5a1b3Sopenharmony_ci hf_audio_agent_card_found(backend, p, &props_i); 63853a5a1b3Sopenharmony_ci } else if (dbus_message_is_signal(m, "org.ofono.HandsfreeAudioManager", "CardRemoved")) { 63953a5a1b3Sopenharmony_ci const char *p; 64053a5a1b3Sopenharmony_ci 64153a5a1b3Sopenharmony_ci if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &p, DBUS_TYPE_INVALID)) { 64253a5a1b3Sopenharmony_ci pa_log_error("Failed to parse org.ofono.HandsfreeAudioManager.CardRemoved: %s", err.message); 64353a5a1b3Sopenharmony_ci goto fail; 64453a5a1b3Sopenharmony_ci } 64553a5a1b3Sopenharmony_ci 64653a5a1b3Sopenharmony_ci hf_audio_agent_card_removed(backend, p); 64753a5a1b3Sopenharmony_ci } 64853a5a1b3Sopenharmony_ci 64953a5a1b3Sopenharmony_cifail: 65053a5a1b3Sopenharmony_ci dbus_error_free(&err); 65153a5a1b3Sopenharmony_ci return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 65253a5a1b3Sopenharmony_ci} 65353a5a1b3Sopenharmony_ci 65453a5a1b3Sopenharmony_cistatic DBusMessage *hf_audio_agent_release(DBusConnection *c, DBusMessage *m, void *data) { 65553a5a1b3Sopenharmony_ci DBusMessage *r; 65653a5a1b3Sopenharmony_ci const char *sender; 65753a5a1b3Sopenharmony_ci pa_bluetooth_backend *backend = data; 65853a5a1b3Sopenharmony_ci 65953a5a1b3Sopenharmony_ci pa_assert(backend); 66053a5a1b3Sopenharmony_ci 66153a5a1b3Sopenharmony_ci sender = dbus_message_get_sender(m); 66253a5a1b3Sopenharmony_ci if (!pa_safe_streq(backend->ofono_bus_id, sender)) { 66353a5a1b3Sopenharmony_ci pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.NotAllowed", "Operation is not allowed by this sender")); 66453a5a1b3Sopenharmony_ci return r; 66553a5a1b3Sopenharmony_ci } 66653a5a1b3Sopenharmony_ci 66753a5a1b3Sopenharmony_ci pa_log_debug("HF audio agent has been unregistered by oFono (%s)", backend->ofono_bus_id); 66853a5a1b3Sopenharmony_ci 66953a5a1b3Sopenharmony_ci ofono_bus_id_destroy(backend); 67053a5a1b3Sopenharmony_ci 67153a5a1b3Sopenharmony_ci pa_assert_se(r = dbus_message_new_method_return(m)); 67253a5a1b3Sopenharmony_ci 67353a5a1b3Sopenharmony_ci return r; 67453a5a1b3Sopenharmony_ci} 67553a5a1b3Sopenharmony_ci 67653a5a1b3Sopenharmony_cistatic DBusMessage *hf_audio_agent_new_connection(DBusConnection *c, DBusMessage *m, void *data) { 67753a5a1b3Sopenharmony_ci DBusMessage *r; 67853a5a1b3Sopenharmony_ci const char *sender, *path; 67953a5a1b3Sopenharmony_ci int fd; 68053a5a1b3Sopenharmony_ci uint8_t codec; 68153a5a1b3Sopenharmony_ci struct hf_audio_card *card; 68253a5a1b3Sopenharmony_ci pa_bluetooth_backend *backend = data; 68353a5a1b3Sopenharmony_ci 68453a5a1b3Sopenharmony_ci pa_assert(backend); 68553a5a1b3Sopenharmony_ci 68653a5a1b3Sopenharmony_ci sender = dbus_message_get_sender(m); 68753a5a1b3Sopenharmony_ci if (!pa_safe_streq(backend->ofono_bus_id, sender)) { 68853a5a1b3Sopenharmony_ci pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.NotAllowed", "Operation is not allowed by this sender")); 68953a5a1b3Sopenharmony_ci return r; 69053a5a1b3Sopenharmony_ci } 69153a5a1b3Sopenharmony_ci 69253a5a1b3Sopenharmony_ci if (dbus_message_get_args(m, NULL, 69353a5a1b3Sopenharmony_ci DBUS_TYPE_OBJECT_PATH, &path, 69453a5a1b3Sopenharmony_ci DBUS_TYPE_UNIX_FD, &fd, 69553a5a1b3Sopenharmony_ci DBUS_TYPE_BYTE, &codec, 69653a5a1b3Sopenharmony_ci DBUS_TYPE_INVALID) == FALSE) { 69753a5a1b3Sopenharmony_ci pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.InvalidArguments", "Invalid arguments in method call")); 69853a5a1b3Sopenharmony_ci return r; 69953a5a1b3Sopenharmony_ci } 70053a5a1b3Sopenharmony_ci 70153a5a1b3Sopenharmony_ci card = pa_hashmap_get(backend->cards, path); 70253a5a1b3Sopenharmony_ci 70353a5a1b3Sopenharmony_ci if (!card || (codec != HFP_AUDIO_CODEC_CVSD && codec != HFP_AUDIO_CODEC_MSBC) || card->fd >= 0) { 70453a5a1b3Sopenharmony_ci pa_log_warn("New audio connection invalid arguments (path=%s fd=%d, codec=%d)", path, fd, codec); 70553a5a1b3Sopenharmony_ci pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.InvalidArguments", "Invalid arguments in method call")); 70653a5a1b3Sopenharmony_ci shutdown(fd, SHUT_RDWR); 70753a5a1b3Sopenharmony_ci close(fd); 70853a5a1b3Sopenharmony_ci return r; 70953a5a1b3Sopenharmony_ci } 71053a5a1b3Sopenharmony_ci 71153a5a1b3Sopenharmony_ci pa_log_debug("New audio connection on card %s (fd=%d, codec=%d)", path, fd, codec); 71253a5a1b3Sopenharmony_ci 71353a5a1b3Sopenharmony_ci card->connecting = false; 71453a5a1b3Sopenharmony_ci card->fd = fd; 71553a5a1b3Sopenharmony_ci if (codec == HFP_AUDIO_CODEC_CVSD) { 71653a5a1b3Sopenharmony_ci pa_bluetooth_transport_reconfigure(card->transport, pa_bluetooth_get_hf_codec("CVSD"), sco_transport_write, NULL); 71753a5a1b3Sopenharmony_ci } else if (codec == HFP_AUDIO_CODEC_MSBC) { 71853a5a1b3Sopenharmony_ci /* oFono is expected to set up socket BT_VOICE_TRANSPARENT option */ 71953a5a1b3Sopenharmony_ci pa_bluetooth_transport_reconfigure(card->transport, pa_bluetooth_get_hf_codec("mSBC"), sco_transport_write, NULL); 72053a5a1b3Sopenharmony_ci } 72153a5a1b3Sopenharmony_ci 72253a5a1b3Sopenharmony_ci pa_bluetooth_transport_set_state(card->transport, PA_BLUETOOTH_TRANSPORT_STATE_PLAYING); 72353a5a1b3Sopenharmony_ci 72453a5a1b3Sopenharmony_ci pa_assert_se(r = dbus_message_new_method_return(m)); 72553a5a1b3Sopenharmony_ci 72653a5a1b3Sopenharmony_ci return r; 72753a5a1b3Sopenharmony_ci} 72853a5a1b3Sopenharmony_ci 72953a5a1b3Sopenharmony_cistatic DBusHandlerResult hf_audio_agent_handler(DBusConnection *c, DBusMessage *m, void *data) { 73053a5a1b3Sopenharmony_ci pa_bluetooth_backend *backend = data; 73153a5a1b3Sopenharmony_ci DBusMessage *r = NULL; 73253a5a1b3Sopenharmony_ci const char *path, *interface, *member; 73353a5a1b3Sopenharmony_ci 73453a5a1b3Sopenharmony_ci pa_assert(backend); 73553a5a1b3Sopenharmony_ci 73653a5a1b3Sopenharmony_ci path = dbus_message_get_path(m); 73753a5a1b3Sopenharmony_ci interface = dbus_message_get_interface(m); 73853a5a1b3Sopenharmony_ci member = dbus_message_get_member(m); 73953a5a1b3Sopenharmony_ci 74053a5a1b3Sopenharmony_ci if (!pa_streq(path, HF_AUDIO_AGENT_PATH)) 74153a5a1b3Sopenharmony_ci return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 74253a5a1b3Sopenharmony_ci 74353a5a1b3Sopenharmony_ci pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member); 74453a5a1b3Sopenharmony_ci 74553a5a1b3Sopenharmony_ci if (dbus_message_is_method_call(m, DBUS_INTERFACE_INTROSPECTABLE, "Introspect")) { 74653a5a1b3Sopenharmony_ci const char *xml = HF_AUDIO_AGENT_XML; 74753a5a1b3Sopenharmony_ci 74853a5a1b3Sopenharmony_ci pa_assert_se(r = dbus_message_new_method_return(m)); 74953a5a1b3Sopenharmony_ci pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID)); 75053a5a1b3Sopenharmony_ci 75153a5a1b3Sopenharmony_ci } else if (dbus_message_is_method_call(m, HF_AUDIO_AGENT_INTERFACE, "NewConnection")) 75253a5a1b3Sopenharmony_ci r = hf_audio_agent_new_connection(c, m, data); 75353a5a1b3Sopenharmony_ci else if (dbus_message_is_method_call(m, HF_AUDIO_AGENT_INTERFACE, "Release")) 75453a5a1b3Sopenharmony_ci r = hf_audio_agent_release(c, m, data); 75553a5a1b3Sopenharmony_ci else 75653a5a1b3Sopenharmony_ci return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 75753a5a1b3Sopenharmony_ci 75853a5a1b3Sopenharmony_ci if (r) { 75953a5a1b3Sopenharmony_ci pa_assert_se(dbus_connection_send(pa_dbus_connection_get(backend->connection), r, NULL)); 76053a5a1b3Sopenharmony_ci dbus_message_unref(r); 76153a5a1b3Sopenharmony_ci } 76253a5a1b3Sopenharmony_ci 76353a5a1b3Sopenharmony_ci return DBUS_HANDLER_RESULT_HANDLED; 76453a5a1b3Sopenharmony_ci} 76553a5a1b3Sopenharmony_ci 76653a5a1b3Sopenharmony_cipa_bluetooth_backend *pa_bluetooth_ofono_backend_new(pa_core *c, pa_bluetooth_discovery *y) { 76753a5a1b3Sopenharmony_ci pa_bluetooth_backend *backend; 76853a5a1b3Sopenharmony_ci DBusError err; 76953a5a1b3Sopenharmony_ci static const DBusObjectPathVTable vtable_hf_audio_agent = { 77053a5a1b3Sopenharmony_ci .message_function = hf_audio_agent_handler, 77153a5a1b3Sopenharmony_ci }; 77253a5a1b3Sopenharmony_ci 77353a5a1b3Sopenharmony_ci pa_assert(c); 77453a5a1b3Sopenharmony_ci 77553a5a1b3Sopenharmony_ci backend = pa_xnew0(pa_bluetooth_backend, 1); 77653a5a1b3Sopenharmony_ci backend->core = c; 77753a5a1b3Sopenharmony_ci backend->discovery = y; 77853a5a1b3Sopenharmony_ci backend->cards = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, 77953a5a1b3Sopenharmony_ci (pa_free_cb_t) hf_audio_card_free); 78053a5a1b3Sopenharmony_ci 78153a5a1b3Sopenharmony_ci dbus_error_init(&err); 78253a5a1b3Sopenharmony_ci 78353a5a1b3Sopenharmony_ci if (!(backend->connection = pa_dbus_bus_get(c, DBUS_BUS_SYSTEM, &err))) { 78453a5a1b3Sopenharmony_ci pa_log("Failed to get D-Bus connection: %s", err.message); 78553a5a1b3Sopenharmony_ci dbus_error_free(&err); 78653a5a1b3Sopenharmony_ci pa_xfree(backend); 78753a5a1b3Sopenharmony_ci return NULL; 78853a5a1b3Sopenharmony_ci } 78953a5a1b3Sopenharmony_ci 79053a5a1b3Sopenharmony_ci /* dynamic detection of handsfree audio cards */ 79153a5a1b3Sopenharmony_ci if (!dbus_connection_add_filter(pa_dbus_connection_get(backend->connection), filter_cb, backend, NULL)) { 79253a5a1b3Sopenharmony_ci pa_log_error("Failed to add filter function"); 79353a5a1b3Sopenharmony_ci pa_dbus_connection_unref(backend->connection); 79453a5a1b3Sopenharmony_ci pa_xfree(backend); 79553a5a1b3Sopenharmony_ci return NULL; 79653a5a1b3Sopenharmony_ci } 79753a5a1b3Sopenharmony_ci 79853a5a1b3Sopenharmony_ci if (pa_dbus_add_matches(pa_dbus_connection_get(backend->connection), &err, 79953a5a1b3Sopenharmony_ci "type='signal',sender='" DBUS_SERVICE_DBUS "',interface='" DBUS_INTERFACE_DBUS "',member='NameOwnerChanged'," 80053a5a1b3Sopenharmony_ci "arg0='" OFONO_SERVICE "'", 80153a5a1b3Sopenharmony_ci "type='signal',sender='" OFONO_SERVICE "',interface='" HF_AUDIO_MANAGER_INTERFACE "',member='CardAdded'", 80253a5a1b3Sopenharmony_ci "type='signal',sender='" OFONO_SERVICE "',interface='" HF_AUDIO_MANAGER_INTERFACE "',member='CardRemoved'", 80353a5a1b3Sopenharmony_ci NULL) < 0) { 80453a5a1b3Sopenharmony_ci pa_log("Failed to add oFono D-Bus matches: %s", err.message); 80553a5a1b3Sopenharmony_ci dbus_connection_remove_filter(pa_dbus_connection_get(backend->connection), filter_cb, backend); 80653a5a1b3Sopenharmony_ci pa_dbus_connection_unref(backend->connection); 80753a5a1b3Sopenharmony_ci pa_xfree(backend); 80853a5a1b3Sopenharmony_ci return NULL; 80953a5a1b3Sopenharmony_ci } 81053a5a1b3Sopenharmony_ci 81153a5a1b3Sopenharmony_ci pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(backend->connection), HF_AUDIO_AGENT_PATH, 81253a5a1b3Sopenharmony_ci &vtable_hf_audio_agent, backend)); 81353a5a1b3Sopenharmony_ci 81453a5a1b3Sopenharmony_ci hf_audio_agent_register(backend); 81553a5a1b3Sopenharmony_ci 81653a5a1b3Sopenharmony_ci return backend; 81753a5a1b3Sopenharmony_ci} 81853a5a1b3Sopenharmony_ci 81953a5a1b3Sopenharmony_civoid pa_bluetooth_ofono_backend_free(pa_bluetooth_backend *backend) { 82053a5a1b3Sopenharmony_ci pa_assert(backend); 82153a5a1b3Sopenharmony_ci 82253a5a1b3Sopenharmony_ci pa_dbus_free_pending_list(&backend->pending); 82353a5a1b3Sopenharmony_ci 82453a5a1b3Sopenharmony_ci hf_audio_agent_unregister(backend); 82553a5a1b3Sopenharmony_ci 82653a5a1b3Sopenharmony_ci dbus_connection_unregister_object_path(pa_dbus_connection_get(backend->connection), HF_AUDIO_AGENT_PATH); 82753a5a1b3Sopenharmony_ci 82853a5a1b3Sopenharmony_ci pa_dbus_remove_matches(pa_dbus_connection_get(backend->connection), 82953a5a1b3Sopenharmony_ci "type='signal',sender='" DBUS_SERVICE_DBUS "',interface='" DBUS_INTERFACE_DBUS "',member='NameOwnerChanged'," 83053a5a1b3Sopenharmony_ci "arg0='" OFONO_SERVICE "'", 83153a5a1b3Sopenharmony_ci "type='signal',sender='" OFONO_SERVICE "',interface='" HF_AUDIO_MANAGER_INTERFACE "',member='CardAdded'", 83253a5a1b3Sopenharmony_ci "type='signal',sender='" OFONO_SERVICE "',interface='" HF_AUDIO_MANAGER_INTERFACE "',member='CardRemoved'", 83353a5a1b3Sopenharmony_ci NULL); 83453a5a1b3Sopenharmony_ci 83553a5a1b3Sopenharmony_ci dbus_connection_remove_filter(pa_dbus_connection_get(backend->connection), filter_cb, backend); 83653a5a1b3Sopenharmony_ci 83753a5a1b3Sopenharmony_ci pa_dbus_connection_unref(backend->connection); 83853a5a1b3Sopenharmony_ci 83953a5a1b3Sopenharmony_ci pa_hashmap_free(backend->cards); 84053a5a1b3Sopenharmony_ci 84153a5a1b3Sopenharmony_ci pa_xfree(backend); 84253a5a1b3Sopenharmony_ci} 843