#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)
pa_sink *sink;
pa_source *source;
} loopback_args;
+ pa_time_event *time_event_bt_sco_close;
};
static void __load_dump_config(struct userdata *u)
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;
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");
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;
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");
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;
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);
}
}
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);
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;
}
/* 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) {
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);
/* 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");
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) {
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;
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;
}
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;
}
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)) {
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");