diff options
author | Mikel Astiz <mikel.astiz@bmw-carit.de> | 2012-08-31 12:51:05 +0200 |
---|---|---|
committer | Arun Raghavan <arun.raghavan@collabora.co.uk> | 2012-09-26 19:27:14 +0800 |
commit | b8fd6f869ede117bf0a0b937c49c25c60c039b7c (patch) | |
tree | be3be62cd34b46b7cc8a4060db4e66c40f571959 /src | |
parent | 884d1d46ca5b563cae319003a2b3d0196dff05d1 (diff) |
bluetooth: Do not acquire transport during profile change
Until today, setting the card to some profile resulted in a transport
acquisition, leading to audio stream setup. This is generally not very
interesting and even undesireable for HFGW use-cases, where the
Gateway role (the remote end) would typically request the SCO link.
Nevertheless, there is no safe way to implement such check without race
conditions, since the BlueZ's state can change between the state report
and the call to Acquire(). The chances for this to reproduce are quite
low though, since interface state changes are relatively slow.
This race condition requires that BlueZ's API is extended in order to
perform the operation atomically, which has already been discussed and
ack-ed in the BlueZ mailing list.
Note that this patch does not introduce a new race condition, since it
already existed before (the PropertyChanged->Acquire race condition,
affecting HFGW use-cases). It is just more explicit now.
Diffstat (limited to 'src')
-rw-r--r-- | src/modules/bluetooth/module-bluetooth-device.c | 38 |
1 files changed, 38 insertions, 0 deletions
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c index c900979e1..6757e87bb 100644 --- a/src/modules/bluetooth/module-bluetooth-device.c +++ b/src/modules/bluetooth/module-bluetooth-device.c @@ -365,8 +365,26 @@ static void bt_transport_release(struct userdata *u) { teardown_stream(u); } +static pa_bt_audio_state_t get_profile_audio_state(const struct userdata *u, const pa_bluetooth_device *d) { + switch(u->profile) { + case PROFILE_HSP: + return d->headset_state; + case PROFILE_A2DP: + return d->audio_sink_state; + case PROFILE_A2DP_SOURCE: + return d->audio_source_state; + case PROFILE_HFGW: + return d->hfgw_state; + case PROFILE_OFF: + break; + } + + pa_assert_not_reached(); +} + static int bt_transport_acquire(struct userdata *u, pa_bool_t start) { const char *accesstype = "rw"; + const pa_bluetooth_device *d; const pa_bluetooth_transport *t; if (u->transport == NULL) { @@ -382,6 +400,12 @@ static int bt_transport_acquire(struct userdata *u, pa_bool_t start) { pa_log_debug("Acquiring transport %s", u->transport); + d = pa_bluetooth_discovery_get_by_path(u->discovery, u->path); + if (!d) { + pa_log_error("Failed to get device object."); + return -1; + } + t = pa_bluetooth_discovery_get_transport(u->discovery, u->transport); if (!t) { pa_log("Transport %s no longer available", u->transport); @@ -390,6 +414,20 @@ static int bt_transport_acquire(struct userdata *u, pa_bool_t start) { return -1; } + if (!start) { + /* FIXME: we are trying to acquire the transport only if the stream is + playing, without actually initiating the stream request from our side + (which is typically undesireable specially for hfgw use-cases. + However this approach is racy, since the stream could have been + suspended in the meantime, so we can't really guarantee that the + stream will not be requested until BlueZ's API supports this + atomically. */ + if (get_profile_audio_state(u, d) < PA_BT_AUDIO_STATE_PLAYING) { + pa_log_info("Failed optional acquire of transport %s", u->transport); + return -1; + } + } + u->stream_fd = pa_bluetooth_transport_acquire(t, accesstype, &u->read_link_mtu, &u->write_link_mtu); if (u->stream_fd < 0) { if (start) |