From: Mikel Astiz <mikel.astiz@xxxxxxxxxxxx> Commit 40329acc1a28145643e49207e9d65cd05bbda2c8 introduced a potential issue during headset pairing. If a headset supporting both HSP/HFP and A2DP is paired, and a late UUID (i.e. A2DP) is announced after HSP/HFP is already connected, the code in uuid_added_cb() can result in duplicated port creation and therefore an assertion failure: D: [pulseaudio] bluetooth-util.c: dbus: interface=org.bluez.Headset, path=/org/bluez/12840/hci0/dev_00_13_1E_1F_F1_53, member=PropertyChanged D: [pulseaudio] bluetooth-util.c: dbus: interface=org.bluez.MediaTransport, path=/org/bluez/12840/hci0/dev_00_13_1E_1F_F1_53/fd0, member=PropertyChanged D: [pulseaudio] bluetooth-util.c: dbus: interface=org.bluez.Device, path=/org/bluez/12840/hci0/dev_00_13_1E_1F_F1_53, member=PropertyChanged D: [pulseaudio] protocol-dbus.c: Interface org.PulseAudio.Core1.CardProfile added for object /org/pulseaudio/core1/card3/profile2 E: [pulseaudio] card.c: Assertion 'pa_hashmap_put(c->ports, p->name, p) >= 0' failed at pulsecore/card.c:107, function pa_card_add_ports(). Aborting. The issue is very unlikely to reproduce but gains interest if further ports needs to be merged in the future as well. --- src/modules/bluetooth/module-bluetooth-device.c | 42 +++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c index 33b2afa..9921892 100644 --- a/src/modules/bluetooth/module-bluetooth-device.c +++ b/src/modules/bluetooth/module-bluetooth-device.c @@ -2401,6 +2401,44 @@ static pa_bluetooth_device* find_device(struct userdata *u, const char *address, } /* Run from main thread */ +static void handle_duplicated_ports(struct userdata *u, pa_hashmap *new_ports) { + void *state; + pa_device_port *new_port; + pa_device_port *old_port; + + pa_assert(u); + pa_assert(new_ports); + + /* Late-announced UUIDs might create duplicated ports because the code in + * create_ports_for_profile() does not consider previously existing ports. + * The scenario is very unlikely but possible if a headset being paired + * connects HSP/HFP before the A2DP UUID is received. In this case the port + * "bluetooth-output" will already exist and therefore it has to be removed + * from the new_ports hashmap */ + PA_HASHMAP_FOREACH(new_port, new_ports, state) { + if ((old_port = pa_hashmap_get(u->card->ports, new_port->name)) == NULL) + continue; + + pa_assert(new_port->is_input == old_port->is_input); + pa_assert(new_port->is_output == old_port->is_output); + + /* FIXME: we should not modify the port's priority without a proper hook + * to propagate this change. However, the scenario is very unlikely and + * other extensions to the core are recently being discussed which would + * also fix this minor issue */ + old_port->priority = PA_MAX(old_port->priority, new_port->priority); + + if (new_port->available == PA_PORT_AVAILABLE_YES) + pa_device_port_set_available(old_port, PA_PORT_AVAILABLE_YES); + else if (new_port->available == PA_PORT_AVAILABLE_UNKNOWN && old_port->available == PA_PORT_AVAILABLE_NO) + pa_device_port_set_available(old_port, PA_PORT_AVAILABLE_UNKNOWN); + + pa_hashmap_remove(new_ports, new_port->name); + pa_device_port_unref(new_port); + } +} + +/* Run from main thread */ static pa_hook_result_t uuid_added_cb(pa_bluetooth_discovery *y, const struct pa_bluetooth_hook_uuid_data *data, struct userdata *u) { pa_card_profile *p; @@ -2414,6 +2452,8 @@ static pa_hook_result_t uuid_added_cb(pa_bluetooth_discovery *y, const struct pa if (data->device != u->device) return PA_HOOK_OK; + pa_log_info("Device %s: new UUID '%s'", data->device->path, data->uuid); + p = create_card_profile(u, data->uuid); if (!p) @@ -2430,6 +2470,8 @@ static pa_hook_result_t uuid_added_cb(pa_bluetooth_discovery *y, const struct pa create_ports_for_profile(u, new_ports, p); + handle_duplicated_ports(u, new_ports); + pa_card_add_ports(u->card, new_ports); pa_device_port_hashmap_free(new_ports); -- 1.8.1