From: Sangchul Lee Date: Tue, 18 Jul 2017 01:10:43 +0000 (+0900) Subject: tizenaudio-policy: Support to open Bluetooth SCO link with voice-recognition role X-Git-Tag: submit/tizen/20170721.075214~2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=22c2d55b2dbced849f76889c61f4e340c20ec19b;p=platform%2Fcore%2Fmultimedia%2Fpulseaudio-modules-tizen.git tizenaudio-policy: Support to open Bluetooth SCO link with voice-recognition role It is added to close the SCO link after 3 seconds from the request if there's no remain stream. Also, voice-information can sound out via Bluetooth SCO link only if 'bt-sco' device is in available device lists and the SCO is already opened. [Version] 5.0.163 [Issue Type] Enhancement Change-Id: I8342b1424b163f1f93ebf0e40d80d89733c4c551 Signed-off-by: Sangchul Lee --- diff --git a/packaging/pulseaudio-modules-tizen.spec b/packaging/pulseaudio-modules-tizen.spec index 113b246..cb38eed 100644 --- a/packaging/pulseaudio-modules-tizen.spec +++ b/packaging/pulseaudio-modules-tizen.spec @@ -1,6 +1,6 @@ Name: pulseaudio-modules-tizen Summary: Pulseaudio modules for Tizen -Version: 5.0.162 +Version: 5.0.163 Release: 0 Group: Multimedia/Audio License: LGPL-2.1+ diff --git a/src/module-tizenaudio-policy.c b/src/module-tizenaudio-policy.c index c82fbc5..bf55947 100644 --- a/src/module-tizenaudio-policy.c +++ b/src/module-tizenaudio-policy.c @@ -86,6 +86,8 @@ struct userdata; #define LOOPBACK_DEFAULT_LATENCY_MSEC 40 #define LOOPBACK_DEFAULT_ADJUST_SEC 3 +#define TIMED_BT_SCO_CLOSE_USEC 3000000 + /* Macros */ #define CONVERT_TO_HAL_DIRECTION(stream_type) \ ((stream_type == STREAM_SINK_INPUT) ? DIRECTION_OUT : DIRECTION_IN) @@ -173,6 +175,7 @@ struct userdata { pa_sink *sink; pa_source *source; } loopback_args; + pa_time_event *time_event_bt_sco_close; }; static void __load_dump_config(struct userdata *u) @@ -228,39 +231,126 @@ static pa_tz_device* _get_sco_connected_device(pa_device_manager *dm) { return NULL; } -/* Open/Close BT SCO if it is possible */ -static int update_bt_sco_state(pa_device_manager *dm, bool open) { +static bool is_bt_sco_opened(pa_device_manager *dm) { dm_device_bt_sco_status_t sco_status; pa_tz_device *bt_device; pa_assert(dm); bt_device = _get_sco_connected_device(dm); + if (bt_device == NULL) { + pa_log_debug("No SCO connected bt device"); + return false; + } + if (pa_tz_device_sco_get_status(bt_device, &sco_status) < 0) { + pa_log_error("get BT SCO status failed"); + return false; + } + if (sco_status != DM_DEVICE_BT_SCO_STATUS_OPENED) { + pa_log_error("SCO is not opened"); + return false; + } + pa_log_info("SCO is opened"); + + return true; +} + +static void timed_bt_sco_close_cb(pa_mainloop_api *a, pa_time_event *e, const struct timeval *t, void *userdata) { + struct userdata *u = userdata; + pa_tz_device *bt_device; + dm_device_bt_sco_status_t sco_status; + + pa_assert(u); + pa_assert(u->time_event_bt_sco_close == e); + + pa_log_info("timed_bt_sco_close_cb is called"); + + u->core->mainloop->time_free(u->time_event_bt_sco_close); + u->time_event_bt_sco_close = NULL; + + bt_device = _get_sco_connected_device(u->device_manager); + if (bt_device == NULL) { + pa_log_debug("No SCO connected bt device"); + return; + } + if (pa_tz_device_sco_get_status(bt_device, &sco_status) < 0) { + pa_log_error("get BT SCO status failed"); + return; + } + if (pa_tz_device_sco_close(bt_device) < 0) { + pa_log_error("BT SCO was opened, but failed to close SCO"); + return; + } + pa_log_info("BT SCO is now closed in timed callback"); +} + + +static int bt_sco_open(struct userdata *u) { + dm_device_bt_sco_status_t sco_status; + pa_tz_device *bt_device; + + pa_assert(u); + + if (u->time_event_bt_sco_close) { + u->core->mainloop->time_free(u->time_event_bt_sco_close); + u->time_event_bt_sco_close = NULL; + } + + bt_device = _get_sco_connected_device(u->device_manager); if (bt_device == NULL) { pa_log_debug("No SCO connected bt device"); return 0; } - pa_log_info("Got BT SCO connected device(%u), open param(%d)", bt_device->id, open); + pa_log_info("Got BT SCO connected device(%u)", bt_device->id); if (pa_tz_device_sco_get_status(bt_device, &sco_status) < 0) { pa_log_error("get BT SCO status failed"); return -1; } + if (sco_status == DM_DEVICE_BT_SCO_STATUS_OPENED) { + pa_log_warn("BT SCO is already opened for this BT device"); + return 0; + } + if (pa_tz_device_sco_open(bt_device) < 0) { + pa_log_error("failed to open BT SCO"); + return -1; + } + pa_log_debug("BT SCO is now opened"); - if (open) { - if (sco_status == DM_DEVICE_BT_SCO_STATUS_OPENED) { - pa_log_warn("BT SCO is already opened for this BT device"); - return 0; - } - if (pa_tz_device_sco_open(bt_device) < 0) { - pa_log_error("failed to open BT SCO"); - return -1; + return 0; +} + +static int bt_sco_close(struct userdata *u, bool delayed_close) { + dm_device_bt_sco_status_t sco_status; + pa_tz_device *bt_device; + + pa_assert(u); + + bt_device = _get_sco_connected_device(u->device_manager); + if (bt_device == NULL) { + pa_log_debug("No SCO connected bt device"); + return 0; + } + pa_log_info("Got BT SCO connected device(%u)", bt_device->id); + + if (pa_tz_device_sco_get_status(bt_device, &sco_status) < 0) { + pa_log_error("get BT SCO status failed"); + return -1; + } + if (sco_status == DM_DEVICE_BT_SCO_STATUS_CONNECTED) { + pa_log_warn("BT SCO is already closed for this BT device"); + return 0; + } + if (delayed_close) { + /* request to close SCO after 3 sec. */ + if (!u->time_event_bt_sco_close) { + u->time_event_bt_sco_close = pa_core_rttime_new(u->core, pa_rtclock_now() + TIMED_BT_SCO_CLOSE_USEC, timed_bt_sco_close_cb, u); + pa_log_debug("Append time event to close BT SCO"); } - pa_log_debug("BT SCO is now opened"); } else { - if (sco_status == DM_DEVICE_BT_SCO_STATUS_CONNECTED) { - pa_log_warn("BT SCO is already closed for this BT device"); - return 0; + if (u->time_event_bt_sco_close) { + u->core->mainloop->time_free(u->time_event_bt_sco_close); + u->time_event_bt_sco_close = NULL; } if (pa_tz_device_sco_close(bt_device) < 0) { pa_log_error("BT SCO was opened, but failed to close SCO"); @@ -272,6 +362,16 @@ static int update_bt_sco_state(pa_device_manager *dm, bool open) { return 0; } +/* Open/Close BT SCO if it is possible */ +static int update_bt_sco_state(struct userdata *u, bool open, bool delayed_close) { + pa_assert(u); + + if (open) + return bt_sco_open(u); + else + return bt_sco_close(u, delayed_close); +} + static int get_bt_property(pa_device_manager *dm, bool *is_wb, bool *is_nrec) { dm_device_bt_sco_status_t sco_status; pa_tz_device *bt_device; @@ -379,9 +479,6 @@ static bool skip_device(const char *stream_role, const char *device_type) if (pa_streq(device_type, DEVICE_TYPE_FORWARDING)) return true; - if (pa_streq(device_type, DEVICE_TYPE_BT_SCO)) - return true; - /* get sound profile */ if (vconf_get_bool(VCONFKEY_SETAPPL_SOUND_STATUS_BOOL, &sound_on) < 0) { pa_log_error("failed to get vconf - sound status"); @@ -396,6 +493,29 @@ static bool skip_device(const char *stream_role, const char *device_type) return false; } +static bool skip_bt_sco_device(struct userdata *u, const char *stream_role, const char *device_type) { + pa_assert(u); + pa_assert(stream_role); + pa_assert(device_type); + + if (pa_streq(stream_role, STREAM_ROLE_VOICE_INFORMATION) && pa_streq(device_type, DEVICE_TYPE_BT_SCO)) { + if (is_bt_sco_opened(u->device_manager)) { + /* remove the request of closing SCO */ + if (u->time_event_bt_sco_close) { + u->core->mainloop->time_free(u->time_event_bt_sco_close); + u->time_event_bt_sco_close = NULL; + } + pa_log_info("It is VOICE_INFORMATION, BT SCO is already opened, do not skip this device"); + return false; + } else { + pa_log_info("It is VOICE_INFORMATION, BT SCO is not opened, skip this device"); + return true; + } + } + + return false; +} + static inline bool is_cached_device_connected(const char* device_type, stream_type_t stream_type) { if (cached_connected_devices[convert_device_type_str(device_type)][CONVERT_TO_DEVICE_DIRECTION(stream_type)-1] > 0) return true; @@ -450,7 +570,7 @@ static pa_hook_result_t select_proper_sink_or_source_hook_cb(pa_core *c, pa_stre if (data->occupying_role) { if (IS_ROLE_COMMUNICATION(data->occupying_role)) { CONVERT_TO_DEVICE_ROLE(data->occupying_role, data->device_role); - pa_log_info("[SELECT] current occupying stream role is [%s], set deivce role to [%s]", data->occupying_role, data->device_role); + pa_log_info("[SELECT] current occupying stream role is [%s], set device role to [%s]", data->occupying_role, data->device_role); } } @@ -480,6 +600,9 @@ static pa_hook_result_t select_proper_sink_or_source_hook_cb(pa_core *c, pa_stre dm_device_type, dm_device_direction, dm_device_id); if (pa_streq(device_type, dm_device_type) && IS_AVAILABLE_DIRECTION(data->stream_type, dm_device_direction)) { pa_log_info(" ** found a matched device: type[%-16s], direction[0x%x]", dm_device_type, dm_device_direction); + if (skip_bt_sco_device(u, data->stream_role, dm_device_type)) + continue; + if (data->stream_type == STREAM_SINK_INPUT) { if (data->route_type == STREAM_ROUTE_TYPE_AUTO_ALL && u->module_combine_sink) { *(data->proper_sink) = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED, PA_NAMEREG_SINK); @@ -513,6 +636,9 @@ static pa_hook_result_t select_proper_sink_or_source_hook_cb(pa_core *c, pa_stre pa_log_debug(" -- type[%-16s], direction[0x%x], id[%u], creation_time[%llu]", dm_device_type, dm_device_direction, dm_device_id, creation_time); if (pa_streq(device_type, dm_device_type) && IS_AVAILABLE_DIRECTION(data->stream_type, dm_device_direction)) { + if (skip_bt_sco_device(u, data->stream_role, dm_device_type)) + continue; + if (!latest_device || (latest_creation_time <= creation_time)) { latest_device = device; latest_creation_time = creation_time; @@ -668,7 +794,7 @@ static void reset_route(struct userdata *u, stream_type_t stream_type) { } /* update BT SCO: close */ - update_bt_sco_state(u->device_manager, false); + update_bt_sco_state(u, false, true); /* unload combine sink */ if (stream_type == STREAM_SINK_INPUT) { @@ -888,8 +1014,6 @@ static pa_hook_result_t route_change_hook_cb(pa_core *c, pa_stream_manager_hook_ else if (data->stream_type == STREAM_SOURCE_OUTPUT && u->loopback_args.source->use_internal_codec) update_loopback_module(u, false); } - /* update BT SCO: close */ - update_bt_sco_state(u->device_manager, false); /* get current connected devices */ conn_devices = pa_device_manager_get_device_list(u->device_manager); @@ -911,6 +1035,8 @@ static pa_hook_result_t route_change_hook_cb(pa_core *c, pa_stream_manager_hook_ /* if it needs to skip it, keep going to next device for proper UCM setting */ if (skip_device(data->stream_role, dm_device_type)) continue; + if (skip_bt_sco_device(u, data->stream_role, dm_device_type)) + continue; fill_device_info(&route_info, dm_device_type, CONVERT_TO_HAL_DIRECTION(data->stream_type), dm_device_id); } else pa_log_debug(" -- it does not use internal audio codec, skip it"); @@ -933,12 +1059,21 @@ static pa_hook_result_t route_change_hook_cb(pa_core *c, pa_stream_manager_hook_ else pa_log_error("[ROUTE][AUTO] could not get sink"); } - break; + if (pa_streq(dm_device_type, DEVICE_TYPE_BT_SCO)) { + if (IS_ROLE_AVAILABLE_BT_SCO_OPEN(route_info.role)) + update_bt_sco_state(u, true, false); + } else { + update_bt_sco_state(u, false, false); + } + + break; } else if (data->route_type == STREAM_ROUTE_TYPE_AUTO_ALL) { uint32_t s_idx = 0; void *s = NULL; + update_bt_sco_state(u, false, false); + /* find the proper sink/source */ /* currently, we support two sinks for combining */ if (data->stream_type == STREAM_SINK_INPUT && u->module_combine_sink) { @@ -1018,6 +1153,14 @@ static pa_hook_result_t route_change_hook_cb(pa_core *c, pa_stream_manager_hook_ pa_log_debug(" -- type[%-16s], direction[0x%x], id[%u], creation_time[%llu]", dm_device_type, dm_device_direction, dm_device_id, creation_time); if (pa_streq(device_type, dm_device_type) && IS_AVAILABLE_DIRECTION(data->stream_type, dm_device_direction)) { + use_internal_codec = pa_tz_device_is_use_internal_codec(device); + if (use_internal_codec) { + /* if it needs to skip it, keep going to next device for proper UCM setting */ + if (skip_device(data->stream_role, dm_device_type)) + continue; + if (skip_bt_sco_device(u, data->stream_role, dm_device_type)) + continue; + } if (!latest_device || (latest_creation_time <= creation_time)) { latest_device = device; latest_creation_time = creation_time; @@ -1049,6 +1192,13 @@ static pa_hook_result_t route_change_hook_cb(pa_core *c, pa_stream_manager_hook_ pa_log_error("[ROUTE][AUTO_LAST_CONN] could not get sink"); } + if (pa_streq(dm_device_type, DEVICE_TYPE_BT_SCO)) { + if (IS_ROLE_AVAILABLE_BT_SCO_OPEN(route_info.role)) + update_bt_sco_state(u, true, false); + } else { + update_bt_sco_state(u, false, false); + } + /* Update device with latest_device to use be used later in this function */ device = latest_device; } @@ -1071,13 +1221,13 @@ static pa_hook_result_t route_change_hook_cb(pa_core *c, pa_stream_manager_hook_ dm_device_direction = pa_tz_device_get_direction(device); pa_log_debug(" ** found a matched device: type[%-16s], direction[0x%x]", dm_device_type, dm_device_direction); - /* Check for BT SCO in case of call routing */ - if (IS_ROLE_COMMUNICATION(route_info.role) && pa_streq(dm_device_type, DEVICE_TYPE_BT_SCO)) { + /* Check for availability for opening Bluetooth SCO */ + if (pa_streq(dm_device_type, DEVICE_TYPE_BT_SCO) && IS_ROLE_AVAILABLE_BT_SCO_OPEN(route_info.role)) { bool is_wb = false; bool is_nrec = false; /* update BT SCO: open */ - if (update_bt_sco_state(u->device_manager, true)) { + if (update_bt_sco_state(u, true, false)) { pa_log_error(" ** could not open BT SCO"); return PA_HOOK_CANCEL; } @@ -1090,7 +1240,7 @@ static pa_hook_result_t route_change_hook_cb(pa_core *c, pa_stream_manager_hook_ pa_log_warn("Failed to get property for wideband / nrec...."); } else { /* update BT SCO: close */ - update_bt_sco_state(u->device_manager, false); + update_bt_sco_state(u, false, false); } /* Check for in/out devices in case of loopback */ if (pa_streq(data->stream_role, STREAM_ROLE_LOOPBACK)) { @@ -1339,6 +1489,8 @@ void pa__done(pa_module *m) if (u->hal_interface) pa_hal_interface_unref(u->hal_interface); + bt_sco_close(u, false); + pa_xfree(u); pa_log_info("Tizen Audio Policy module is unloaded\n"); diff --git a/src/stream-manager.h b/src/stream-manager.h index 56ef0f8..c6def4e 100644 --- a/src/stream-manager.h +++ b/src/stream-manager.h @@ -47,6 +47,11 @@ #define IS_ROLE_RINGTONE(stream_role) \ (pa_streq(stream_role, STREAM_ROLE_RINGTONE_CALL) || pa_streq(stream_role, STREAM_ROLE_RINGTONE_VOIP)) +#define IS_ROLE_AVAILABLE_BT_SCO_OPEN(stream_role) \ + (stream_role && (pa_streq(stream_role, STREAM_ROLE_CALL_VOICE) || pa_streq(stream_role, STREAM_ROLE_CALL_VIDEO) || \ + pa_streq(stream_role, STREAM_ROLE_VOIP) || pa_streq(stream_role, STREAM_ROLE_RINGBACKTONE_CALL) || \ + pa_streq(stream_role, STREAM_ROLE_VOICE_RECOGNITION))) + #define CONVERT_TO_DEVICE_ROLE(x_stream_role, x_device_role) \ do { \ pa_assert(x_stream_role); \ @@ -77,6 +82,8 @@ do { \ #define STREAM_ROLE_LOOPBACK_MIRRORING "loopback-mirroring" #define STREAM_ROLE_RADIO "radio" #define STREAM_ROLE_SOLO "solo" +#define STREAM_ROLE_VOICE_RECOGNITION "voice-recognition" +#define STREAM_ROLE_VOICE_INFORMATION "voice-information" #define SINK_NAME_COMBINED "sink_combined" #define SINK_NAME_NULL "sink_null" diff --git a/src/tizen-device.c b/src/tizen-device.c index e9ff3b3..218cf91 100644 --- a/src/tizen-device.c +++ b/src/tizen-device.c @@ -824,6 +824,7 @@ int pa_tz_device_sco_open(pa_tz_device *device) { } device->sco_opened = true; + device->creation_time = pa_rtclock_now(); pa_tz_device_dump_info(device, PA_LOG_DEBUG); pa_log_info("BT SCO Open - SUCCESS");