fhub: Apply lastest FHUB4.0 bluetooth-agent
[platform/core/connectivity/bluetooth-agent.git] / hf-agent / bluetooth-hf-agent.c
index 87d2611..edc623a 100644 (file)
 #include "bluetooth-hf-agent.h"
 #include "bluetooth-agent-profile.h"
 
+#include <audio_io.h>
+
+#define HFP_SCO_DATA_BUFFER_SIZE       48
+#define HFP_FHUB_SAMPLE_RATE           8000
+
 #define BT_AGENT_SYSPOPUP_MAX_ATTEMPT 3
 #define CALL_ALIAS_APP_ID "org.tizen.call-ui"
 
@@ -79,6 +84,7 @@ int clcc_async_retry_count = 0;
 #define CLCC_RETRY_TIMER 100   /* 100 msec */
 
 #define HFP_HF_UUID "0000111e-0000-1000-8000-00805f9b34fb"
+#define PBAP_PCE_UUID "0000112e-0000-1000-8000-00805f9b34fb"
 #define DEFAULT_ADAPTER_OBJECT_PATH "/org/bluez/hci0"
 
 /*Below Inrospection data is exposed to bluez from agent*/
@@ -149,6 +155,10 @@ static const gchar hf_agent_introspection_xml[] =
 "               <method name='IsInbandRingtoneSupported'>"
 "                      <arg type='b' name='status' direction='out'/>"
 "               </method>"
+"               <method name='AudioMuteOn'>"
+"               </method>"
+"               <method name='AudioMuteOff'>"
+"               </method>"
 " </interface>"
 "</node>";
 
@@ -702,6 +712,14 @@ static void __hf_agent_method(GDBusConnection *connection,
                                "Supported" : "NotSupported");
                g_dbus_method_invocation_return_value(context,
                                g_variant_new("(b)", is_supported));
+       } else if (g_strcmp0(method_name, "AudioMuteOn") == 0) {
+               DBG("Going to MUTE audio");
+               bt_hf_info.is_audio_input_mute = TRUE;
+               g_dbus_method_invocation_return_value(context, NULL);
+       } else if (g_strcmp0(method_name, "AudioMuteOff") == 0) {
+               DBG("Going to UNMUTE audio");
+               bt_hf_info.is_audio_input_mute = FALSE;
+               g_dbus_method_invocation_return_value(context, NULL);
        }
 
        INFO("-");
@@ -1194,18 +1212,24 @@ static void __bt_hf_agent_handle_ind_change(bt_hf_agent_info_t *bt_hf_info,
                                        "CallStarted", NULL);
                        bt_hf_info->is_dialing = FALSE;
                        bt_hf_info->call_active = TRUE;
+                       if (TIZEN_MODEL_NAME_FHUB)
+                               bt_hf_info->call_state = BT_HF_DA_CALL_STAT_CALL_STARTED;
                } else if (bt_hf_info->call_active) {
                        __bt_hf_agent_emit_signal(conn,
                                        BT_HF_AGENT_OBJECT_PATH,
                                        BT_HF_SERVICE_INTERFACE,
                                        "CallEnded", NULL);
                        bt_hf_info->call_active = FALSE;
+                       if (TIZEN_MODEL_NAME_FHUB)
+                               bt_hf_info->call_state = BT_HF_DA_CALL_STAT_CALL_ENDED;
 
                        if (bt_hf_info->ciev_call_setup_status == 0) {
                                __bt_hf_agent_emit_signal(gdbus_conn,
                                                BT_HF_AGENT_OBJECT_PATH,
                                                BT_HF_SERVICE_INTERFACE,
                                                "CallIdle", NULL);
+                               if (TIZEN_MODEL_NAME_FHUB)
+                                       bt_hf_info->call_state = BT_HF_DA_CALL_STAT_CALL_IDLE;
                        }
                }
 
@@ -1218,6 +1242,8 @@ static void __bt_hf_agent_handle_ind_change(bt_hf_agent_info_t *bt_hf_info,
                                        BT_HF_SERVICE_INTERFACE,
                                        "CallTerminated",
                                        NULL);
+                       if (TIZEN_MODEL_NAME_FHUB)
+                               bt_hf_info->call_state = BT_HF_DA_CALL_STAT_CALL_TERMINATED;
                } else if (!bt_hf_info->is_dialing && value > 0) {
                        bt_hf_info->is_dialing = TRUE;
                }
@@ -1227,6 +1253,8 @@ static void __bt_hf_agent_handle_ind_change(bt_hf_agent_info_t *bt_hf_info,
                                        BT_HF_AGENT_OBJECT_PATH,
                                        BT_HF_SERVICE_INTERFACE,
                                        "CallSetupIncoming", NULL);
+                       if (TIZEN_MODEL_NAME_FHUB)
+                               bt_hf_info->call_state = BT_HF_DA_CALL_STAT_CALL_SETUP_INCOMING;
                        if (__bt_hf_agent_launch_call_app()  == FALSE)
                                DBG("call app launching failed");
                } else if (bt_hf_info->ciev_call_setup_status == 2) {
@@ -1234,17 +1262,23 @@ static void __bt_hf_agent_handle_ind_change(bt_hf_agent_info_t *bt_hf_info,
                                        BT_HF_AGENT_OBJECT_PATH,
                                        BT_HF_SERVICE_INTERFACE,
                                        "CallSetupDialing", NULL);
+                       if (TIZEN_MODEL_NAME_FHUB)
+                               bt_hf_info->call_state = BT_HF_DA_CALL_STAT_CALL_SETUP_DIALING;
                } else if (bt_hf_info->ciev_call_setup_status == 3) {
                        __bt_hf_agent_emit_signal(gdbus_conn,
                                        BT_HF_AGENT_OBJECT_PATH,
                                        BT_HF_SERVICE_INTERFACE,
                                        "CallSetupAlerting", NULL);
+                       if (TIZEN_MODEL_NAME_FHUB)
+                               bt_hf_info->call_state = BT_HF_DA_CALL_STAT_CALL_SETUP_ALERTING;
                } else  if (bt_hf_info->ciev_call_status == 0 &&
                                                bt_hf_info->ciev_call_setup_status == 0) {
                        __bt_hf_agent_emit_signal(gdbus_conn,
                                        BT_HF_AGENT_OBJECT_PATH,
                                        BT_HF_SERVICE_INTERFACE,
                                                "CallIdle", NULL);
+                       if (TIZEN_MODEL_NAME_FHUB)
+                               bt_hf_info->call_state = BT_HF_DA_CALL_STAT_CALL_IDLE;
                }
        } else if (!strcmp(name, "\"callheld\"")) {
                if (value == 0) { /* No calls held*/
@@ -2515,6 +2549,196 @@ static guint __bt_hf_get_hold_mpty_features(gchar *features)
        return result;
 }
 
+/* for FHUB's HFP HF role */
+#include <sound_manager.h>
+sound_stream_info_h g_stream_info_write_h = NULL;
+
+static void __release_audio_in(bt_hf_agent_info_t *bt_hf_info)
+{
+       INFO("+");
+
+       if (bt_hf_info->audio_input != NULL) {
+               audio_in_unprepare((audio_in_h)bt_hf_info->audio_input);
+               audio_in_destroy((audio_in_h)bt_hf_info->audio_input);
+               bt_hf_info->audio_input = NULL;
+       }
+
+       INFO("-");
+}
+
+static void __release_audio_out(bt_hf_agent_info_t *bt_hf_info)
+{
+       INFO("+");
+
+       if (bt_hf_info->audio_output != NULL) {
+               audio_out_drain((audio_out_h)bt_hf_info->audio_output);
+               audio_out_unprepare((audio_out_h)bt_hf_info->audio_output);
+               audio_out_destroy((audio_out_h)bt_hf_info->audio_output);
+               bt_hf_info->audio_output = NULL;
+       }
+
+       INFO("-");
+}
+
+static int __initialize_audio_in(bt_hf_agent_info_t *bt_hf_info)
+{
+       int ret = 0;
+
+       INFO("+");
+       if (bt_hf_info->audio_input != NULL) {
+               __release_audio_in(bt_hf_info);
+       }
+
+       //init capturing
+       ret = audio_in_create(HFP_FHUB_SAMPLE_RATE, AUDIO_CHANNEL_MONO,
+                                       AUDIO_SAMPLE_TYPE_S16_LE, (audio_in_h *)&bt_hf_info->audio_input);
+       if (ret == AUDIO_IO_ERROR_NONE) {
+               if (!(audio_in_prepare((audio_in_h)bt_hf_info->audio_input))) {
+                       DBG("voice capturing is ready!\n");
+                       bt_hf_info->is_audio_input_mute = FALSE;
+               } else {
+                       ERR("audio_in_prepare failed, err(0x%x)\n", ret);
+                       audio_in_destroy((audio_in_h)bt_hf_info->audio_input);
+                       ret = -18;
+               }
+       } else {
+               ERR("audio_in_create failed.\n");
+               ret = -17;
+       }
+
+       INFO("-");
+       return ret;
+}
+
+static int __initialize_audio_out(bt_hf_agent_info_t *bt_hf_info)
+{
+       int ret = 0;
+       INFO("+");
+
+       if (bt_hf_info->audio_output != NULL) {
+               __release_audio_out(bt_hf_info);
+       }
+
+       //init playback
+       ret = audio_out_create_new(HFP_FHUB_SAMPLE_RATE, AUDIO_CHANNEL_MONO,
+                                       AUDIO_SAMPLE_TYPE_S16_LE, (audio_out_h *)&bt_hf_info->audio_output);
+       if (ret == AUDIO_IO_ERROR_NONE) {
+               INFO("after audio_out_create!!!");
+
+               /* set sound stream info */
+               if (g_stream_info_write_h) {
+                       if (!(ret = audio_out_set_sound_stream_info(bt_hf_info->audio_output, g_stream_info_write_h))) {
+                               INFO("after audio_out_set_sound_stream_info");
+                       } else {
+                               ERR("fail to audio_out_set_sound_stream_info(), ret(0x%x)\n", ret);
+                       }
+               } else {
+                       ERR("no stream info!!!!");
+               }
+
+               /* prepare */
+               if (!(ret = audio_out_prepare((audio_out_h)bt_hf_info->audio_output))) {
+                       DBG("voice playback is ready!\n");
+               } else {
+                       ERR("audio_out_prepare failed, err(0x%x)\n", ret);
+                       audio_out_destroy((audio_out_h)bt_hf_info->audio_output);
+                       bt_hf_info->audio_output = NULL;
+               }
+       } else {
+               ERR("audio_out_create failed. \n");
+       }
+
+       INFO("-");
+       return ret;
+}
+
+static gboolean __bt_hf_agent_sco_event_cb(GIOChannel *chan, GIOCondition cond, gpointer user_data)
+{
+       bt_hf_agent_info_t *bt_hf_info = user_data;
+       GDBusConnection *conn;
+       int sco_skt = g_io_channel_unix_get_fd(chan);
+
+       if (cond & G_IO_NVAL)
+               return FALSE;
+
+       if (cond & (G_IO_HUP | G_IO_ERR)) {
+               g_io_channel_shutdown(chan, TRUE, NULL);
+               close(bt_hf_info->cli_sco_fd);
+               bt_hf_info->cli_sco_fd = -1;
+               g_io_channel_unref(chan);
+               DBG("Emit AudioDisconnected Signal");
+
+               sco_audio_connected = BT_HF_AUDIO_DISCONNECTED;
+
+               bt_hf_info->call_state = BT_HF_DA_CALL_STAT_CALL_IDLE;
+               bt_hf_info->is_first_audio_out = TRUE;
+
+               __release_audio_out(bt_hf_info);
+               __release_audio_in(bt_hf_info);
+
+               conn = __bt_hf_get_gdbus_connection();
+               if (!conn) {
+                       ERR("Unable to get connection");
+                       return FALSE;
+               }
+               __bt_hf_agent_emit_signal(conn,
+                               BT_HF_AGENT_OBJECT_PATH,
+                               BT_HF_SERVICE_INTERFACE,
+                               "AudioDisconnected", NULL);
+
+               return FALSE;
+       }
+
+       if (cond & G_IO_IN) {
+               gchar buf_rx[HFP_SCO_DATA_BUFFER_SIZE] = { 0, };
+               int read;
+
+               read = recv(sco_skt, buf_rx, HFP_SCO_DATA_BUFFER_SIZE, MSG_DONTWAIT);
+               if (read > 0) {
+                       if (bt_hf_info->call_state == BT_HF_DA_CALL_STAT_CALL_SETUP_DIALING ||
+                               bt_hf_info->call_state == BT_HF_DA_CALL_STAT_CALL_SETUP_ALERTING ||
+                               bt_hf_info->call_active) {
+
+                               if (bt_hf_info->is_first_audio_out) {
+                                       __initialize_audio_out(bt_hf_info);
+                                       bt_hf_info->is_first_audio_out = FALSE;
+                               }
+                               if (bt_hf_info->audio_output) {
+                                       audio_out_write(bt_hf_info->audio_output, buf_rx, read);
+                               } else {
+                                       ERR("can't write audio");
+                               }
+                       }
+               }
+       }
+       if (cond & G_IO_OUT) {
+               gchar buf_tx[HFP_SCO_DATA_BUFFER_SIZE] = { 0, };
+               int size;
+
+               if (bt_hf_info->is_first_audio_in) {
+                       __initialize_audio_in(bt_hf_info);
+                       bt_hf_info->is_first_audio_in = FALSE;
+                       return  TRUE;
+               }
+
+               size = audio_in_read(bt_hf_info->audio_input, (void *)buf_tx, HFP_SCO_DATA_BUFFER_SIZE);
+               if (size > 0) {
+                       if (bt_hf_info->call_active) {
+
+                               if (bt_hf_info->is_audio_input_mute) {
+                                       memset(buf_tx, 0x0, size);
+                               }
+
+                               if (send(sco_skt, buf_tx, size, MSG_DONTWAIT) < 0) {
+                                       ERR("Write failed, error = %d[%s]", errno, strerror(errno));
+                               }
+                       }
+               }
+       }
+       return TRUE;
+}
+
+
 static gboolean __bt_hf_agent_sco_disconnect_cb(GIOChannel *chan, GIOCondition cond, gpointer user_data)
 {
        bt_hf_agent_info_t *bt_hf_info = user_data;
@@ -2581,8 +2805,17 @@ static gboolean __bt_hf_agent_sco_accept_cb(GIOChannel *chan, GIOCondition cond,
        g_io_channel_set_flags(sco_io, G_IO_FLAG_NONBLOCK, NULL);
        g_io_channel_set_buffered(sco_io, FALSE);
 
-       g_io_add_watch(sco_io, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
-                                       __bt_hf_agent_sco_disconnect_cb, bt_hf_info);
+       if (TIZEN_MODEL_NAME_FHUB) {
+               bt_hf_info->call_state = BT_HF_DA_CALL_STAT_CALL_IDLE;
+               bt_hf_info->is_first_audio_out = TRUE;
+               bt_hf_info->is_first_audio_in = TRUE;
+               //__initialize_audio_in(bt_hf_info);
+
+               g_io_add_watch(sco_io, G_IO_IN | G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                                               __bt_hf_agent_sco_event_cb, bt_hf_info);
+       } else
+               g_io_add_watch(sco_io, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                                               __bt_hf_agent_sco_disconnect_cb, bt_hf_info);
 
        if (bt_hf_info->state != BT_HF_STATE_CONNECTED)
                ERR("HFP is not yet connected");
@@ -2789,14 +3022,22 @@ static gboolean __bt_establish_service_level_conn(bt_hf_agent_info_t *bt_hf_info
        gchar cmd_buf[BT_HF_CMD_BUF_SIZE] = {0};
        gboolean ret;
        char *buf_ptr;
-       guint feature = BT_HF_FEATURE_EC_ANDOR_NR |
-                       BT_HF_FEATURE_CALL_WAITING_AND_3WAY |
-                       BT_HF_FEATURE_CLI_PRESENTATION |
-                       BT_HF_FEATURE_REMOTE_VOLUME_CONTROL |
-                       BT_HF_FEATURE_ENHANCED_CALL_STATUS;
-
-       if (TIZEN_PROFILE_WEARABLE)
-               feature = feature | BT_HF_FEATURE_CODEC_NEGOTIATION;
+       guint feature;
+
+       if (TIZEN_MODEL_NAME_FHUB)
+               feature = BT_HF_FEATURE_CLI_PRESENTATION |
+                               BT_HF_FEATURE_REMOTE_VOLUME_CONTROL |
+                               BT_HF_FEATURE_ENHANCED_CALL_STATUS;
+       else {
+               feature = BT_HF_FEATURE_EC_ANDOR_NR |
+                               BT_HF_FEATURE_CALL_WAITING_AND_3WAY |
+                               BT_HF_FEATURE_CLI_PRESENTATION |
+                               BT_HF_FEATURE_REMOTE_VOLUME_CONTROL |
+                               BT_HF_FEATURE_ENHANCED_CALL_STATUS;
+
+               if (TIZEN_PROFILE_WEARABLE)
+                       feature = feature | BT_HF_FEATURE_CODEC_NEGOTIATION;
+       }
 
        snprintf(cmd_buf, sizeof(cmd_buf), BT_HF_FEATURES, feature);
        ret = __bt_hf_send_and_read(bt_hf_info, cmd_buf, buf,
@@ -3205,11 +3446,18 @@ static int __bt_hf_register_profile(const char *uuid, uint16_t version,
        g_variant_builder_add(builder, "{sv}",
                        "features", g_variant_new("q", features));
 
-       ret = g_dbus_proxy_call_sync(proxy, "RegisterProfile",
-                               g_variant_new("(osa{sv})", path,
-                                       HFP_HF_UUID, builder),
-                               G_DBUS_CALL_FLAGS_NONE, -1,
-                               NULL, &error);
+       if (TIZEN_MODEL_NAME_FHUB)
+               ret = g_dbus_proxy_call_sync(proxy, "RegisterProfile",
+                                       g_variant_new("(osa{sv})", object,
+                                               uuid, builder),
+                                       G_DBUS_CALL_FLAGS_NONE, -1,
+                                       NULL, &error);
+       else
+               ret = g_dbus_proxy_call_sync(proxy, "RegisterProfile",
+                                       g_variant_new("(osa{sv})", path,
+                                               HFP_HF_UUID, builder),
+                                       G_DBUS_CALL_FLAGS_NONE, -1,
+                                       NULL, &error);
 
        g_variant_builder_unref(builder);
 
@@ -3246,7 +3494,15 @@ static void __bt_hf_agent_register(void)
        ret = __bt_hf_register_profile(HFP_HF_UUID, version, name, path,
                                                                features);
        if (ret)
-               ERR("Error in register");
+               ERR("Error in hf register");
+
+       if (TIZEN_MODEL_NAME_FHUB) {
+               ret = __bt_hf_register_profile(PBAP_PCE_UUID, 0x0101,
+                                       "Phone Book Access Client",
+                                       BT_PBAP_CLIENT_OBJECT_PATH, 0);
+               if (ret)
+                       ERR("Error in pce register");
+       }
 
        g_free(path);
        g_free(name);
@@ -3777,15 +4033,23 @@ static int bt_hf_agent_send_at_cmd(GDBusMethodInvocation *context, char *atcmd)
 
 static uint32_t __bt_hf_agent_get_hf_features(void)
 {
-       uint32_t hf_features = BT_HF_FEATURE_EC_ANDOR_NR |
-                       BT_HF_FEATURE_CALL_WAITING_AND_3WAY |
-                       BT_HF_FEATURE_CLI_PRESENTATION |
-                       BT_HF_FEATURE_REMOTE_VOLUME_CONTROL |
-                       BT_HF_FEATURE_ENHANCED_CALL_STATUS |
-                       BT_HF_FEATURE_CODEC_NEGOTIATION;
+       uint32_t hf_features;
 
-       if (TIZEN_PROFILE_WEARABLE)
-               hf_features = hf_features | BT_HF_FEATURE_CODEC_NEGOTIATION;
+       if (TIZEN_MODEL_NAME_FHUB)
+               hf_features = BT_HF_FEATURE_CLI_PRESENTATION |
+                               BT_HF_FEATURE_REMOTE_VOLUME_CONTROL |
+                               BT_HF_FEATURE_ENHANCED_CALL_STATUS;
+       else {
+               hf_features = BT_HF_FEATURE_EC_ANDOR_NR |
+                               BT_HF_FEATURE_CALL_WAITING_AND_3WAY |
+                               BT_HF_FEATURE_CLI_PRESENTATION |
+                               BT_HF_FEATURE_REMOTE_VOLUME_CONTROL |
+                               BT_HF_FEATURE_ENHANCED_CALL_STATUS |
+                               BT_HF_FEATURE_CODEC_NEGOTIATION;
+
+               if (TIZEN_PROFILE_WEARABLE)
+                       hf_features = hf_features | BT_HF_FEATURE_CODEC_NEGOTIATION;
+       }
 
        hf_ver = HFP_VERSION_1_6;
 
@@ -3806,6 +4070,9 @@ int main(void)
        hf_features = __bt_hf_agent_get_hf_features();
        bt_hf_info.feature = (uint16_t) hf_features & 0x3F;
 
+       bt_hf_info.audio_input = NULL;
+       bt_hf_info.audio_output = NULL;
+
        memset(&sa, 0, sizeof(sa));
        sa.sa_flags = SA_NOCLDSTOP;
        sa.sa_handler = __bt_hf_agent_sigterm_handler;