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