[pulseaudio-discuss] [PATCH v2 1/7] bluetooth: wait for all profiles to connect before creating card
Arun Raghavan
arun at arunraghavan.net
Wed Aug 10 15:25:39 UTC 2016
On Sun, 7 Aug 2016, at 09:15 PM, Tanu Kaskinen wrote:
> The CONNECTION_CHANGED hook is used to notify the discovery module
> about new and removed devices. When a bluetooth device connects, the
> hook used to be called immediately when the first profile connected.
> That meant that only one profile was marked as available during the
> card creation, other profiles would get marked as available later.
>
> That makes it hard for module-card-restore to restore the saved
> profile, if the saved profile becomes available with some delay.
> module-card-restore has a workaround for this problem, but that turned
> out to interfere with module-bluetooth-policy, so the workaround will
> be removed in the next patch.
>
> The BlueZ 4 code doesn't need changes, because we use the
> org.bluez.Audio interface to get a notification when all profiles are
> connected.
> ---
Except one trivial comment I have below, the first 4 patches of the
series look okay to me. I'm not too fussed about changes to the bluez4
side of things, so unless there's something that you'd specifically like
a second pair of eyes on, if it works, just go ahead and push it.
-- Arun
> src/modules/bluetooth/bluez5-util.c | 120
> +++++++++++++++++++++++++++++++++++-
> src/modules/bluetooth/bluez5-util.h | 2 +
> 2 files changed, 120 insertions(+), 2 deletions(-)
>
> diff --git a/src/modules/bluetooth/bluez5-util.c
> b/src/modules/bluetooth/bluez5-util.c
> index 03c76bf..f6c18e7 100644
> --- a/src/modules/bluetooth/bluez5-util.c
> +++ b/src/modules/bluetooth/bluez5-util.c
> @@ -21,6 +21,8 @@
> #include <config.h>
> #endif
>
> +#include <pulse/rtclock.h>
> +#include <pulse/timeval.h>
> #include <pulse/xmalloc.h>
>
> #include <pulsecore/core.h>
> @@ -35,6 +37,8 @@
>
> #include "bluez5-util.h"
>
> +#define WAIT_FOR_PROFILES_TIMEOUT_USEC (3 * PA_USEC_PER_SEC)
> +
> #define BLUEZ_SERVICE "org.bluez"
> #define BLUEZ_ADAPTER_INTERFACE BLUEZ_SERVICE ".Adapter1"
> #define BLUEZ_DEVICE_INTERFACE BLUEZ_SERVICE ".Device1"
> @@ -164,6 +168,97 @@ static const char
> *transport_state_to_string(pa_bluetooth_transport_state_t stat
> return "invalid";
> }
>
> +static bool device_supports_profile(pa_bluetooth_device *device,
> pa_bluetooth_profile_t profile) {
> + switch (profile) {
> + case PA_BLUETOOTH_PROFILE_A2DP_SINK:
> + return !!pa_hashmap_get(device->uuids,
> PA_BLUETOOTH_UUID_A2DP_SINK);
> + case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
> + return !!pa_hashmap_get(device->uuids,
> PA_BLUETOOTH_UUID_A2DP_SOURCE);
> + case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT:
> + return !!pa_hashmap_get(device->uuids,
> PA_BLUETOOTH_UUID_HSP_HS)
> + || !!pa_hashmap_get(device->uuids,
> PA_BLUETOOTH_UUID_HFP_HF);
> + case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY:
> + return !!pa_hashmap_get(device->uuids,
> PA_BLUETOOTH_UUID_HSP_AG)
> + || !!pa_hashmap_get(device->uuids,
> PA_BLUETOOTH_UUID_HFP_AG);
> + case PA_BLUETOOTH_PROFILE_OFF:
> + pa_assert_not_reached();
> + }
> +
> + pa_assert_not_reached();
> +}
> +
> +static bool device_is_profile_connected(pa_bluetooth_device *device,
> pa_bluetooth_profile_t profile) {
> + if (device->transports[profile] &&
> device->transports[profile]->state !=
> PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED)
> + return true;
> + else
> + return false;
> +}
> +
> +static unsigned device_count_disconnected_profiles(pa_bluetooth_device
> *device) {
> + pa_bluetooth_profile_t profile;
> + unsigned count = 0;
> +
> + for (profile = 0; profile < PA_BLUETOOTH_PROFILE_COUNT; profile++) {
> + if (!device_supports_profile(device, profile))
> + continue;
> +
> + if (!device_is_profile_connected(device, profile))
> + count++;
> + }
> +
> + return count;
> +}
> +
> +static void device_stop_waiting_for_profiles(pa_bluetooth_device
> *device);
I think it's better to just have the code here rather than just the
declaration.
> +static void wait_for_profiles_cb(pa_mainloop_api *api, pa_time_event*
> event, const struct timeval *tv, void *userdata) {
> + pa_bluetooth_device *device = userdata;
> + pa_strbuf *buf;
> + pa_bluetooth_profile_t profile;
> + bool first = true;
> + char *profiles_str;
> +
> + device_stop_waiting_for_profiles(device);
> +
> + buf = pa_strbuf_new();
> +
> + for (profile = 0; profile < PA_BLUETOOTH_PROFILE_COUNT; profile++) {
> + if (device_is_profile_connected(device, profile))
> + continue;
> +
> + if (!device_supports_profile(device, profile))
> + continue;
> +
> + if (first)
> + first = false;
> + else
> + pa_strbuf_puts(buf, ", ");
> +
> + pa_strbuf_puts(buf, pa_bluetooth_profile_to_string(profile));
> + }
> +
> + profiles_str = pa_strbuf_to_string_free(buf);
> + pa_log_debug("Timeout expired, and device %s still has disconnected
> profiles: %s",
> + device->path, profiles_str);
> + pa_xfree(profiles_str);
> +
> pa_hook_fire(&device->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED],
> device);
> +}
> +
> +static void device_start_waiting_for_profiles(pa_bluetooth_device
> *device) {
> + pa_assert(!device->wait_for_profiles_timer);
> + device->wait_for_profiles_timer =
> pa_core_rttime_new(device->discovery->core,
> +
> pa_rtclock_now() + WAIT_FOR_PROFILES_TIMEOUT_USEC,
> +
> wait_for_profiles_cb, device);
> +}
> +
> +static void device_stop_waiting_for_profiles(pa_bluetooth_device
> *device) {
> + if (!device->wait_for_profiles_timer)
> + return;
> +
> +
> device->discovery->core->mainloop->time_free(device->wait_for_profiles_timer);
> + device->wait_for_profiles_timer = NULL;
> +}
> +
> void pa_bluetooth_transport_set_state(pa_bluetooth_transport *t,
> pa_bluetooth_transport_state_t state) {
> bool old_any_connected;
>
> @@ -181,8 +276,27 @@ void
> pa_bluetooth_transport_set_state(pa_bluetooth_transport *t,
> pa_bluetooth_tr
>
> pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED],
> t);
>
> - if (old_any_connected !=
> pa_bluetooth_device_any_transport_connected(t->device))
> -
> pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED],
> t->device);
> + if (old_any_connected !=
> pa_bluetooth_device_any_transport_connected(t->device)) {
> + unsigned n_disconnected_profiles;
> +
> + /* If there are profiles that are expected to get connected soon
> (based
> + * on the UUID list), we wait for a bit before announcing the
> new
> + * device, so that all profiles have time to get connected
> before the
> + * card object is created. If we didn't wait, the card would
> always
> + * have only one profile marked as available in the initial
> state,
> + * which would prevent module-card-restore from restoring the
> initial
> + * profile properly. */
> +
> + n_disconnected_profiles =
> device_count_disconnected_profiles(t->device);
> +
> + if (n_disconnected_profiles == 0)
> + device_stop_waiting_for_profiles(t->device);
> +
> + if (!old_any_connected && n_disconnected_profiles > 0)
> + device_start_waiting_for_profiles(t->device);
> + else
> +
> pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED],
> t->device);
> + }
> }
>
> void pa_bluetooth_transport_put(pa_bluetooth_transport *t) {
> @@ -416,6 +530,8 @@ static void device_free(pa_bluetooth_device *d) {
>
> pa_assert(d);
>
> + device_stop_waiting_for_profiles(d);
> +
> for (i = 0; i < PA_BLUETOOTH_PROFILE_COUNT; i++) {
> pa_bluetooth_transport *t;
>
> diff --git a/src/modules/bluetooth/bluez5-util.h
> b/src/modules/bluetooth/bluez5-util.h
> index d66e8a3..2aa6ea6 100644
> --- a/src/modules/bluetooth/bluez5-util.h
> +++ b/src/modules/bluetooth/bluez5-util.h
> @@ -105,6 +105,8 @@ struct pa_bluetooth_device {
> pa_hashmap *uuids;
>
> pa_bluetooth_transport *transports[PA_BLUETOOTH_PROFILE_COUNT];
> +
> + pa_time_event *wait_for_profiles_timer;
> };
>
> struct pa_bluetooth_adapter {
> --
> 2.8.1
>
> _______________________________________________
> pulseaudio-discuss mailing list
> pulseaudio-discuss at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss
More information about the pulseaudio-discuss
mailing list