bluez: fix a2dp source

Actually compile the codec config and declare the config as extern
or else it's just all 0.
Only acquire the transport when ACTIVE.
Implement transport state_change signal and acquire the transport
when going to pending/active.
Try to acquire the transport before we expose the device if we
can.
Force device expose when we get the device ServicesResolved=1 property
update.
Free transport when the rfcomm is closed.
Implement various DBus signals to detect dynamic property updates.
This commit is contained in:
Wim Taymans 2019-05-16 13:18:45 +02:00
parent c348790ca1
commit 6cfddde39c
6 changed files with 213 additions and 34 deletions

View file

@ -434,13 +434,16 @@ static int adapter_update_props(struct spa_bt_adapter *adapter,
while (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID) {
const char *uuid;
enum spa_bt_profile profile;
dbus_message_iter_get_basic(&iter, &uuid);
spa_log_debug(monitor->log, "adapter %p: add UUID=%s", adapter, uuid);
adapter->profiles |= spa_bt_profile_from_uuid(uuid);
profile = spa_bt_profile_from_uuid(uuid);
if (profile && (adapter->profiles & profile) == 0) {
spa_log_debug(monitor->log, "adapter %p: add UUID=%s", adapter, uuid);
adapter->profiles |= profile;
}
dbus_message_iter_next(&iter);
}
}
@ -615,7 +618,7 @@ static int device_stop_timer(struct spa_bt_device *device)
return 0;
}
static int check_profiles(struct spa_bt_device *device)
static int device_check_profiles(struct spa_bt_device *device, bool force)
{
struct spa_bt_monitor *monitor = device->monitor;
uint32_t connected_profiles = device->connected_profiles;
@ -634,7 +637,7 @@ static int check_profiles(struct spa_bt_device *device)
device_remove(monitor, device);
}
}
else if ((device->profiles & connected_profiles) == device->profiles) {
else if (force || (device->profiles & connected_profiles) == device->profiles) {
device_stop_timer(device);
device_add(monitor, device);
} else {
@ -651,7 +654,7 @@ static void device_set_connected(struct spa_bt_device *device, int connected)
device->connected = connected;
if (connected)
check_profiles(device);
device_check_profiles(device, false);
else
device_stop_timer(device);
}
@ -659,7 +662,7 @@ static void device_set_connected(struct spa_bt_device *device, int connected)
static int device_connect_profile(struct spa_bt_device *device, enum spa_bt_profile profile)
{
device->connected_profiles |= profile;
check_profiles(device);
device_check_profiles(device, false);
return 0;
}
@ -720,7 +723,7 @@ static int device_update_props(struct spa_bt_device *device,
dbus_message_iter_get_basic(&it[1], &value);
spa_log_debug(monitor->log, "device %p: %s=%d", device, key, value);
spa_log_debug(monitor->log, "device %p: %s=%08x", device, key, value);
if (strcmp(key, "Class") == 0)
device->bluetooth_class = value;
@ -764,6 +767,10 @@ static int device_update_props(struct spa_bt_device *device,
else if (strcmp(key, "Blocked") == 0) {
device->blocked = value;
}
else if (strcmp(key, "ServicesResolved") == 0) {
if (value)
device_check_profiles(device, true);
}
}
else if (strcmp(key, "UUIDs") == 0) {
DBusMessageIter iter;
@ -775,13 +782,15 @@ static int device_update_props(struct spa_bt_device *device,
while (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID) {
const char *uuid;
enum spa_bt_profile profile;
dbus_message_iter_get_basic(&iter, &uuid);
spa_log_debug(monitor->log, "device %p: add UUID=%s", device, uuid);
device->profiles |= spa_bt_profile_from_uuid(uuid);
profile = spa_bt_profile_from_uuid(uuid);
if (profile && (device->profiles & profile) == 0) {
spa_log_debug(monitor->log, "device %p: add UUID=%s", device, uuid);
device->profiles |= profile;
}
dbus_message_iter_next(&iter);
}
}
@ -821,9 +830,26 @@ static struct spa_bt_transport *transport_create(struct spa_bt_monitor *monitor,
return t;
}
static void transport_set_state(struct spa_bt_transport *transport, enum spa_bt_transport_state state)
{
struct spa_bt_monitor *monitor = transport->monitor;
enum spa_bt_transport_state old = transport->state;
if (old != state) {
transport->state = state;
spa_log_debug(monitor->log, "transport %p: %s state changed %d -> %d",
transport, transport->path, old, state);
spa_bt_transport_emit_state_changed(transport, old, state);
}
}
static void transport_free(struct spa_bt_transport *transport)
{
struct spa_bt_monitor *monitor = transport->monitor;
spa_log_debug(monitor->log, "transport %p: free %s", transport, transport->path);
transport_set_state(transport, SPA_BT_TRANSPORT_STATE_IDLE);
spa_bt_transport_emit_destroy(transport);
@ -877,7 +903,7 @@ static int transport_update_props(struct spa_bt_transport *transport,
}
}
else if (strcmp(key, "State") == 0) {
transport->state = spa_bt_transport_state_from_string(value);
transport_set_state(transport, spa_bt_transport_state_from_string(value));
}
else if (strcmp(key, "Device") == 0) {
transport->device = device_find(monitor, value);
@ -960,7 +986,6 @@ static int transport_acquire(void *data, bool optional)
spa_log_error(monitor->log, "Transport %s() failed for transport %s (%s)",
method, transport->path, err.message);
}
dbus_error_free(&err);
return -EIO;
}
@ -1117,7 +1142,7 @@ static DBusHandlerResult endpoint_clear_configuration(DBusConnection *conn, DBus
transport_free(transport);
if (device != NULL)
check_profiles(device);
device_check_profiles(device, false);
}
if ((r = dbus_message_new_method_return(m)) == NULL)
@ -1424,7 +1449,10 @@ static void rfcomm_event(struct spa_source *source)
spa_log_error(monitor->log, "RFCOMM write error: %s", strerror(errno));
}
}
return;
fail:
transport_free(t);
return;
}
@ -1724,6 +1752,8 @@ static DBusHandlerResult profile_new_connection(DBusConnection *conn, DBusMessag
sco_listen(t);
spa_log_debug(monitor->log, "Transport %s available for profile %s", t->path, handler);
if ((r = dbus_message_new_method_return(m)) == NULL)
return DBUS_HANDLER_RESULT_NEED_MEMORY;
if (!dbus_connection_send(conn, r, NULL))
@ -1775,7 +1805,7 @@ static DBusHandlerResult profile_request_disconnection(DBusConnection *conn, DBu
if (t->profile == profile)
transport_free(t);
}
check_profiles(d);
device_check_profiles(d, false);
if ((r = dbus_message_new_method_return(m)) == NULL)
return DBUS_HANDLER_RESULT_NEED_MEMORY;
@ -2039,6 +2069,120 @@ static void get_managed_objects(struct spa_bt_monitor *monitor)
dbus_message_unref(m);
}
static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *user_data)
{
struct spa_bt_monitor *monitor = user_data;
DBusError err;
dbus_error_init(&err);
if (dbus_message_is_signal(m, "org.freedesktop.DBus", "NameOwnerChanged")) {
spa_log_debug(monitor->log, "Name owner changed %s", dbus_message_get_path(m));
} else if (dbus_message_is_signal(m, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded")) {
spa_log_debug(monitor->log, "interfaces added %s", dbus_message_get_path(m));
} else if (dbus_message_is_signal(m, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved")) {
spa_log_debug(monitor->log, "interfaces removed %s", dbus_message_get_path(m));
} else if (dbus_message_is_signal(m, "org.freedesktop.DBus.Properties", "PropertiesChanged")) {
DBusMessageIter it[2];
const char *iface, *path;
if (!dbus_message_iter_init(m, &it[0]) ||
strcmp(dbus_message_get_signature(m), "sa{sv}as") != 0) {
spa_log_error(monitor->log, "Invalid signature found in PropertiesChanged");
goto fail;
}
path = dbus_message_get_path(m);
dbus_message_iter_get_basic(&it[0], &iface);
dbus_message_iter_next(&it[0]);
dbus_message_iter_recurse(&it[0], &it[1]);
if (strcmp(iface, BLUEZ_ADAPTER_INTERFACE) == 0) {
struct spa_bt_adapter *a;
a = adapter_find(monitor, path);
if (a == NULL) {
spa_log_warn(monitor->log,
"Properties changed in unknown adapter %s", path);
goto fail;
}
spa_log_debug(monitor->log, "Properties changed in adapter %s", path);
adapter_update_props(a, &it[1], NULL);
}
else if (strcmp(iface, BLUEZ_DEVICE_INTERFACE) == 0) {
struct spa_bt_device *d;
d = device_find(monitor, path);
if (d == NULL) {
spa_log_warn(monitor->log,
"Properties changed in unknown device %s", path);
goto fail;
}
spa_log_debug(monitor->log, "Properties changed in device %s", path);
device_update_props(d, &it[1], NULL);
}
else if (strcmp(iface, BLUEZ_MEDIA_TRANSPORT_INTERFACE) == 0) {
struct spa_bt_transport *transport;
transport = transport_find(monitor, path);
if (transport == NULL) {
spa_log_warn(monitor->log,
"Properties changed in unknown transport %s", path);
goto fail;
}
spa_log_debug(monitor->log, "Properties changed in transport %s", path);
transport_update_props(transport, &it[1], NULL);
}
}
fail:
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
static void add_filters(struct spa_bt_monitor *this)
{
DBusError err;
dbus_error_init(&err);
if (!dbus_connection_add_filter(this->conn, filter_cb, this, NULL)) {
spa_log_error(this->log, "failed to add filter function");
goto fail;
}
dbus_bus_add_match(this->conn,
"type='signal',sender='org.freedesktop.DBus',"
"interface='org.freedesktop.DBus',member='NameOwnerChanged',"
"arg0='" BLUEZ_SERVICE "'", &err);
dbus_bus_add_match(this->conn,
"type='signal',sender='" BLUEZ_SERVICE "',"
"interface='org.freedesktop.DBus.ObjectManager',member='InterfacesAdded'", &err);
dbus_bus_add_match(this->conn,
"type='signal',sender='" BLUEZ_SERVICE "',"
"interface='org.freedesktop.DBus.ObjectManager',member='InterfacesRemoved'", &err);
dbus_bus_add_match(this->conn,
"type='signal',sender='" BLUEZ_SERVICE "',"
"interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',"
"arg0='" BLUEZ_ADAPTER_INTERFACE "'", &err);
dbus_bus_add_match(this->conn,
"type='signal',sender='" BLUEZ_SERVICE "',"
"interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',"
"arg0='" BLUEZ_DEVICE_INTERFACE "'", &err);
dbus_bus_add_match(this->conn,
"type='signal',sender='" BLUEZ_SERVICE "',"
"interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',"
"arg0='" BLUEZ_MEDIA_TRANSPORT_INTERFACE "'", &err);
return;
fail:
dbus_error_free(&err);
}
static int
impl_monitor_set_callbacks(struct spa_monitor *monitor,
const struct spa_monitor_callbacks *callbacks,
@ -2053,6 +2197,7 @@ impl_monitor_set_callbacks(struct spa_monitor *monitor,
this->callbacks = SPA_CALLBACKS_INIT(callbacks, data);
if (callbacks) {
add_filters(this);
get_managed_objects(this);
}