From: Mikel Astiz <mikel.astiz@xxxxxxxxxxxx> Load module-loopback only while the sink or source is not suspended. If it enters suspend state after the module was loaded, this module should be unloaded. This patch is required in order to avoid module-bluetooth-policy always resuming an IDLE device, therefore undesirably requesting the SCO link for HFGW use-cases. --- src/modules/bluetooth/module-bluetooth-policy.c | 43 ++++++++++++++++++++--- 1 files changed, 38 insertions(+), 5 deletions(-) diff --git a/src/modules/bluetooth/module-bluetooth-policy.c b/src/modules/bluetooth/module-bluetooth-policy.c index c5db1d3..a53d47d 100644 --- a/src/modules/bluetooth/module-bluetooth-policy.c +++ b/src/modules/bluetooth/module-bluetooth-policy.c @@ -54,6 +54,9 @@ struct userdata { bool enable_hfgw; pa_hook_slot *source_put_slot; pa_hook_slot *sink_put_slot; + pa_hook_slot *source_state_changed_slot; + pa_hook_slot *sink_state_changed_slot; + pa_hashmap *loaded_modules; }; static const char *get_bluetooth_protocol(pa_proplist *p) { @@ -72,6 +75,8 @@ static const char *get_bluetooth_protocol(pa_proplist *p) { static pa_hook_result_t device_hook_callback(pa_core *c, pa_object *o, struct userdata *u) { char *args; + pa_module *module; + void *old_module; pa_assert(c); pa_assert(o); @@ -79,11 +84,12 @@ static pa_hook_result_t device_hook_callback(pa_core *c, pa_object *o, struct us if (pa_sink_isinstance(o)) { pa_sink *sink = PA_SINK(o); + pa_sink_state_t state = pa_sink_get_state(sink); const char *protocol = get_bluetooth_protocol(sink->proplist); const char *role; - if (protocol == NULL) - return PA_HOOK_OK; + if (protocol == NULL || state != PA_SINK_IDLE) + goto unload; if (u->enable_hfgw && pa_streq(protocol, "hfgw")) /* HFP profile (we're doing headset role) */ role = "phone"; @@ -95,11 +101,12 @@ static pa_hook_result_t device_hook_callback(pa_core *c, pa_object *o, struct us args = pa_sprintf_malloc("sink=\"%s\" sink_dont_move=\"true\" source_output_properties=\"media.role=%s\"", sink->name, role); } else if (pa_source_isinstance(o)) { pa_source *source = PA_SOURCE(o); + pa_source_state_t state = pa_source_get_state(source); const char *protocol = get_bluetooth_protocol(source->proplist); const char *role; - if (protocol == NULL) - return PA_HOOK_OK; + if (protocol == NULL || state != PA_SOURCE_IDLE) + goto unload; if (u->enable_a2dp_source && pa_streq(protocol, "a2dp_source")) /* A2DP profile (we're doing sink role) */ role = "music"; @@ -114,11 +121,24 @@ static pa_hook_result_t device_hook_callback(pa_core *c, pa_object *o, struct us } else return PA_HOOK_OK; + if (pa_hashmap_get(u->loaded_modules, o) != NULL) { + pa_xfree(args); + return PA_HOOK_OK; + } + /* Load module-loopback */ - (void) pa_module_load(c, "module-loopback", args); + if ((module = pa_module_load(c, "module-loopback", args)) != NULL) + pa_hashmap_put(u->loaded_modules, o, PA_UINT32_TO_PTR(module->index)); + pa_xfree(args); return PA_HOOK_OK; + +unload: + if ((old_module = pa_hashmap_remove(u->loaded_modules, o)) != NULL) + pa_module_unload_by_index(c, PA_PTR_TO_UINT32(old_module), TRUE); + + return PA_HOOK_OK; } int pa__init(pa_module *m) { @@ -134,6 +154,8 @@ int pa__init(pa_module *m) { m->userdata = u = pa_xnew(struct userdata, 1); + u->loaded_modules = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + u->enable_a2dp_source = TRUE; if (pa_modargs_get_value_boolean(ma, "a2dp_source", &u->enable_a2dp_source) < 0) { pa_log("Failed to parse a2dp_source argument."); @@ -150,6 +172,10 @@ int pa__init(pa_module *m) { u->sink_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_NORMAL, (pa_hook_cb_t) device_hook_callback, u); + u->sink_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) device_hook_callback, u); + + u->source_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) device_hook_callback, u); + pa_modargs_free(ma); return 0; @@ -172,5 +198,12 @@ void pa__done(pa_module *m) { if (u->sink_put_slot) pa_hook_slot_free(u->sink_put_slot); + if (u->source_state_changed_slot) + pa_hook_slot_free(u->source_state_changed_slot); + + if (u->sink_state_changed_slot) + pa_hook_slot_free(u->sink_state_changed_slot); + + pa_hashmap_free(u->loaded_modules, NULL, NULL); pa_xfree(u); } -- 1.7.7.6