tizenaudio-policy: Support to open Bluetooth SCO link with voice-recognition role 90/139190/1
authorSangchul Lee <sc11.lee@samsung.com>
Tue, 18 Jul 2017 01:10:43 +0000 (10:10 +0900)
committerSangchul Lee <sc11.lee@samsung.com>
Tue, 18 Jul 2017 01:11:39 +0000 (10:11 +0900)
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 <sc11.lee@samsung.com>
packaging/pulseaudio-modules-tizen.spec
src/module-tizenaudio-policy.c
src/stream-manager.h
src/tizen-device.c

index 113b246..cb38eed 100644 (file)
@@ -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+
index c82fbc5..bf55947 100644 (file)
@@ -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,8 +231,7 @@ 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;
 
@@ -238,29 +240,117 @@ static int update_bt_sco_state(pa_device_manager *dm, bool open) {
     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");
index 56ef0f8..c6def4e 100644 (file)
 #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"
index e9ff3b3..218cf91 100644 (file)
@@ -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");