bluez5: a2dp-source: release transport if it went idle

Release the transport if it went idle, ensuring that the fd is closed,
and add safeguards we won't double-acquire/release it.

This can occur if the device pauses the playback. The transport may also
activate again later on, and in this case we need to reacquire a new fd.
Not closing the old fd causes problems in this case.

However, apparently the BlueZ Release() call fails if the transport is
idle. We just ignore the error and downgrade the error message; it might
not be safe to not call Release() because the idle property update is
async.
This commit is contained in:
Pauli Virtanen 2021-02-07 02:41:07 +02:00 committed by Wim Taymans
parent d3d31c4317
commit f99eefeb4f
2 changed files with 45 additions and 12 deletions

View file

@ -112,6 +112,7 @@ struct impl {
struct port port;
unsigned int started:1;
unsigned int transport_acquired:1;
unsigned int following:1;
struct spa_source source;
@ -539,11 +540,16 @@ static int transport_start(struct impl *this)
int res, val;
struct port *port = &this->port;
if (this->transport_acquired)
return 0;
spa_log_debug(this->log, NAME" %p: transport %p acquire", this,
this->transport);
if ((res = spa_bt_transport_acquire(this->transport, false)) < 0)
return res;
this->transport_acquired = true;
this->codec_data = this->codec->init(this->codec, 0,
this->transport->configuration,
this->transport->configuration_len,
@ -622,6 +628,28 @@ static int do_remove_source(struct spa_loop *loop,
return 0;
}
static int transport_stop(struct impl *this)
{
int res;
spa_log_debug(this->log, NAME" %p: transport stop", this);
spa_loop_invoke(this->data_loop, do_remove_source, 0, NULL, 0, true, this);
if (this->transport && this->transport_acquired)
res = spa_bt_transport_release(this->transport);
else
res = 0;
this->transport_acquired = false;
if (this->codec_data)
this->codec->deinit(this->codec_data);
this->codec_data = NULL;
return res;
}
static int do_stop(struct impl *this)
{
int res;
@ -631,19 +659,10 @@ static int do_stop(struct impl *this)
spa_log_debug(this->log, NAME" %p: stop", this);
spa_loop_invoke(this->data_loop, do_remove_source, 0, NULL, 0, true, this);
res = transport_stop(this);
this->started = false;
if (this->transport)
res = spa_bt_transport_release(this->transport);
else
res = 0;
if (this->codec_data)
this->codec->deinit(this->codec_data);
this->codec_data = NULL;
return res;
}
@ -1139,6 +1158,7 @@ static int do_transport_destroy(struct spa_loop *loop,
{
struct impl *this = user_data;
this->transport = NULL;
this->transport_acquired = false;
return 0;
}
@ -1158,6 +1178,8 @@ static void transport_state_changed(void *data, enum spa_bt_transport_state old,
if (state >= SPA_BT_TRANSPORT_STATE_PENDING && old < SPA_BT_TRANSPORT_STATE_PENDING)
transport_start(this);
else if (state < SPA_BT_TRANSPORT_STATE_PENDING && old >= SPA_BT_TRANSPORT_STATE_PENDING)
transport_stop(this);
}
static const struct spa_bt_transport_events transport_events = {

View file

@ -1422,6 +1422,7 @@ static int transport_release(void *data)
struct spa_bt_monitor *monitor = transport->monitor;
DBusMessage *m, *r;
DBusError err;
bool is_idle = (transport->state == SPA_BT_TRANSPORT_STATE_IDLE);
spa_log_debug(monitor->log, NAME": transport %p: Release %s",
transport, transport->path);
@ -1446,8 +1447,18 @@ static int transport_release(void *data)
dbus_message_unref(r);
if (dbus_error_is_set(&err)) {
spa_log_error(monitor->log, "Failed to release transport %s: %s",
transport->path, err.message);
if (is_idle) {
/* XXX: The fd always needs to be closed. However, Release()
* XXX: apparently doesn't need to be called on idle transports
* XXX: and fails. We call it just to be sure (e.g. in case
* XXX: there's a race with updating the property), but tone down the error.
*/
spa_log_debug(monitor->log, "Failed to release idle transport %s: %s",
transport->path, err.message);
} else {
spa_log_error(monitor->log, "Failed to release transport %s: %s",
transport->path, err.message);
}
dbus_error_free(&err);
}
else