2023-02-08 18:12:00 +01:00
|
|
|
|
/* Spa oFono backend */
|
|
|
|
|
|
/* SPDX-FileCopyrightText: Copyright © 2020 Collabora Ltd. */
|
|
|
|
|
|
/* SPDX-License-Identifier: MIT */
|
2020-07-17 18:10:38 +02:00
|
|
|
|
|
|
|
|
|
|
#include <errno.h>
|
2020-07-21 11:00:57 +02:00
|
|
|
|
#include <unistd.h>
|
2021-04-08 15:38:25 +02:00
|
|
|
|
#include <poll.h>
|
2020-07-21 11:00:57 +02:00
|
|
|
|
#include <sys/socket.h>
|
|
|
|
|
|
|
|
|
|
|
|
#include <bluetooth/bluetooth.h>
|
|
|
|
|
|
#include <bluetooth/sco.h>
|
2020-07-17 18:10:38 +02:00
|
|
|
|
|
|
|
|
|
|
#include <dbus/dbus.h>
|
|
|
|
|
|
|
|
|
|
|
|
#include <spa/support/log.h>
|
|
|
|
|
|
#include <spa/support/loop.h>
|
|
|
|
|
|
#include <spa/support/dbus.h>
|
|
|
|
|
|
#include <spa/support/plugin.h>
|
2021-05-18 11:36:13 +10:00
|
|
|
|
#include <spa/utils/string.h>
|
2020-07-17 18:10:38 +02:00
|
|
|
|
#include <spa/utils/type.h>
|
bluez5: backend-ofono: don't do codec probe connections + add wait
Codec probe connections can trigger bad behavior from oFono if done when
device is busy (e.g. at connect), and they might be done at the same
time as A2DP transport is acquired which cannot work.
Also, oFono will not reply to DBus Acquire, if device does not complete
codec negotiation correctly. This is most likely to happen just after
device connect, when it is busy with other stuff (eg A2DP).
Remove codec probe connections altogether: instead, we guess mSBC if
mSBC is enabled and otherwise CVSD. If the guess turns out to be wrong,
which is unlikely (almost all devices have mSBC), we recreate the
transport with correct codec (from main loop, must not be done in
*_acquire because that can destroy nodes + unload the spa libs while
we're being called from there).
To avoid oFono DBus hangs at startup, add delay before marking the
profile connected, enforcing a time difference to A2DP operations.
2022-03-03 23:32:33 +02:00
|
|
|
|
#include <spa/utils/result.h>
|
2021-03-02 22:15:21 +02:00
|
|
|
|
#include <spa/param/audio/raw.h>
|
2023-12-30 01:08:31 +01:00
|
|
|
|
#include <spa-private/dbus-helpers.h>
|
2020-07-17 18:10:38 +02:00
|
|
|
|
|
|
|
|
|
|
#include "defs.h"
|
|
|
|
|
|
|
bluez5: backend-ofono: don't do codec probe connections + add wait
Codec probe connections can trigger bad behavior from oFono if done when
device is busy (e.g. at connect), and they might be done at the same
time as A2DP transport is acquired which cannot work.
Also, oFono will not reply to DBus Acquire, if device does not complete
codec negotiation correctly. This is most likely to happen just after
device connect, when it is busy with other stuff (eg A2DP).
Remove codec probe connections altogether: instead, we guess mSBC if
mSBC is enabled and otherwise CVSD. If the guess turns out to be wrong,
which is unlikely (almost all devices have mSBC), we recreate the
transport with correct codec (from main loop, must not be done in
*_acquire because that can destroy nodes + unload the spa libs while
we're being called from there).
To avoid oFono DBus hangs at startup, add delay before marking the
profile connected, enforcing a time difference to A2DP operations.
2022-03-03 23:32:33 +02:00
|
|
|
|
#define INITIAL_INTERVAL_NSEC (500 * SPA_NSEC_PER_MSEC)
|
|
|
|
|
|
#define ACTION_INTERVAL_NSEC (3000 * SPA_NSEC_PER_MSEC)
|
|
|
|
|
|
|
2023-12-23 21:07:45 +02:00
|
|
|
|
SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.bluez5.ofono");
|
2021-10-01 19:03:49 +03:00
|
|
|
|
#undef SPA_LOG_TOPIC_DEFAULT
|
|
|
|
|
|
#define SPA_LOG_TOPIC_DEFAULT &log_topic
|
2020-11-29 16:38:36 +01:00
|
|
|
|
|
2021-03-18 21:48:10 +02:00
|
|
|
|
struct impl {
|
|
|
|
|
|
struct spa_bt_backend this;
|
|
|
|
|
|
|
2020-07-17 18:10:38 +02:00
|
|
|
|
struct spa_bt_monitor *monitor;
|
|
|
|
|
|
|
|
|
|
|
|
struct spa_log *log;
|
|
|
|
|
|
struct spa_loop *main_loop;
|
bluez5: backend-ofono: don't do codec probe connections + add wait
Codec probe connections can trigger bad behavior from oFono if done when
device is busy (e.g. at connect), and they might be done at the same
time as A2DP transport is acquired which cannot work.
Also, oFono will not reply to DBus Acquire, if device does not complete
codec negotiation correctly. This is most likely to happen just after
device connect, when it is busy with other stuff (eg A2DP).
Remove codec probe connections altogether: instead, we guess mSBC if
mSBC is enabled and otherwise CVSD. If the guess turns out to be wrong,
which is unlikely (almost all devices have mSBC), we recreate the
transport with correct codec (from main loop, must not be done in
*_acquire because that can destroy nodes + unload the spa libs while
we're being called from there).
To avoid oFono DBus hangs at startup, add delay before marking the
profile connected, enforcing a time difference to A2DP operations.
2022-03-03 23:32:33 +02:00
|
|
|
|
struct spa_system *main_system;
|
2020-07-17 18:10:38 +02:00
|
|
|
|
struct spa_dbus *dbus;
|
bluez5: backend-ofono: don't do codec probe connections + add wait
Codec probe connections can trigger bad behavior from oFono if done when
device is busy (e.g. at connect), and they might be done at the same
time as A2DP transport is acquired which cannot work.
Also, oFono will not reply to DBus Acquire, if device does not complete
codec negotiation correctly. This is most likely to happen just after
device connect, when it is busy with other stuff (eg A2DP).
Remove codec probe connections altogether: instead, we guess mSBC if
mSBC is enabled and otherwise CVSD. If the guess turns out to be wrong,
which is unlikely (almost all devices have mSBC), we recreate the
transport with correct codec (from main loop, must not be done in
*_acquire because that can destroy nodes + unload the spa libs while
we're being called from there).
To avoid oFono DBus hangs at startup, add delay before marking the
profile connected, enforcing a time difference to A2DP operations.
2022-03-03 23:32:33 +02:00
|
|
|
|
struct spa_loop_utils *loop_utils;
|
2020-07-17 18:10:38 +02:00
|
|
|
|
DBusConnection *conn;
|
|
|
|
|
|
|
2021-01-24 15:15:27 +02:00
|
|
|
|
const struct spa_bt_quirks *quirks;
|
|
|
|
|
|
|
bluez5: backend-ofono: don't do codec probe connections + add wait
Codec probe connections can trigger bad behavior from oFono if done when
device is busy (e.g. at connect), and they might be done at the same
time as A2DP transport is acquired which cannot work.
Also, oFono will not reply to DBus Acquire, if device does not complete
codec negotiation correctly. This is most likely to happen just after
device connect, when it is busy with other stuff (eg A2DP).
Remove codec probe connections altogether: instead, we guess mSBC if
mSBC is enabled and otherwise CVSD. If the guess turns out to be wrong,
which is unlikely (almost all devices have mSBC), we recreate the
transport with correct codec (from main loop, must not be done in
*_acquire because that can destroy nodes + unload the spa libs while
we're being called from there).
To avoid oFono DBus hangs at startup, add delay before marking the
profile connected, enforcing a time difference to A2DP operations.
2022-03-03 23:32:33 +02:00
|
|
|
|
struct spa_source *timer;
|
|
|
|
|
|
|
2020-07-17 18:10:38 +02:00
|
|
|
|
unsigned int filters_added:1;
|
2020-12-08 15:19:17 +01:00
|
|
|
|
unsigned int msbc_supported:1;
|
2020-07-17 18:10:38 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
2021-03-18 22:46:56 +02:00
|
|
|
|
struct transport_data {
|
|
|
|
|
|
struct spa_source sco;
|
bluez5: backend-ofono: don't do codec probe connections + add wait
Codec probe connections can trigger bad behavior from oFono if done when
device is busy (e.g. at connect), and they might be done at the same
time as A2DP transport is acquired which cannot work.
Also, oFono will not reply to DBus Acquire, if device does not complete
codec negotiation correctly. This is most likely to happen just after
device connect, when it is busy with other stuff (eg A2DP).
Remove codec probe connections altogether: instead, we guess mSBC if
mSBC is enabled and otherwise CVSD. If the guess turns out to be wrong,
which is unlikely (almost all devices have mSBC), we recreate the
transport with correct codec (from main loop, must not be done in
*_acquire because that can destroy nodes + unload the spa libs while
we're being called from there).
To avoid oFono DBus hangs at startup, add delay before marking the
profile connected, enforcing a time difference to A2DP operations.
2022-03-03 23:32:33 +02:00
|
|
|
|
unsigned int broken:1;
|
|
|
|
|
|
unsigned int activated:1;
|
2021-03-18 22:46:56 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
2020-07-17 18:10:38 +02:00
|
|
|
|
#define OFONO_HF_AUDIO_MANAGER_INTERFACE OFONO_SERVICE ".HandsfreeAudioManager"
|
2020-07-21 11:00:57 +02:00
|
|
|
|
#define OFONO_HF_AUDIO_CARD_INTERFACE OFONO_SERVICE ".HandsfreeAudioCard"
|
2020-07-17 18:10:38 +02:00
|
|
|
|
#define OFONO_HF_AUDIO_AGENT_INTERFACE OFONO_SERVICE ".HandsfreeAudioAgent"
|
|
|
|
|
|
|
|
|
|
|
|
#define OFONO_AUDIO_CLIENT "/Profile/ofono"
|
|
|
|
|
|
|
|
|
|
|
|
#define OFONO_INTROSPECT_XML \
|
|
|
|
|
|
DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
|
|
|
|
|
|
"<node>" \
|
|
|
|
|
|
" <interface name=\"" OFONO_HF_AUDIO_AGENT_INTERFACE "\">" \
|
|
|
|
|
|
" <method name=\"Release\">" \
|
|
|
|
|
|
" </method>" \
|
|
|
|
|
|
" <method name=\"NewConnection\">" \
|
|
|
|
|
|
" <arg name=\"card\" direction=\"in\" type=\"o\"/>" \
|
|
|
|
|
|
" <arg name=\"fd\" direction=\"in\" type=\"h\"/>" \
|
|
|
|
|
|
" <arg name=\"codec\" direction=\"in\" type=\"b\"/>" \
|
|
|
|
|
|
" </method>" \
|
|
|
|
|
|
" </interface>" \
|
|
|
|
|
|
" <interface name=\"org.freedesktop.DBus.Introspectable\">" \
|
|
|
|
|
|
" <method name=\"Introspect\">" \
|
|
|
|
|
|
" <arg name=\"data\" type=\"s\" direction=\"out\"/>" \
|
|
|
|
|
|
" </method>" \
|
|
|
|
|
|
" </interface>" \
|
|
|
|
|
|
"</node>"
|
|
|
|
|
|
|
|
|
|
|
|
#define OFONO_ERROR_INVALID_ARGUMENTS "org.ofono.Error.InvalidArguments"
|
2021-03-18 22:46:56 +02:00
|
|
|
|
#define OFONO_ERROR_NOT_IMPLEMENTED "org.ofono.Error.NotImplemented"
|
2020-07-17 18:10:38 +02:00
|
|
|
|
#define OFONO_ERROR_IN_USE "org.ofono.Error.InUse"
|
2021-04-08 15:38:25 +02:00
|
|
|
|
#define OFONO_ERROR_FAILED "org.ofono.Error.Failed"
|
2020-07-17 18:10:38 +02:00
|
|
|
|
|
2021-03-18 21:48:10 +02:00
|
|
|
|
static void ofono_transport_get_mtu(struct impl *backend, struct spa_bt_transport *t)
|
2020-07-21 11:00:57 +02:00
|
|
|
|
{
|
2020-12-30 21:40:26 +02:00
|
|
|
|
struct sco_options sco_opt;
|
|
|
|
|
|
socklen_t len;
|
|
|
|
|
|
|
|
|
|
|
|
/* Fallback values */
|
2023-10-15 16:46:32 +03:00
|
|
|
|
t->read_mtu = 144;
|
|
|
|
|
|
t->write_mtu = 144;
|
2020-12-30 21:40:26 +02:00
|
|
|
|
|
|
|
|
|
|
len = sizeof(sco_opt);
|
|
|
|
|
|
memset(&sco_opt, 0, len);
|
|
|
|
|
|
|
|
|
|
|
|
if (getsockopt(t->fd, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0)
|
2023-10-15 16:46:32 +03:00
|
|
|
|
spa_log_warn(backend->log, "getsockopt(SCO_OPTIONS) failed: %d (%m)", errno);
|
2020-12-30 21:40:26 +02:00
|
|
|
|
else {
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_debug(backend->log, "autodetected mtu = %u", sco_opt.mtu);
|
2020-12-30 21:40:26 +02:00
|
|
|
|
t->read_mtu = sco_opt.mtu;
|
|
|
|
|
|
t->write_mtu = sco_opt.mtu;
|
2020-07-21 11:00:57 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-03-18 21:48:10 +02:00
|
|
|
|
static struct spa_bt_transport *_transport_create(struct impl *backend,
|
2020-12-08 15:19:17 +01:00
|
|
|
|
const char *path,
|
|
|
|
|
|
struct spa_bt_device *device,
|
|
|
|
|
|
enum spa_bt_profile profile,
|
|
|
|
|
|
int codec,
|
|
|
|
|
|
struct spa_callbacks *impl)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct spa_bt_transport *t = NULL;
|
|
|
|
|
|
char *t_path = strdup(path);
|
|
|
|
|
|
|
2021-03-18 22:46:56 +02:00
|
|
|
|
t = spa_bt_transport_create(backend->monitor, t_path, sizeof(struct transport_data));
|
2020-12-08 15:19:17 +01:00
|
|
|
|
if (t == NULL) {
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_warn(backend->log, "can't create transport: %m");
|
2020-12-08 15:19:17 +01:00
|
|
|
|
free(t_path);
|
|
|
|
|
|
goto finish;
|
|
|
|
|
|
}
|
|
|
|
|
|
spa_bt_transport_set_implementation(t, impl, t);
|
|
|
|
|
|
|
|
|
|
|
|
t->device = device;
|
|
|
|
|
|
spa_list_append(&t->device->transport_list, &t->device_link);
|
2021-03-18 21:48:10 +02:00
|
|
|
|
t->backend = &backend->this;
|
2020-12-08 15:19:17 +01:00
|
|
|
|
t->profile = profile;
|
|
|
|
|
|
t->codec = codec;
|
2021-03-02 22:15:21 +02:00
|
|
|
|
t->n_channels = 1;
|
|
|
|
|
|
t->channels[0] = SPA_AUDIO_CHANNEL_MONO;
|
2020-12-08 15:19:17 +01:00
|
|
|
|
|
|
|
|
|
|
finish:
|
|
|
|
|
|
return t;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-03-18 21:48:10 +02:00
|
|
|
|
static int _audio_acquire(struct impl *backend, const char *path, uint8_t *codec)
|
2020-07-21 11:00:57 +02:00
|
|
|
|
{
|
2023-07-11 19:24:46 +02:00
|
|
|
|
spa_autoptr(DBusMessage) m = NULL, r = NULL;
|
2023-07-11 20:44:23 +02:00
|
|
|
|
spa_auto(DBusError) err = DBUS_ERROR_INIT;
|
2020-07-21 11:00:57 +02:00
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
2020-12-08 15:19:17 +01:00
|
|
|
|
m = dbus_message_new_method_call(OFONO_SERVICE, path,
|
|
|
|
|
|
OFONO_HF_AUDIO_CARD_INTERFACE,
|
|
|
|
|
|
"Acquire");
|
2020-07-21 11:00:57 +02:00
|
|
|
|
if (m == NULL)
|
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
|
|
|
bluez5: backend-ofono: don't do codec probe connections + add wait
Codec probe connections can trigger bad behavior from oFono if done when
device is busy (e.g. at connect), and they might be done at the same
time as A2DP transport is acquired which cannot work.
Also, oFono will not reply to DBus Acquire, if device does not complete
codec negotiation correctly. This is most likely to happen just after
device connect, when it is busy with other stuff (eg A2DP).
Remove codec probe connections altogether: instead, we guess mSBC if
mSBC is enabled and otherwise CVSD. If the guess turns out to be wrong,
which is unlikely (almost all devices have mSBC), we recreate the
transport with correct codec (from main loop, must not be done in
*_acquire because that can destroy nodes + unload the spa libs while
we're being called from there).
To avoid oFono DBus hangs at startup, add delay before marking the
profile connected, enforcing a time difference to A2DP operations.
2022-03-03 23:32:33 +02:00
|
|
|
|
/*
|
|
|
|
|
|
* XXX: We assume here oFono replies. It however can happen that the headset does
|
|
|
|
|
|
* XXX: not properly respond to the codec negotiation RFCOMM commands.
|
|
|
|
|
|
* XXX: oFono (1.34) fails to handle this condition, and will not send DBus reply
|
|
|
|
|
|
* XXX: in this case. The transport acquire API is synchronous, so we can't
|
|
|
|
|
|
* XXX: do better here right now.
|
|
|
|
|
|
*/
|
2020-07-21 11:00:57 +02:00
|
|
|
|
r = dbus_connection_send_with_reply_and_block(backend->conn, m, -1, &err);
|
|
|
|
|
|
if (r == NULL) {
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_error(backend->log, "Transport Acquire() failed for transport %s (%s)",
|
2020-12-08 15:19:17 +01:00
|
|
|
|
path, err.message);
|
2020-07-21 11:00:57 +02:00
|
|
|
|
return -EIO;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_error(backend->log, "Acquire returned error: %s", dbus_message_get_error_name(r));
|
2023-07-11 19:24:46 +02:00
|
|
|
|
return -EIO;
|
2020-07-21 11:00:57 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!dbus_message_get_args(r, &err,
|
2020-12-08 15:19:17 +01:00
|
|
|
|
DBUS_TYPE_UNIX_FD, &ret,
|
|
|
|
|
|
DBUS_TYPE_BYTE, codec,
|
|
|
|
|
|
DBUS_TYPE_INVALID)) {
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_error(backend->log, "Failed to parse Acquire() reply: %s", err.message);
|
2023-07-11 19:24:46 +02:00
|
|
|
|
return -EIO;
|
2020-07-21 11:00:57 +02:00
|
|
|
|
}
|
2020-12-08 15:19:17 +01:00
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int ofono_audio_acquire(void *data, bool optional)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct spa_bt_transport *transport = data;
|
bluez5: backend-ofono: don't do codec probe connections + add wait
Codec probe connections can trigger bad behavior from oFono if done when
device is busy (e.g. at connect), and they might be done at the same
time as A2DP transport is acquired which cannot work.
Also, oFono will not reply to DBus Acquire, if device does not complete
codec negotiation correctly. This is most likely to happen just after
device connect, when it is busy with other stuff (eg A2DP).
Remove codec probe connections altogether: instead, we guess mSBC if
mSBC is enabled and otherwise CVSD. If the guess turns out to be wrong,
which is unlikely (almost all devices have mSBC), we recreate the
transport with correct codec (from main loop, must not be done in
*_acquire because that can destroy nodes + unload the spa libs while
we're being called from there).
To avoid oFono DBus hangs at startup, add delay before marking the
profile connected, enforcing a time difference to A2DP operations.
2022-03-03 23:32:33 +02:00
|
|
|
|
struct transport_data *td = transport->user_data;
|
2021-03-18 21:48:10 +02:00
|
|
|
|
struct impl *backend = SPA_CONTAINER_OF(transport->backend, struct impl, this);
|
2020-12-08 15:19:17 +01:00
|
|
|
|
uint8_t codec;
|
|
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
2021-03-20 18:14:32 +02:00
|
|
|
|
if (transport->fd >= 0)
|
2021-03-18 22:46:56 +02:00
|
|
|
|
goto finish;
|
bluez5: backend-ofono: don't do codec probe connections + add wait
Codec probe connections can trigger bad behavior from oFono if done when
device is busy (e.g. at connect), and they might be done at the same
time as A2DP transport is acquired which cannot work.
Also, oFono will not reply to DBus Acquire, if device does not complete
codec negotiation correctly. This is most likely to happen just after
device connect, when it is busy with other stuff (eg A2DP).
Remove codec probe connections altogether: instead, we guess mSBC if
mSBC is enabled and otherwise CVSD. If the guess turns out to be wrong,
which is unlikely (almost all devices have mSBC), we recreate the
transport with correct codec (from main loop, must not be done in
*_acquire because that can destroy nodes + unload the spa libs while
we're being called from there).
To avoid oFono DBus hangs at startup, add delay before marking the
profile connected, enforcing a time difference to A2DP operations.
2022-03-03 23:32:33 +02:00
|
|
|
|
if (td->broken) {
|
|
|
|
|
|
ret = -EIO;
|
|
|
|
|
|
goto finish;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
spa_bt_device_update_last_bluez_action_time(transport->device);
|
2021-03-18 22:46:56 +02:00
|
|
|
|
|
2020-12-08 15:19:17 +01:00
|
|
|
|
ret = _audio_acquire(backend, transport->path, &codec);
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
|
goto finish;
|
|
|
|
|
|
|
|
|
|
|
|
transport->fd = ret;
|
|
|
|
|
|
|
|
|
|
|
|
if (transport->codec != codec) {
|
bluez5: backend-ofono: don't do codec probe connections + add wait
Codec probe connections can trigger bad behavior from oFono if done when
device is busy (e.g. at connect), and they might be done at the same
time as A2DP transport is acquired which cannot work.
Also, oFono will not reply to DBus Acquire, if device does not complete
codec negotiation correctly. This is most likely to happen just after
device connect, when it is busy with other stuff (eg A2DP).
Remove codec probe connections altogether: instead, we guess mSBC if
mSBC is enabled and otherwise CVSD. If the guess turns out to be wrong,
which is unlikely (almost all devices have mSBC), we recreate the
transport with correct codec (from main loop, must not be done in
*_acquire because that can destroy nodes + unload the spa libs while
we're being called from there).
To avoid oFono DBus hangs at startup, add delay before marking the
profile connected, enforcing a time difference to A2DP operations.
2022-03-03 23:32:33 +02:00
|
|
|
|
struct timespec ts;
|
2020-12-08 15:19:17 +01:00
|
|
|
|
|
bluez5: backend-ofono: don't do codec probe connections + add wait
Codec probe connections can trigger bad behavior from oFono if done when
device is busy (e.g. at connect), and they might be done at the same
time as A2DP transport is acquired which cannot work.
Also, oFono will not reply to DBus Acquire, if device does not complete
codec negotiation correctly. This is most likely to happen just after
device connect, when it is busy with other stuff (eg A2DP).
Remove codec probe connections altogether: instead, we guess mSBC if
mSBC is enabled and otherwise CVSD. If the guess turns out to be wrong,
which is unlikely (almost all devices have mSBC), we recreate the
transport with correct codec (from main loop, must not be done in
*_acquire because that can destroy nodes + unload the spa libs while
we're being called from there).
To avoid oFono DBus hangs at startup, add delay before marking the
profile connected, enforcing a time difference to A2DP operations.
2022-03-03 23:32:33 +02:00
|
|
|
|
spa_log_info(backend->log, "transport %p: acquired codec (%d) differs from transport one (%d)",
|
|
|
|
|
|
transport, codec, transport->codec);
|
2020-12-08 15:19:17 +01:00
|
|
|
|
|
|
|
|
|
|
/* shutdown to make sure connection is dropped immediately */
|
|
|
|
|
|
shutdown(transport->fd, SHUT_RDWR);
|
|
|
|
|
|
close(transport->fd);
|
|
|
|
|
|
transport->fd = -1;
|
|
|
|
|
|
|
bluez5: backend-ofono: don't do codec probe connections + add wait
Codec probe connections can trigger bad behavior from oFono if done when
device is busy (e.g. at connect), and they might be done at the same
time as A2DP transport is acquired which cannot work.
Also, oFono will not reply to DBus Acquire, if device does not complete
codec negotiation correctly. This is most likely to happen just after
device connect, when it is busy with other stuff (eg A2DP).
Remove codec probe connections altogether: instead, we guess mSBC if
mSBC is enabled and otherwise CVSD. If the guess turns out to be wrong,
which is unlikely (almost all devices have mSBC), we recreate the
transport with correct codec (from main loop, must not be done in
*_acquire because that can destroy nodes + unload the spa libs while
we're being called from there).
To avoid oFono DBus hangs at startup, add delay before marking the
profile connected, enforcing a time difference to A2DP operations.
2022-03-03 23:32:33 +02:00
|
|
|
|
/* schedule immediate profile update, from main loop */
|
|
|
|
|
|
transport->codec = codec;
|
|
|
|
|
|
td->broken = true;
|
|
|
|
|
|
ts.tv_sec = 0;
|
|
|
|
|
|
ts.tv_nsec = 1;
|
|
|
|
|
|
spa_loop_utils_update_timer(backend->loop_utils, backend->timer,
|
|
|
|
|
|
&ts, NULL, false);
|
2023-03-14 22:06:40 +02:00
|
|
|
|
|
|
|
|
|
|
ret = -EIO;
|
|
|
|
|
|
goto finish;
|
2020-12-08 15:19:17 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
bluez5: backend-ofono: don't do codec probe connections + add wait
Codec probe connections can trigger bad behavior from oFono if done when
device is busy (e.g. at connect), and they might be done at the same
time as A2DP transport is acquired which cannot work.
Also, oFono will not reply to DBus Acquire, if device does not complete
codec negotiation correctly. This is most likely to happen just after
device connect, when it is busy with other stuff (eg A2DP).
Remove codec probe connections altogether: instead, we guess mSBC if
mSBC is enabled and otherwise CVSD. If the guess turns out to be wrong,
which is unlikely (almost all devices have mSBC), we recreate the
transport with correct codec (from main loop, must not be done in
*_acquire because that can destroy nodes + unload the spa libs while
we're being called from there).
To avoid oFono DBus hangs at startup, add delay before marking the
profile connected, enforcing a time difference to A2DP operations.
2022-03-03 23:32:33 +02:00
|
|
|
|
td->broken = false;
|
|
|
|
|
|
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_debug(backend->log, "transport %p: Acquire %s, fd %d codec %d", transport,
|
2020-07-21 11:00:57 +02:00
|
|
|
|
transport->path, transport->fd, transport->codec);
|
|
|
|
|
|
|
|
|
|
|
|
ofono_transport_get_mtu(backend, transport);
|
2020-12-22 20:36:04 +02:00
|
|
|
|
ret = 0;
|
2020-07-21 11:00:57 +02:00
|
|
|
|
|
|
|
|
|
|
finish:
|
2023-03-14 22:06:40 +02:00
|
|
|
|
if (ret < 0)
|
|
|
|
|
|
spa_bt_transport_set_state(transport, SPA_BT_TRANSPORT_STATE_ERROR);
|
|
|
|
|
|
else
|
|
|
|
|
|
spa_bt_transport_set_state(transport, SPA_BT_TRANSPORT_STATE_ACTIVE);
|
|
|
|
|
|
|
2020-07-21 11:00:57 +02:00
|
|
|
|
return ret;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int ofono_audio_release(void *data)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct spa_bt_transport *transport = data;
|
2021-03-18 21:48:10 +02:00
|
|
|
|
struct impl *backend = SPA_CONTAINER_OF(transport->backend, struct impl, this);
|
2020-07-21 11:00:57 +02:00
|
|
|
|
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_debug(backend->log, "transport %p: Release %s",
|
2020-07-21 11:00:57 +02:00
|
|
|
|
transport, transport->path);
|
|
|
|
|
|
|
2023-03-14 22:06:40 +02:00
|
|
|
|
spa_bt_transport_set_state(transport, SPA_BT_TRANSPORT_STATE_IDLE);
|
|
|
|
|
|
|
2020-12-23 15:10:04 +02:00
|
|
|
|
if (transport->sco_io) {
|
|
|
|
|
|
spa_bt_sco_io_destroy(transport->sco_io);
|
|
|
|
|
|
transport->sco_io = NULL;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-21 11:00:57 +02:00
|
|
|
|
/* shutdown to make sure connection is dropped immediately */
|
|
|
|
|
|
shutdown(transport->fd, SHUT_RDWR);
|
|
|
|
|
|
close(transport->fd);
|
|
|
|
|
|
transport->fd = -1;
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-03-18 21:48:10 +02:00
|
|
|
|
static DBusHandlerResult ofono_audio_card_removed(struct impl *backend, const char *path)
|
2020-07-17 18:10:38 +02:00
|
|
|
|
{
|
2020-07-21 11:00:57 +02:00
|
|
|
|
struct spa_bt_transport *transport;
|
|
|
|
|
|
|
|
|
|
|
|
spa_assert(backend);
|
|
|
|
|
|
spa_assert(path);
|
|
|
|
|
|
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_debug(backend->log, "card removed: %s", path);
|
2020-07-17 18:10:38 +02:00
|
|
|
|
|
2020-07-21 11:00:57 +02:00
|
|
|
|
transport = spa_bt_transport_find(backend->monitor, path);
|
|
|
|
|
|
|
|
|
|
|
|
if (transport != NULL) {
|
|
|
|
|
|
struct spa_bt_device *device = transport->device;
|
|
|
|
|
|
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_debug(backend->log, "transport %p: free %s",
|
2020-07-21 11:00:57 +02:00
|
|
|
|
transport, transport->path);
|
|
|
|
|
|
|
|
|
|
|
|
spa_bt_transport_free(transport);
|
|
|
|
|
|
if (device != NULL)
|
|
|
|
|
|
spa_bt_device_check_profiles(device, false);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-17 18:10:38 +02:00
|
|
|
|
return DBUS_HANDLER_RESULT_HANDLED;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-21 11:00:57 +02:00
|
|
|
|
static const struct spa_bt_transport_implementation ofono_transport_impl = {
|
|
|
|
|
|
SPA_VERSION_BT_TRANSPORT_IMPLEMENTATION,
|
|
|
|
|
|
.acquire = ofono_audio_acquire,
|
|
|
|
|
|
.release = ofono_audio_release,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2023-07-03 04:34:59 +02:00
|
|
|
|
static bool activate_transport(struct spa_bt_transport *t, const void *data)
|
bluez5: backend-ofono: don't do codec probe connections + add wait
Codec probe connections can trigger bad behavior from oFono if done when
device is busy (e.g. at connect), and they might be done at the same
time as A2DP transport is acquired which cannot work.
Also, oFono will not reply to DBus Acquire, if device does not complete
codec negotiation correctly. This is most likely to happen just after
device connect, when it is busy with other stuff (eg A2DP).
Remove codec probe connections altogether: instead, we guess mSBC if
mSBC is enabled and otherwise CVSD. If the guess turns out to be wrong,
which is unlikely (almost all devices have mSBC), we recreate the
transport with correct codec (from main loop, must not be done in
*_acquire because that can destroy nodes + unload the spa libs while
we're being called from there).
To avoid oFono DBus hangs at startup, add delay before marking the
profile connected, enforcing a time difference to A2DP operations.
2022-03-03 23:32:33 +02:00
|
|
|
|
{
|
|
|
|
|
|
struct impl *backend = (void *)data;
|
|
|
|
|
|
struct transport_data *td = t->user_data;
|
|
|
|
|
|
struct timespec ts;
|
|
|
|
|
|
uint64_t now, threshold;
|
|
|
|
|
|
|
|
|
|
|
|
if (t->backend != &backend->this)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
/* Check device-specific rate limit */
|
|
|
|
|
|
spa_system_clock_gettime(backend->main_system, CLOCK_MONOTONIC, &ts);
|
|
|
|
|
|
now = SPA_TIMESPEC_TO_NSEC(&ts);
|
|
|
|
|
|
threshold = t->device->last_bluez_action_time + ACTION_INTERVAL_NSEC;
|
|
|
|
|
|
if (now < threshold) {
|
|
|
|
|
|
ts.tv_sec = (threshold - now) / SPA_NSEC_PER_SEC;
|
|
|
|
|
|
ts.tv_nsec = (threshold - now) % SPA_NSEC_PER_SEC;
|
|
|
|
|
|
spa_loop_utils_update_timer(backend->loop_utils, backend->timer,
|
|
|
|
|
|
&ts, NULL, false);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!td->activated) {
|
|
|
|
|
|
/* Connect profile */
|
|
|
|
|
|
spa_log_debug(backend->log, "Transport %s activated", t->path);
|
|
|
|
|
|
td->activated = true;
|
|
|
|
|
|
spa_bt_device_connect_profile(t->device, t->profile);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (td->broken) {
|
|
|
|
|
|
/* Recreate the transport */
|
|
|
|
|
|
struct spa_bt_transport *t_copy;
|
|
|
|
|
|
|
|
|
|
|
|
t_copy = _transport_create(backend, t->path, t->device,
|
|
|
|
|
|
t->profile, t->codec, (struct spa_callbacks *)&ofono_transport_impl);
|
|
|
|
|
|
spa_bt_transport_free(t);
|
|
|
|
|
|
|
|
|
|
|
|
if (t_copy)
|
|
|
|
|
|
spa_bt_device_connect_profile(t_copy->device, t_copy->profile);
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void activate_transports(struct impl *backend)
|
|
|
|
|
|
{
|
|
|
|
|
|
while (spa_bt_transport_find_full(backend->monitor, activate_transport, backend));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void activate_timer_event(void *userdata, uint64_t expirations)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct impl *backend = userdata;
|
|
|
|
|
|
spa_loop_utils_update_timer(backend->loop_utils, backend->timer, NULL, NULL, false);
|
|
|
|
|
|
activate_transports(backend);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-03-18 21:48:10 +02:00
|
|
|
|
static DBusHandlerResult ofono_audio_card_found(struct impl *backend, char *path, DBusMessageIter *props_i)
|
2020-07-17 18:10:38 +02:00
|
|
|
|
{
|
2020-07-21 11:00:57 +02:00
|
|
|
|
const char *remote_address = NULL;
|
|
|
|
|
|
const char *local_address = NULL;
|
|
|
|
|
|
struct spa_bt_device *d;
|
|
|
|
|
|
struct spa_bt_transport *t;
|
bluez5: backend-ofono: don't do codec probe connections + add wait
Codec probe connections can trigger bad behavior from oFono if done when
device is busy (e.g. at connect), and they might be done at the same
time as A2DP transport is acquired which cannot work.
Also, oFono will not reply to DBus Acquire, if device does not complete
codec negotiation correctly. This is most likely to happen just after
device connect, when it is busy with other stuff (eg A2DP).
Remove codec probe connections altogether: instead, we guess mSBC if
mSBC is enabled and otherwise CVSD. If the guess turns out to be wrong,
which is unlikely (almost all devices have mSBC), we recreate the
transport with correct codec (from main loop, must not be done in
*_acquire because that can destroy nodes + unload the spa libs while
we're being called from there).
To avoid oFono DBus hangs at startup, add delay before marking the
profile connected, enforcing a time difference to A2DP operations.
2022-03-03 23:32:33 +02:00
|
|
|
|
struct transport_data *td;
|
2020-07-21 11:00:57 +02:00
|
|
|
|
enum spa_bt_profile profile = SPA_BT_PROFILE_HFP_AG;
|
bluez5: backend-ofono: don't do codec probe connections + add wait
Codec probe connections can trigger bad behavior from oFono if done when
device is busy (e.g. at connect), and they might be done at the same
time as A2DP transport is acquired which cannot work.
Also, oFono will not reply to DBus Acquire, if device does not complete
codec negotiation correctly. This is most likely to happen just after
device connect, when it is busy with other stuff (eg A2DP).
Remove codec probe connections altogether: instead, we guess mSBC if
mSBC is enabled and otherwise CVSD. If the guess turns out to be wrong,
which is unlikely (almost all devices have mSBC), we recreate the
transport with correct codec (from main loop, must not be done in
*_acquire because that can destroy nodes + unload the spa libs while
we're being called from there).
To avoid oFono DBus hangs at startup, add delay before marking the
profile connected, enforcing a time difference to A2DP operations.
2022-03-03 23:32:33 +02:00
|
|
|
|
uint8_t codec = backend->msbc_supported ?
|
|
|
|
|
|
HFP_AUDIO_CODEC_MSBC : HFP_AUDIO_CODEC_CVSD;
|
2020-07-21 11:00:57 +02:00
|
|
|
|
|
|
|
|
|
|
spa_assert(backend);
|
|
|
|
|
|
spa_assert(path);
|
|
|
|
|
|
spa_assert(props_i);
|
|
|
|
|
|
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_debug(backend->log, "new card: %s", path);
|
2020-07-17 18:10:38 +02:00
|
|
|
|
|
2020-07-21 11:00:57 +02:00
|
|
|
|
while (dbus_message_iter_get_arg_type(props_i) != DBUS_TYPE_INVALID) {
|
|
|
|
|
|
DBusMessageIter i, value_i;
|
|
|
|
|
|
const char *key, *value;
|
|
|
|
|
|
char c;
|
|
|
|
|
|
|
|
|
|
|
|
dbus_message_iter_recurse(props_i, &i);
|
|
|
|
|
|
|
|
|
|
|
|
dbus_message_iter_get_basic(&i, &key);
|
|
|
|
|
|
dbus_message_iter_next(&i);
|
|
|
|
|
|
dbus_message_iter_recurse(&i, &value_i);
|
|
|
|
|
|
|
|
|
|
|
|
if ((c = dbus_message_iter_get_arg_type(&value_i)) != DBUS_TYPE_STRING) {
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_error(backend->log, "Invalid properties for %s: expected 's', received '%c'", path, c);
|
2020-07-21 11:00:57 +02:00
|
|
|
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dbus_message_iter_get_basic(&value_i, &value);
|
|
|
|
|
|
|
2021-05-18 11:36:13 +10:00
|
|
|
|
if (spa_streq(key, "RemoteAddress")) {
|
2020-07-21 11:00:57 +02:00
|
|
|
|
remote_address = value;
|
2021-05-18 11:36:13 +10:00
|
|
|
|
} else if (spa_streq(key, "LocalAddress")) {
|
2020-07-21 11:00:57 +02:00
|
|
|
|
local_address = value;
|
2021-05-18 11:36:13 +10:00
|
|
|
|
} else if (spa_streq(key, "Type")) {
|
|
|
|
|
|
if (spa_streq(value, "gateway"))
|
2020-07-21 11:00:57 +02:00
|
|
|
|
profile = SPA_BT_PROFILE_HFP_HF;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_debug(backend->log, "%s: %s", key, value);
|
2020-07-21 11:00:57 +02:00
|
|
|
|
|
|
|
|
|
|
dbus_message_iter_next(props_i);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-03-27 13:55:01 +02:00
|
|
|
|
if (!remote_address || !local_address) {
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_error(backend->log, "Missing addresses for %s", path);
|
2021-03-27 13:55:01 +02:00
|
|
|
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-21 11:00:57 +02:00
|
|
|
|
d = spa_bt_device_find_by_address(backend->monitor, remote_address, local_address);
|
2022-01-19 19:05:37 +02:00
|
|
|
|
if (!d || !d->adapter) {
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_error(backend->log, "Device doesn’t exist for %s", path);
|
2020-07-21 11:00:57 +02:00
|
|
|
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
|
|
|
|
|
}
|
2021-06-20 23:00:37 +08:00
|
|
|
|
spa_bt_device_add_profile(d, profile);
|
2020-07-21 11:00:57 +02:00
|
|
|
|
|
2020-12-08 15:19:17 +01:00
|
|
|
|
t = _transport_create(backend, path, d, profile, codec, (struct spa_callbacks *)&ofono_transport_impl);
|
bluez5: backend-ofono: don't do codec probe connections + add wait
Codec probe connections can trigger bad behavior from oFono if done when
device is busy (e.g. at connect), and they might be done at the same
time as A2DP transport is acquired which cannot work.
Also, oFono will not reply to DBus Acquire, if device does not complete
codec negotiation correctly. This is most likely to happen just after
device connect, when it is busy with other stuff (eg A2DP).
Remove codec probe connections altogether: instead, we guess mSBC if
mSBC is enabled and otherwise CVSD. If the guess turns out to be wrong,
which is unlikely (almost all devices have mSBC), we recreate the
transport with correct codec (from main loop, must not be done in
*_acquire because that can destroy nodes + unload the spa libs while
we're being called from there).
To avoid oFono DBus hangs at startup, add delay before marking the
profile connected, enforcing a time difference to A2DP operations.
2022-03-03 23:32:33 +02:00
|
|
|
|
if (t == NULL) {
|
|
|
|
|
|
spa_log_error(backend->log, "failed to create transport: %s", spa_strerror(-errno));
|
|
|
|
|
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
|
|
|
|
|
}
|
2020-07-21 11:00:57 +02:00
|
|
|
|
|
bluez5: backend-ofono: don't do codec probe connections + add wait
Codec probe connections can trigger bad behavior from oFono if done when
device is busy (e.g. at connect), and they might be done at the same
time as A2DP transport is acquired which cannot work.
Also, oFono will not reply to DBus Acquire, if device does not complete
codec negotiation correctly. This is most likely to happen just after
device connect, when it is busy with other stuff (eg A2DP).
Remove codec probe connections altogether: instead, we guess mSBC if
mSBC is enabled and otherwise CVSD. If the guess turns out to be wrong,
which is unlikely (almost all devices have mSBC), we recreate the
transport with correct codec (from main loop, must not be done in
*_acquire because that can destroy nodes + unload the spa libs while
we're being called from there).
To avoid oFono DBus hangs at startup, add delay before marking the
profile connected, enforcing a time difference to A2DP operations.
2022-03-03 23:32:33 +02:00
|
|
|
|
td = t->user_data;
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* For HF profile, delay profile connect, so that we likely don't do it at the
|
|
|
|
|
|
* same time as the device is busy with A2DP connect. This avoids some oFono
|
|
|
|
|
|
* misbehavior (see comment in _audio_acquire above).
|
|
|
|
|
|
*
|
|
|
|
|
|
* For AG mode, we delay the emission of the nodes, so it is not necessary
|
|
|
|
|
|
* to know the codec in advance.
|
|
|
|
|
|
*/
|
|
|
|
|
|
if (profile == SPA_BT_PROFILE_HFP_HF) {
|
|
|
|
|
|
struct timespec ts;
|
|
|
|
|
|
ts.tv_sec = INITIAL_INTERVAL_NSEC / SPA_NSEC_PER_SEC;
|
|
|
|
|
|
ts.tv_nsec = INITIAL_INTERVAL_NSEC % SPA_NSEC_PER_SEC;
|
|
|
|
|
|
spa_loop_utils_update_timer(backend->loop_utils, backend->timer,
|
|
|
|
|
|
&ts, NULL, false);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
td->activated = true;
|
|
|
|
|
|
spa_bt_device_connect_profile(t->device, t->profile);
|
|
|
|
|
|
}
|
2020-07-21 11:00:57 +02:00
|
|
|
|
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_debug(backend->log, "Transport %s available, codec %d", t->path, t->codec);
|
2020-07-21 11:00:57 +02:00
|
|
|
|
|
2020-07-17 18:10:38 +02:00
|
|
|
|
return DBUS_HANDLER_RESULT_HANDLED;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static DBusHandlerResult ofono_release(DBusConnection *conn, DBusMessage *m, void *userdata)
|
|
|
|
|
|
{
|
2021-03-18 21:48:10 +02:00
|
|
|
|
struct impl *backend = userdata;
|
2020-07-17 18:10:38 +02:00
|
|
|
|
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_warn(backend->log, "release");
|
2020-07-17 18:10:38 +02:00
|
|
|
|
|
2023-07-11 19:24:46 +02:00
|
|
|
|
if (!reply_with_error(conn, m, OFONO_HF_AUDIO_AGENT_INTERFACE ".Error.NotImplemented", "Method not implemented"))
|
2020-07-17 18:10:38 +02:00
|
|
|
|
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
|
|
|
|
|
|
|
|
|
|
|
return DBUS_HANDLER_RESULT_HANDLED;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-03-18 22:46:56 +02:00
|
|
|
|
static void sco_event(struct spa_source *source)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct spa_bt_transport *t = source->data;
|
2021-03-18 21:48:10 +02:00
|
|
|
|
struct impl *backend = SPA_CONTAINER_OF(t->backend, struct impl, this);
|
2021-03-18 22:46:56 +02:00
|
|
|
|
|
|
|
|
|
|
if (source->rmask & (SPA_IO_HUP | SPA_IO_ERR)) {
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_debug(backend->log, "transport %p: error on SCO socket: %s", t, strerror(errno));
|
2021-03-18 22:46:56 +02:00
|
|
|
|
if (t->fd >= 0) {
|
|
|
|
|
|
if (source->loop)
|
|
|
|
|
|
spa_loop_remove_source(source->loop, source);
|
|
|
|
|
|
shutdown(t->fd, SHUT_RDWR);
|
|
|
|
|
|
close (t->fd);
|
|
|
|
|
|
t->fd = -1;
|
|
|
|
|
|
spa_bt_transport_set_state(t, SPA_BT_TRANSPORT_STATE_IDLE);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-04-08 15:38:25 +02:00
|
|
|
|
static int enable_sco_socket(int sock)
|
|
|
|
|
|
{
|
|
|
|
|
|
char c;
|
|
|
|
|
|
struct pollfd pfd;
|
|
|
|
|
|
|
|
|
|
|
|
if (sock < 0)
|
|
|
|
|
|
return ENOTCONN;
|
|
|
|
|
|
|
|
|
|
|
|
memset(&pfd, 0, sizeof(pfd));
|
|
|
|
|
|
pfd.fd = sock;
|
|
|
|
|
|
pfd.events = POLLOUT;
|
|
|
|
|
|
|
|
|
|
|
|
if (poll(&pfd, 1, 0) < 0)
|
|
|
|
|
|
return errno;
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* If socket already writable then it is not in defer setup state,
|
|
|
|
|
|
* otherwise it needs to be read to authorize the connection.
|
|
|
|
|
|
*/
|
|
|
|
|
|
if ((pfd.revents & POLLOUT))
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
|
|
/* Enable socket by reading 1 byte */
|
|
|
|
|
|
if (read(sock, &c, 1) < 0)
|
|
|
|
|
|
return errno;
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-17 18:10:38 +02:00
|
|
|
|
static DBusHandlerResult ofono_new_audio_connection(DBusConnection *conn, DBusMessage *m, void *userdata)
|
|
|
|
|
|
{
|
2021-03-18 21:48:10 +02:00
|
|
|
|
struct impl *backend = userdata;
|
2020-07-17 18:10:38 +02:00
|
|
|
|
const char *path;
|
|
|
|
|
|
int fd;
|
|
|
|
|
|
uint8_t codec;
|
2020-07-21 11:00:57 +02:00
|
|
|
|
struct spa_bt_transport *t;
|
2021-03-18 22:46:56 +02:00
|
|
|
|
struct transport_data *td;
|
2023-07-11 19:24:46 +02:00
|
|
|
|
spa_autoptr(DBusMessage) r = NULL;
|
2020-07-17 18:10:38 +02:00
|
|
|
|
|
|
|
|
|
|
if (dbus_message_get_args(m, NULL,
|
|
|
|
|
|
DBUS_TYPE_OBJECT_PATH, &path,
|
|
|
|
|
|
DBUS_TYPE_UNIX_FD, &fd,
|
|
|
|
|
|
DBUS_TYPE_BYTE, &codec,
|
|
|
|
|
|
DBUS_TYPE_INVALID) == FALSE) {
|
|
|
|
|
|
r = dbus_message_new_error(m, OFONO_ERROR_INVALID_ARGUMENTS, "Invalid arguments in method call");
|
|
|
|
|
|
goto fail;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-21 11:00:57 +02:00
|
|
|
|
t = spa_bt_transport_find(backend->monitor, path);
|
2021-03-18 22:46:56 +02:00
|
|
|
|
if (t && (t->profile & SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY)) {
|
2021-04-08 15:38:25 +02:00
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
|
|
err = enable_sco_socket(fd);
|
|
|
|
|
|
if (err) {
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_error(backend->log, "transport %p: Couldn't authorize SCO connection: %s", t, strerror(err));
|
2021-04-08 15:38:25 +02:00
|
|
|
|
r = dbus_message_new_error(m, OFONO_ERROR_FAILED, "SCO authorization failed");
|
|
|
|
|
|
shutdown(fd, SHUT_RDWR);
|
|
|
|
|
|
close(fd);
|
|
|
|
|
|
goto fail;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-03-18 22:46:56 +02:00
|
|
|
|
t->fd = fd;
|
|
|
|
|
|
t->codec = codec;
|
|
|
|
|
|
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_debug(backend->log, "transport %p: NewConnection %s, fd %d codec %d",
|
2021-03-18 22:46:56 +02:00
|
|
|
|
t, t->path, t->fd, t->codec);
|
|
|
|
|
|
|
|
|
|
|
|
td = t->user_data;
|
|
|
|
|
|
td->sco.func = sco_event;
|
|
|
|
|
|
td->sco.data = t;
|
|
|
|
|
|
td->sco.fd = fd;
|
|
|
|
|
|
td->sco.mask = SPA_IO_HUP | SPA_IO_ERR;
|
|
|
|
|
|
td->sco.rmask = 0;
|
|
|
|
|
|
spa_loop_add_source(backend->main_loop, &td->sco);
|
|
|
|
|
|
|
|
|
|
|
|
ofono_transport_get_mtu(backend, t);
|
|
|
|
|
|
spa_bt_transport_set_state (t, SPA_BT_TRANSPORT_STATE_PENDING);
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (fd) {
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_debug(backend->log, "ignoring NewConnection");
|
2021-03-18 22:46:56 +02:00
|
|
|
|
r = dbus_message_new_error(m, OFONO_ERROR_NOT_IMPLEMENTED, "Method not implemented");
|
|
|
|
|
|
shutdown(fd, SHUT_RDWR);
|
|
|
|
|
|
close(fd);
|
2020-07-21 11:00:57 +02:00
|
|
|
|
}
|
2020-07-17 18:10:38 +02:00
|
|
|
|
|
|
|
|
|
|
fail:
|
|
|
|
|
|
if (r) {
|
2021-03-27 13:55:01 +02:00
|
|
|
|
if (!dbus_connection_send(backend->conn, r, NULL))
|
2023-07-11 19:24:46 +02:00
|
|
|
|
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
|
|
|
|
|
return DBUS_HANDLER_RESULT_HANDLED;
|
2020-07-17 18:10:38 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return DBUS_HANDLER_RESULT_HANDLED;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static DBusHandlerResult ofono_handler(DBusConnection *c, DBusMessage *m, void *userdata)
|
|
|
|
|
|
{
|
2021-03-18 21:48:10 +02:00
|
|
|
|
struct impl *backend = userdata;
|
2020-07-17 18:10:38 +02:00
|
|
|
|
const char *path, *interface, *member;
|
|
|
|
|
|
DBusHandlerResult res;
|
|
|
|
|
|
|
|
|
|
|
|
path = dbus_message_get_path(m);
|
|
|
|
|
|
interface = dbus_message_get_interface(m);
|
|
|
|
|
|
member = dbus_message_get_member(m);
|
|
|
|
|
|
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_debug(backend->log, "path=%s, interface=%s, member=%s", path, interface, member);
|
2020-07-17 18:10:38 +02:00
|
|
|
|
|
|
|
|
|
|
if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
|
|
|
|
|
|
const char *xml = OFONO_INTROSPECT_XML;
|
2023-07-11 19:24:46 +02:00
|
|
|
|
spa_autoptr(DBusMessage) r = NULL;
|
2020-07-17 18:10:38 +02:00
|
|
|
|
|
|
|
|
|
|
if ((r = dbus_message_new_method_return(m)) == NULL)
|
|
|
|
|
|
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
|
|
|
|
|
if (!dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID))
|
|
|
|
|
|
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
|
|
|
|
|
if (!dbus_connection_send(backend->conn, r, NULL))
|
|
|
|
|
|
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
|
|
|
|
|
|
|
|
|
|
|
res = DBUS_HANDLER_RESULT_HANDLED;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (dbus_message_is_method_call(m, OFONO_HF_AUDIO_AGENT_INTERFACE, "Release"))
|
|
|
|
|
|
res = ofono_release(c, m, userdata);
|
|
|
|
|
|
else if (dbus_message_is_method_call(m, OFONO_HF_AUDIO_AGENT_INTERFACE, "NewConnection"))
|
|
|
|
|
|
res = ofono_new_audio_connection(c, m, userdata);
|
|
|
|
|
|
else
|
|
|
|
|
|
res = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
|
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void ofono_getcards_reply(DBusPendingCall *pending, void *user_data)
|
|
|
|
|
|
{
|
2021-03-18 21:48:10 +02:00
|
|
|
|
struct impl *backend = user_data;
|
2020-07-17 18:10:38 +02:00
|
|
|
|
DBusMessageIter i, array_i, struct_i, props_i;
|
|
|
|
|
|
|
2023-07-11 19:24:46 +02:00
|
|
|
|
spa_autoptr(DBusMessage) r = steal_reply_and_unref(&pending);
|
2020-07-17 18:10:38 +02:00
|
|
|
|
if (r == NULL)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_error(backend->log, "Failed to get a list of handsfree audio cards: %s",
|
2020-07-17 18:10:38 +02:00
|
|
|
|
dbus_message_get_error_name(r));
|
2023-07-11 19:24:46 +02:00
|
|
|
|
return;
|
2020-07-17 18:10:38 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-18 11:43:49 +10:00
|
|
|
|
if (!dbus_message_iter_init(r, &i) || !spa_streq(dbus_message_get_signature(r), "a(oa{sv})")) {
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_error(backend->log, "Invalid arguments in GetCards() reply");
|
2023-07-11 19:24:46 +02:00
|
|
|
|
return;
|
2020-07-17 18:10:38 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dbus_message_iter_recurse(&i, &array_i);
|
|
|
|
|
|
while (dbus_message_iter_get_arg_type(&array_i) != DBUS_TYPE_INVALID) {
|
|
|
|
|
|
char *path;
|
|
|
|
|
|
|
|
|
|
|
|
dbus_message_iter_recurse(&array_i, &struct_i);
|
|
|
|
|
|
dbus_message_iter_get_basic(&struct_i, &path);
|
|
|
|
|
|
dbus_message_iter_next(&struct_i);
|
|
|
|
|
|
|
|
|
|
|
|
dbus_message_iter_recurse(&struct_i, &props_i);
|
|
|
|
|
|
|
|
|
|
|
|
ofono_audio_card_found(backend, path, &props_i);
|
|
|
|
|
|
|
|
|
|
|
|
dbus_message_iter_next(&array_i);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-07-11 19:24:46 +02:00
|
|
|
|
static int ofono_register(struct impl *backend)
|
2020-07-17 18:10:38 +02:00
|
|
|
|
{
|
2023-07-11 19:24:46 +02:00
|
|
|
|
spa_autoptr(DBusMessage) m = NULL, r = NULL;
|
2021-02-17 18:41:24 +01:00
|
|
|
|
const char *path = OFONO_AUDIO_CLIENT;
|
|
|
|
|
|
uint8_t codecs[2];
|
|
|
|
|
|
const uint8_t *pcodecs = codecs;
|
2023-07-11 20:44:23 +02:00
|
|
|
|
int ncodecs = 0;
|
|
|
|
|
|
spa_auto(DBusError) err = DBUS_ERROR_INIT;
|
2020-07-17 18:10:38 +02:00
|
|
|
|
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_debug(backend->log, "Registering");
|
2021-02-17 18:41:24 +01:00
|
|
|
|
|
|
|
|
|
|
m = dbus_message_new_method_call(OFONO_SERVICE, "/",
|
|
|
|
|
|
OFONO_HF_AUDIO_MANAGER_INTERFACE, "Register");
|
|
|
|
|
|
if (m == NULL)
|
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
|
|
codecs[ncodecs++] = HFP_AUDIO_CODEC_CVSD;
|
|
|
|
|
|
if (backend->msbc_supported)
|
|
|
|
|
|
codecs[ncodecs++] = HFP_AUDIO_CODEC_MSBC;
|
|
|
|
|
|
|
|
|
|
|
|
dbus_message_append_args(m, DBUS_TYPE_OBJECT_PATH, &path,
|
|
|
|
|
|
DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &pcodecs, ncodecs,
|
|
|
|
|
|
DBUS_TYPE_INVALID);
|
|
|
|
|
|
|
|
|
|
|
|
r = dbus_connection_send_with_reply_and_block(backend->conn, m, -1, &err);
|
|
|
|
|
|
if (r == NULL) {
|
2021-03-26 12:09:43 +01:00
|
|
|
|
if (dbus_error_has_name(&err, "org.freedesktop.DBus.Error.ServiceUnknown")) {
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_info(backend->log, "oFono not available: %s",
|
2021-03-26 12:09:43 +01:00
|
|
|
|
err.message);
|
2023-07-11 20:44:23 +02:00
|
|
|
|
return -ENOTSUP;
|
2021-03-26 12:09:43 +01:00
|
|
|
|
} else {
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_warn(backend->log, "Registering Profile %s failed: %s (%s)",
|
2021-03-26 12:09:43 +01:00
|
|
|
|
path, err.message, err.name);
|
2023-07-11 20:44:23 +02:00
|
|
|
|
return -EIO;
|
2021-03-26 12:09:43 +01:00
|
|
|
|
}
|
2021-02-17 18:41:24 +01:00
|
|
|
|
}
|
2020-07-17 18:10:38 +02:00
|
|
|
|
|
|
|
|
|
|
if (dbus_message_is_error(r, OFONO_ERROR_INVALID_ARGUMENTS)) {
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_warn(backend->log, "invalid arguments");
|
2023-07-11 19:24:46 +02:00
|
|
|
|
return -EIO;
|
2020-07-17 18:10:38 +02:00
|
|
|
|
}
|
|
|
|
|
|
if (dbus_message_is_error(r, OFONO_ERROR_IN_USE)) {
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_warn(backend->log, "already in use");
|
2023-07-11 19:24:46 +02:00
|
|
|
|
return -EIO;
|
2020-07-17 18:10:38 +02:00
|
|
|
|
}
|
|
|
|
|
|
if (dbus_message_is_error(r, DBUS_ERROR_UNKNOWN_METHOD)) {
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_warn(backend->log, "Error registering profile");
|
2023-07-11 19:24:46 +02:00
|
|
|
|
return -EIO;
|
2020-07-17 18:10:38 +02:00
|
|
|
|
}
|
2021-01-14 17:11:42 +01:00
|
|
|
|
if (dbus_message_is_error(r, DBUS_ERROR_SERVICE_UNKNOWN)) {
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_info(backend->log, "oFono not available, disabling");
|
2023-07-11 19:24:46 +02:00
|
|
|
|
return -EIO;
|
2021-01-14 17:11:42 +01:00
|
|
|
|
}
|
2020-07-17 18:10:38 +02:00
|
|
|
|
if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_error(backend->log, "Register() failed: %s",
|
2020-07-17 18:10:38 +02:00
|
|
|
|
dbus_message_get_error_name(r));
|
2023-07-11 19:24:46 +02:00
|
|
|
|
return -EIO;
|
2020-07-17 18:10:38 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_debug(backend->log, "registered");
|
2020-07-17 18:10:38 +02:00
|
|
|
|
|
2023-07-11 19:24:46 +02:00
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int ofono_getcards(struct impl *backend)
|
|
|
|
|
|
{
|
|
|
|
|
|
spa_autoptr(DBusMessage) m = NULL;
|
|
|
|
|
|
|
2020-07-17 18:10:38 +02:00
|
|
|
|
m = dbus_message_new_method_call(OFONO_SERVICE, "/",
|
|
|
|
|
|
OFONO_HF_AUDIO_MANAGER_INTERFACE, "GetCards");
|
|
|
|
|
|
if (m == NULL)
|
2023-07-11 19:24:46 +02:00
|
|
|
|
return -ENOMEM;
|
2020-07-17 18:10:38 +02:00
|
|
|
|
|
2023-07-11 19:57:23 +02:00
|
|
|
|
if (!send_with_reply(backend->conn, m, ofono_getcards_reply, backend))
|
|
|
|
|
|
return -EIO;
|
2020-07-17 18:10:38 +02:00
|
|
|
|
|
2021-02-17 18:41:24 +01:00
|
|
|
|
return 0;
|
2023-07-11 19:24:46 +02:00
|
|
|
|
}
|
2021-02-17 18:41:24 +01:00
|
|
|
|
|
2023-07-11 19:24:46 +02:00
|
|
|
|
static int backend_ofono_register(void *data)
|
|
|
|
|
|
{
|
|
|
|
|
|
int ret = ofono_register(data);
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
|
|
ret = ofono_getcards(data);
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
2020-07-17 18:10:38 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static DBusHandlerResult ofono_filter_cb(DBusConnection *bus, DBusMessage *m, void *user_data)
|
|
|
|
|
|
{
|
2021-03-18 21:48:10 +02:00
|
|
|
|
struct impl *backend = user_data;
|
2020-07-17 18:10:38 +02:00
|
|
|
|
|
2021-02-17 18:41:24 +01:00
|
|
|
|
if (dbus_message_is_signal(m, OFONO_HF_AUDIO_MANAGER_INTERFACE, "CardAdded")) {
|
2020-07-17 18:10:38 +02:00
|
|
|
|
char *p;
|
|
|
|
|
|
DBusMessageIter arg_i, props_i;
|
|
|
|
|
|
|
2021-05-18 11:43:49 +10:00
|
|
|
|
if (!dbus_message_iter_init(m, &arg_i) || !spa_streq(dbus_message_get_signature(m), "oa{sv}")) {
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_error(backend->log, "Failed to parse org.ofono.HandsfreeAudioManager.CardAdded");
|
2020-07-17 18:10:38 +02:00
|
|
|
|
goto fail;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dbus_message_iter_get_basic(&arg_i, &p);
|
|
|
|
|
|
|
|
|
|
|
|
dbus_message_iter_next(&arg_i);
|
|
|
|
|
|
spa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_ARRAY);
|
|
|
|
|
|
|
|
|
|
|
|
dbus_message_iter_recurse(&arg_i, &props_i);
|
|
|
|
|
|
|
|
|
|
|
|
return ofono_audio_card_found(backend, p, &props_i);
|
|
|
|
|
|
} else if (dbus_message_is_signal(m, OFONO_HF_AUDIO_MANAGER_INTERFACE, "CardRemoved")) {
|
|
|
|
|
|
const char *p;
|
2023-07-11 20:44:23 +02:00
|
|
|
|
spa_auto(DBusError) err = DBUS_ERROR_INIT;
|
2020-07-17 18:10:38 +02:00
|
|
|
|
|
|
|
|
|
|
if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &p, DBUS_TYPE_INVALID)) {
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_error(backend->log, "Failed to parse org.ofono.HandsfreeAudioManager.CardRemoved: %s", err.message);
|
2020-07-17 18:10:38 +02:00
|
|
|
|
goto fail;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return ofono_audio_card_removed(backend, p);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fail:
|
|
|
|
|
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-08-29 18:22:41 +03:00
|
|
|
|
static int add_filters(struct impl *backend)
|
2020-07-17 18:10:38 +02:00
|
|
|
|
{
|
|
|
|
|
|
if (backend->filters_added)
|
2021-03-18 21:48:10 +02:00
|
|
|
|
return 0;
|
2020-07-17 18:10:38 +02:00
|
|
|
|
|
|
|
|
|
|
if (!dbus_connection_add_filter(backend->conn, ofono_filter_cb, backend, NULL)) {
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_error(backend->log, "failed to add filter function");
|
2023-07-11 20:44:23 +02:00
|
|
|
|
return -EIO;
|
2020-07-17 18:10:38 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-07-11 20:44:23 +02:00
|
|
|
|
spa_auto(DBusError) err = DBUS_ERROR_INIT;
|
|
|
|
|
|
|
2020-07-17 18:10:38 +02:00
|
|
|
|
dbus_bus_add_match(backend->conn,
|
|
|
|
|
|
"type='signal',sender='" OFONO_SERVICE "',"
|
|
|
|
|
|
"interface='" OFONO_HF_AUDIO_MANAGER_INTERFACE "',member='CardAdded'", &err);
|
|
|
|
|
|
dbus_bus_add_match(backend->conn,
|
|
|
|
|
|
"type='signal',sender='" OFONO_SERVICE "',"
|
|
|
|
|
|
"interface='" OFONO_HF_AUDIO_MANAGER_INTERFACE "',member='CardRemoved'", &err);
|
|
|
|
|
|
|
|
|
|
|
|
backend->filters_added = true;
|
|
|
|
|
|
|
2021-03-18 21:48:10 +02:00
|
|
|
|
return 0;
|
2020-07-17 18:10:38 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-03-18 21:48:10 +02:00
|
|
|
|
static int backend_ofono_free(void *data)
|
2020-07-17 18:10:38 +02:00
|
|
|
|
{
|
2021-03-18 21:48:10 +02:00
|
|
|
|
struct impl *backend = data;
|
|
|
|
|
|
|
2021-04-10 18:10:37 +03:00
|
|
|
|
if (backend->filters_added) {
|
|
|
|
|
|
dbus_connection_remove_filter(backend->conn, ofono_filter_cb, backend);
|
|
|
|
|
|
backend->filters_added = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
bluez5: backend-ofono: don't do codec probe connections + add wait
Codec probe connections can trigger bad behavior from oFono if done when
device is busy (e.g. at connect), and they might be done at the same
time as A2DP transport is acquired which cannot work.
Also, oFono will not reply to DBus Acquire, if device does not complete
codec negotiation correctly. This is most likely to happen just after
device connect, when it is busy with other stuff (eg A2DP).
Remove codec probe connections altogether: instead, we guess mSBC if
mSBC is enabled and otherwise CVSD. If the guess turns out to be wrong,
which is unlikely (almost all devices have mSBC), we recreate the
transport with correct codec (from main loop, must not be done in
*_acquire because that can destroy nodes + unload the spa libs while
we're being called from there).
To avoid oFono DBus hangs at startup, add delay before marking the
profile connected, enforcing a time difference to A2DP operations.
2022-03-03 23:32:33 +02:00
|
|
|
|
if (backend->timer)
|
|
|
|
|
|
spa_loop_utils_destroy_source(backend->loop_utils, backend->timer);
|
|
|
|
|
|
|
2020-07-17 18:10:38 +02:00
|
|
|
|
dbus_connection_unregister_object_path(backend->conn, OFONO_AUDIO_CLIENT);
|
|
|
|
|
|
|
|
|
|
|
|
free(backend);
|
2021-03-18 21:48:10 +02:00
|
|
|
|
|
|
|
|
|
|
return 0;
|
2020-07-17 18:10:38 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-03-18 21:48:10 +02:00
|
|
|
|
static const struct spa_bt_backend_implementation backend_impl = {
|
|
|
|
|
|
SPA_VERSION_BT_BACKEND_IMPLEMENTATION,
|
|
|
|
|
|
.free = backend_ofono_free,
|
|
|
|
|
|
.register_profiles = backend_ofono_register,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2021-08-29 18:22:41 +03:00
|
|
|
|
static bool is_available(struct impl *backend)
|
|
|
|
|
|
{
|
2023-07-11 19:24:46 +02:00
|
|
|
|
spa_autoptr(DBusMessage) m = NULL, r = NULL;
|
2023-07-11 20:44:23 +02:00
|
|
|
|
spa_auto(DBusError) err = DBUS_ERROR_INIT;
|
2021-08-29 18:22:41 +03:00
|
|
|
|
|
|
|
|
|
|
m = dbus_message_new_method_call(OFONO_SERVICE, "/",
|
|
|
|
|
|
DBUS_INTERFACE_INTROSPECTABLE, "Introspect");
|
|
|
|
|
|
if (m == NULL)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
r = dbus_connection_send_with_reply_and_block(backend->conn, m, -1, &err);
|
|
|
|
|
|
if (r && dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_METHOD_RETURN)
|
2023-07-11 19:24:46 +02:00
|
|
|
|
return true;
|
2021-08-29 18:22:41 +03:00
|
|
|
|
|
2023-07-11 19:24:46 +02:00
|
|
|
|
return false;
|
2021-08-29 18:22:41 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-17 18:10:38 +02:00
|
|
|
|
struct spa_bt_backend *backend_ofono_new(struct spa_bt_monitor *monitor,
|
2020-07-24 15:26:07 +02:00
|
|
|
|
void *dbus_connection,
|
2020-12-08 15:19:17 +01:00
|
|
|
|
const struct spa_dict *info,
|
2021-01-24 15:15:27 +02:00
|
|
|
|
const struct spa_bt_quirks *quirks,
|
2020-07-17 18:10:38 +02:00
|
|
|
|
const struct spa_support *support,
|
2020-11-29 16:38:36 +01:00
|
|
|
|
uint32_t n_support)
|
2020-07-17 18:10:38 +02:00
|
|
|
|
{
|
2021-03-18 21:48:10 +02:00
|
|
|
|
struct impl *backend;
|
2020-12-08 15:19:17 +01:00
|
|
|
|
const char *str;
|
2020-07-17 18:10:38 +02:00
|
|
|
|
static const DBusObjectPathVTable vtable_profile = {
|
|
|
|
|
|
.message_function = ofono_handler,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2021-03-18 21:48:10 +02:00
|
|
|
|
backend = calloc(1, sizeof(struct impl));
|
2020-07-17 18:10:38 +02:00
|
|
|
|
if (backend == NULL)
|
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
2021-03-18 21:48:10 +02:00
|
|
|
|
spa_bt_backend_set_implementation(&backend->this, &backend_impl, backend);
|
|
|
|
|
|
|
2021-08-29 18:22:41 +03:00
|
|
|
|
backend->this.name = "ofono";
|
|
|
|
|
|
backend->this.exclusive = true;
|
2020-07-17 18:10:38 +02:00
|
|
|
|
backend->monitor = monitor;
|
2021-01-24 15:15:27 +02:00
|
|
|
|
backend->quirks = quirks;
|
2020-07-17 18:10:38 +02:00
|
|
|
|
backend->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
|
|
|
|
|
|
backend->dbus = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DBus);
|
|
|
|
|
|
backend->main_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Loop);
|
bluez5: backend-ofono: don't do codec probe connections + add wait
Codec probe connections can trigger bad behavior from oFono if done when
device is busy (e.g. at connect), and they might be done at the same
time as A2DP transport is acquired which cannot work.
Also, oFono will not reply to DBus Acquire, if device does not complete
codec negotiation correctly. This is most likely to happen just after
device connect, when it is busy with other stuff (eg A2DP).
Remove codec probe connections altogether: instead, we guess mSBC if
mSBC is enabled and otherwise CVSD. If the guess turns out to be wrong,
which is unlikely (almost all devices have mSBC), we recreate the
transport with correct codec (from main loop, must not be done in
*_acquire because that can destroy nodes + unload the spa libs while
we're being called from there).
To avoid oFono DBus hangs at startup, add delay before marking the
profile connected, enforcing a time difference to A2DP operations.
2022-03-03 23:32:33 +02:00
|
|
|
|
backend->main_system = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_System);
|
|
|
|
|
|
backend->loop_utils = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_LoopUtils);
|
2020-07-24 15:26:07 +02:00
|
|
|
|
backend->conn = dbus_connection;
|
2021-01-24 15:15:27 +02:00
|
|
|
|
if (info && (str = spa_dict_lookup(info, "bluez5.enable-msbc")))
|
2021-05-18 15:18:14 +10:00
|
|
|
|
backend->msbc_supported = spa_atob(str);
|
2020-12-08 15:19:17 +01:00
|
|
|
|
else
|
|
|
|
|
|
backend->msbc_supported = false;
|
2020-07-17 18:10:38 +02:00
|
|
|
|
|
2021-10-01 19:03:49 +03:00
|
|
|
|
spa_log_topic_init(backend->log, &log_topic);
|
|
|
|
|
|
|
bluez5: backend-ofono: don't do codec probe connections + add wait
Codec probe connections can trigger bad behavior from oFono if done when
device is busy (e.g. at connect), and they might be done at the same
time as A2DP transport is acquired which cannot work.
Also, oFono will not reply to DBus Acquire, if device does not complete
codec negotiation correctly. This is most likely to happen just after
device connect, when it is busy with other stuff (eg A2DP).
Remove codec probe connections altogether: instead, we guess mSBC if
mSBC is enabled and otherwise CVSD. If the guess turns out to be wrong,
which is unlikely (almost all devices have mSBC), we recreate the
transport with correct codec (from main loop, must not be done in
*_acquire because that can destroy nodes + unload the spa libs while
we're being called from there).
To avoid oFono DBus hangs at startup, add delay before marking the
profile connected, enforcing a time difference to A2DP operations.
2022-03-03 23:32:33 +02:00
|
|
|
|
backend->timer = spa_loop_utils_add_timer(backend->loop_utils, activate_timer_event, backend);
|
|
|
|
|
|
if (backend->timer == NULL) {
|
|
|
|
|
|
free(backend);
|
|
|
|
|
|
return NULL;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-17 18:10:38 +02:00
|
|
|
|
if (!dbus_connection_register_object_path(backend->conn,
|
|
|
|
|
|
OFONO_AUDIO_CLIENT,
|
|
|
|
|
|
&vtable_profile, backend)) {
|
2020-11-29 16:38:36 +01:00
|
|
|
|
free(backend);
|
2020-07-17 18:10:38 +02:00
|
|
|
|
return NULL;
|
2020-11-29 16:38:36 +01:00
|
|
|
|
}
|
2020-07-17 18:10:38 +02:00
|
|
|
|
|
2021-08-29 18:22:41 +03:00
|
|
|
|
if (add_filters(backend) < 0) {
|
|
|
|
|
|
dbus_connection_unregister_object_path(backend->conn, OFONO_AUDIO_CLIENT);
|
|
|
|
|
|
free(backend);
|
|
|
|
|
|
return NULL;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
backend->this.available = is_available(backend);
|
|
|
|
|
|
|
2021-03-18 21:48:10 +02:00
|
|
|
|
return &backend->this;
|
2020-07-17 18:10:38 +02:00
|
|
|
|
}
|