Modify the initiation logic for GATT connection 47/261947/4
authorSeonah Moon <seonah1.moon@samsung.com>
Thu, 29 Jul 2021 09:49:48 +0000 (18:49 +0900)
committerseonah moon <seonah1.moon@samsung.com>
Mon, 9 Aug 2021 05:05:10 +0000 (05:05 +0000)
- Manage connection state callbacks globally
- Add the new characteristic used in initiation phase
- Do not call bt_gatt_disconnect()

Change-Id: Id5fbacd2f8e19ab46c901795e3c82d30bf597f39

plugins/ble-gatt/ble-gatt-plugin.cpp

index af7dbc55e8de385129975dbb51da914c6242bfaa..b3a0d5bee69fcdbd1de6c32f9dd55985515b2f9b 100755 (executable)
@@ -29,9 +29,15 @@ using namespace std;
 
 // TODO: change UUIDs
 #define VINE_GATT_SERVICE_UUID "000018f2-0000-1000-8000-00805f9b34fb"
-#define VINE_GATT_CHAR_UUID "00002af6-0000-1000-8000-00805f9b34fb"
+#define VINE_GATT_CONNECTION_CHAR_UUID "5fbfd598-ab0f-4c20-9966-60a11a41e974"
+#define VINE_GATT_READ_WRITE_CHAR_UUID "00002af6-0000-1000-8000-00805f9b34fb"
 #define VINE_GATT_DESC_UUID "fa87c0d0-afac-11de-8a39-0800200c9a66"
 
+// 2-way handshake for VINE GATT connection
+#define VINE_GATT_CONNECTION_SYN "VINE_GATT_CONNECTION_SYN"
+#define VINE_GATT_CONNECTION_ACK "VINE_GATT_CONNECTION_ACK"
+#define VINE_GATT_CONNECTION_FIN "VINE_GATT_CONNECTION_FIN"
+
 #define BT_ADDRESS_LEN 17
 #define BT_GATT_ATT_DATA_LEN_MAX 512
 
@@ -65,8 +71,10 @@ struct vine_gatt_s {
        int eventfd;
 
        bt_gatt_h service;
-       bt_gatt_h characteristic;
-       bt_gatt_h descriptor;
+       bt_gatt_h connection_char;
+       bt_gatt_h connection_desc;
+       bt_gatt_h read_write_char;
+       bt_gatt_h read_write_desc;
 
        vine_gatt_role_e type;
        union {
@@ -93,9 +101,25 @@ static vine_dp_plugin_callbacks g_callbacks = {
 
 static map<int, struct vine_gatt_s *> g_eventfds;
 
+// Wrapper for bt_gatt_connection_state_changed_cb()
+typedef void (*gatt_connection_state_cb)(vine_gatt_s *gatt,
+               bool connected, const char *remote_address, void *user_data);
+typedef struct {
+       vine_gatt_s *gatt;
+       gatt_connection_state_cb func;
+       void *user_data;
+} conn_state_cb_data;
+static multimap<string, conn_state_cb_data *> g_conn_state_cbs;
+
 static vine_gatt_s *_create_gatt(void);
 static int __write_to_client(vine_gatt_s *gatt);
 static int __write_to_server(vine_gatt_s *gatt);
+static int __update_gatt_service_info(const char *remote_address, vine_gatt_s *gatt);
+static void __handle_connected_client(vine_gatt_s *gatt, const char *address);
+static void __handle_disconnected_client(vine_gatt_s *gatt, const char *address);
+static void __send_conn_syn(vine_gatt_s *gatt, const char *remote_address);
+static void __send_conn_ack(vine_gatt_s *gatt, const char *remote_address);
+static void __send_conn_fin(vine_gatt_s *gatt, const char *remote_address);
 
 static vine_data_path_error __convert_bt_error_to_data_path_error(int error)
 {
@@ -218,14 +242,53 @@ static void __invoke_terminated_cb(void *user_data)
 }
 
 // Server Callbacks
+static void __gatt_server_read_connection_value_requested_cb(
+        const char *remote_address, int request_id,
+        bt_gatt_server_h server, bt_gatt_h gatt_handle,
+        int offset, void *user_data)
+{
+       // Do nothing.
+}
+
+static void __gatt_server_write_connection_value_requested_cb(const char *remote_address,
+        int request_id, bt_gatt_server_h server,
+        bt_gatt_h gatt_handle, bool response_needed, int offset,
+        const char *value, int len, void *user_data)
+{
+       RET_IF(user_data == NULL, "user_data is NULL");
+       VINE_LOGI("Got write init value[%s] request from %s.", value, remote_address);
+
+       vine_gatt_s *gatt = (vine_gatt_s *)user_data;
+
+       if (bt_gatt_server_send_response(request_id, BT_GATT_REQUEST_TYPE_WRITE,
+                               offset, BT_ERROR_NONE, "", 0) != BT_ERROR_NONE) {
+               VINE_LOGE("Failed to send initiation message");
+               return;
+       }
+
+       if (strncmp(value, VINE_GATT_CONNECTION_SYN, len) == 0) {
+               VINE_LOGI("Got SYN mesage from %s", remote_address);
+               __send_conn_ack(gatt, remote_address);
+               __handle_connected_client(gatt, remote_address);
+       } else if (strncmp(value, VINE_GATT_CONNECTION_FIN, strlen(VINE_GATT_CONNECTION_FIN) == 0)) {
+               VINE_LOGI("Got FIN message from %s", remote_address);
+               __handle_disconnected_client(gatt, remote_address);
+       }
+}
+
+void __gatt_server_noti_connection_state_changed_cb(bool notify, bt_gatt_server_h server,
+               bt_gatt_h gatt_handle, void *user_data)
+{
+       // Do nothing.
+       VINE_LOGI("+");
+}
+
 static void __gatt_server_read_value_requested_cb(
         const char *remote_address, int request_id,
         bt_gatt_server_h server, bt_gatt_h gatt_handle,
         int offset, void *user_data)
 {
-       // TODO: Send response.
-       // TODO: Save data
-       // Is this function needed for VINE?
+       // Do nothing.
 }
 
 static void __gatt_server_write_value_requested_cb(const char *remote_address,
@@ -318,6 +381,148 @@ void __gatt_server_noti_sent_cb(int result,
 }
 
 // Client Callbacks
+void __gatt_client_read_write_char_changed_cb(bt_gatt_h characteristic,
+               char *value, int len, void *user_data)
+{
+       VINE_LOGD("+");
+       RET_IF(user_data == NULL, "user_data is NULL");
+
+       vine_gatt_s *gatt = (vine_gatt_s *)user_data;
+       gatt_data_s *recv_data =__create_gatt_data(gatt, value, len, true);
+       RET_IF(recv_data == NULL, "recv_data is NULL");
+
+       gatt->recv_buffer->push(recv_data);
+
+       VINE_LOGD("recv_data[%p] is pushed to recv_buffer. length[%zd]", recv_data, len);
+       if (g_callbacks.received_cb)
+               g_callbacks.received_cb(len, gatt->user);
+}
+
+void __gatt_client_connection_char_changed_cb(bt_gatt_h characteristic,
+               char *value, int len, void *user_data)
+{
+       RET_IF(user_data == NULL, "user_data is NULL");
+
+       vine_gatt_s *gatt = (vine_gatt_s *)user_data;
+       if (strncmp(value, VINE_GATT_CONNECTION_ACK, strlen(VINE_GATT_CONNECTION_ACK)) == 0) {
+               VINE_LOGI("Got ACK message.");
+               if (bt_gatt_client_set_characteristic_value_changed_cb(gatt->read_write_char,
+                                       __gatt_client_read_write_char_changed_cb, gatt) != BT_ERROR_NONE) {
+                       VINE_LOGE("Failed to set characteristic_value_changed_cb");
+                       // TODO: disconnect.
+                       return;
+               }
+               gatt->ready_to_write = true;
+               __invoke_connected_cb(0, gatt->user);
+       } else if (strncmp(value, VINE_GATT_CONNECTION_FIN, strlen(VINE_GATT_CONNECTION_FIN) == 0)) {
+               VINE_LOGI("Got FIN message.");
+               __invoke_terminated_cb(gatt->user);
+       } else {
+               VINE_LOGI("Unknown message. [%s]", value);
+       }
+}
+
+void __send_conn_syn_completed_cb(int result, bt_gatt_h gatt_handle, void *user_data)
+{
+       // Do nothing.
+}
+
+static void __send_conn_syn(vine_gatt_s *gatt, const char *remote_address)
+{
+       // Only the client can send a SYN.
+       RET_IF(gatt->type != VINE_GATT_ROLE_CLIENT, "gatt isn't a client");
+       RET_IF(remote_address == NULL, "remote_address is NULL");
+
+       bt_gatt_set_value(gatt->connection_char,
+                       VINE_GATT_CONNECTION_SYN, strlen(VINE_GATT_CONNECTION_SYN));
+       bt_gatt_client_write_value(gatt->connection_char,
+                       __send_conn_syn_completed_cb, (void *)gatt);
+}
+
+static void __send_conn_ack_completed_cb(int result,
+               const char *remote_address, bt_gatt_server_h server, bt_gatt_h characteristic,
+               bool completed, void *user_data)
+{
+       VINE_LOGD("+");
+}
+
+static void __send_conn_ack(vine_gatt_s *gatt, const char *remote_address)
+{
+       VINE_LOGI("+");
+       // Only the server can send a ACK.
+       RET_IF(gatt->type != VINE_GATT_ROLE_SERVER, "gatt isn't a server");
+       RET_IF(remote_address == NULL, "remote_address is NULL");
+
+       bt_gatt_set_value(gatt->connection_char,
+                       VINE_GATT_CONNECTION_ACK, strlen(VINE_GATT_CONNECTION_ACK));
+       bt_gatt_server_notify_characteristic_changed_value(gatt->connection_char,
+                       __send_conn_ack_completed_cb, gatt->remote_address, (void *)gatt);
+}
+
+void __send_conn_fin_to_server_completed_cb(int result, bt_gatt_h gatt_handle, void *user_data)
+{
+       VINE_LOGD("+");
+}
+
+static void __send_conn_fin_to_client_completed_cb(int result,
+               const char *remote_address, bt_gatt_server_h server, bt_gatt_h characteristic,
+               bool completed, void *user_data)
+{
+       VINE_LOGD("+");
+}
+
+static void __send_conn_fin(vine_gatt_s *gatt, const char *remote_address)
+{
+       VINE_LOGI("+");
+       RET_IF(gatt->type == VINE_GATT_ROLE_UNKNOWN, "Unknown type");
+       RET_IF(remote_address == NULL, "remote_address is NULL");
+
+       bt_gatt_set_value(gatt->connection_char,
+                       VINE_GATT_CONNECTION_FIN, strlen(VINE_GATT_CONNECTION_FIN));
+
+       if (gatt->type == VINE_GATT_ROLE_SERVER) {
+               bt_gatt_server_notify_characteristic_changed_value(gatt->connection_char,
+                               __send_conn_fin_to_client_completed_cb, gatt->remote_address, (void *)gatt);
+       } else if (gatt->type = VINE_GATT_ROLE_CLIENT) {
+               bt_gatt_client_write_value(gatt->connection_char,
+                       __send_conn_fin_to_server_completed_cb, (void *)gatt);
+       }
+}
+
+static void __gatt_client_state_changed_cb(vine_gatt_s *gatt,
+               bool connected, const char *remote_address, void *user_data)
+{
+       RET_IF(gatt == NULL, "gatt is NULL");
+       VINE_LOGI("gatt[%p] connected[%d] remote address[%s]", gatt, connected, remote_address);
+
+       if (!connected) {
+               // TODO: unregister here?
+               __invoke_terminated_cb(gatt->user);
+               return;
+       }
+
+       if (__update_gatt_service_info(remote_address, gatt) != VINE_DATA_PATH_ERROR_NONE) {
+               __send_conn_fin(gatt, remote_address);
+               return;
+       }
+
+       __send_conn_syn(gatt, remote_address);
+}
+
+static void __gatt_accepted_client_state_changed_cb(vine_gatt_s *gatt,
+               bool connected, const char *remote_address, void *user_data)
+{
+       RET_IF(gatt == NULL, "gatt is NULL");
+       VINE_LOGI("gatt[%p] connected[%d] remote address[%s]", gatt, connected, remote_address);
+
+       if (connected) { // Do nothing.
+               VINE_LOGI("[%p] is already connected.", user_data);
+               return;
+       }
+
+       __handle_disconnected_client(gatt, remote_address);
+}
+
 static void __gatt_client_mtu_changed_cb(bt_gatt_client_h client,
        const bt_gatt_client_att_mtu_info_s *mtu_info, void *user_data)
 {
@@ -358,23 +563,6 @@ void __gatt_client_write_complete_cb(int result, bt_gatt_h gatt_handle, void *us
        __notify_write_event(gatt);
 }
 
-void __gatt_client_characteristic_value_changed_cb(bt_gatt_h characteristic,
-               char *value, int len, void *user_data)
-{
-       VINE_LOGD("+");
-       RET_IF(user_data == NULL, "user_data is NULL");
-
-       vine_gatt_s *gatt = (vine_gatt_s *)user_data;
-       gatt_data_s *recv_data =__create_gatt_data(gatt, value, len, true);
-       RET_IF(recv_data == NULL, "recv_data is NULL");
-
-       gatt->recv_buffer->push(recv_data);
-
-       VINE_LOGD("recv_data[%p] is pushed to recv_buffer. length[%zd]", recv_data, len);
-       if (g_callbacks.received_cb)
-               g_callbacks.received_cb(len, gatt->user);
-}
-
 static int __update_gatt_service_info(const char *remote_address, vine_gatt_s *gatt)
 {
        RET_VAL_IF(gatt == NULL, VINE_DATA_PATH_ERROR_INVALID_PARAMETER, "gatt is NULL");
@@ -383,6 +571,7 @@ static int __update_gatt_service_info(const char *remote_address, vine_gatt_s *g
 
        bt_gatt_client_h client = NULL;
        bt_gatt_h service = NULL;
+       bt_gatt_h connection_char = NULL;
        bt_gatt_h characteristic = NULL;
        int ret = BT_ERROR_OPERATION_FAILED;
 
@@ -406,12 +595,20 @@ static int __update_gatt_service_info(const char *remote_address, vine_gatt_s *g
        if (ret != BT_ERROR_NONE)
                goto ERR;
 
-    ret = bt_gatt_service_get_characteristic(service, VINE_GATT_CHAR_UUID, &characteristic);
+    ret = bt_gatt_service_get_characteristic(service,
+                       VINE_GATT_CONNECTION_CHAR_UUID, &connection_char);
        if (ret != BT_ERROR_NONE)
                goto ERR;
 
-       ret = bt_gatt_client_set_characteristic_value_changed_cb(characteristic,
-                       __gatt_client_characteristic_value_changed_cb, gatt);
+       ret = bt_gatt_client_set_characteristic_value_changed_cb(connection_char,
+                       __gatt_client_connection_char_changed_cb, gatt);
+       if (ret != BT_ERROR_NONE) {
+               VINE_LOGE("Failed to set characteristic_value_changed_cb");
+               goto ERR;
+       }
+
+    ret = bt_gatt_service_get_characteristic(service,
+                       VINE_GATT_READ_WRITE_CHAR_UUID, &characteristic);
        if (ret != BT_ERROR_NONE)
                goto ERR;
 
@@ -420,7 +617,8 @@ static int __update_gatt_service_info(const char *remote_address, vine_gatt_s *g
                goto ERR;
 
        gatt->service = service;
-       gatt->characteristic = characteristic;
+       gatt->connection_char = connection_char;
+       gatt->read_write_char = characteristic;
        gatt->remote_address = STRDUP(remote_address);
 
        VINE_LOGI("Succeeded to update GATT service info.");
@@ -448,13 +646,65 @@ static vine_gatt_s *__create_accepted_gatt(const char *remote_address, vine_gatt
        accepted_gatt->remote_address = STRDUP(remote_address);
        // refer to server's gatt handles.
        accepted_gatt->service = gatt->service;
-       accepted_gatt->characteristic = gatt->characteristic;
-       accepted_gatt->descriptor = gatt->descriptor;
+       accepted_gatt->connection_char = gatt->connection_char;
+       accepted_gatt->connection_desc = gatt->connection_desc;
+       accepted_gatt->read_write_char = gatt->read_write_char;
+       accepted_gatt->read_write_desc = gatt->read_write_desc;
 
        accepted_gatt->user = user;
        return accepted_gatt;
 }
 
+static void __gatt_connection_state_changed_cb(int result, bool connected,
+                                      const char *remote_address, void *user_data)
+{
+       VINE_LOGI("result[%d] connected[%d] remote address[%s]", result, connected, remote_address);
+
+       typedef multimap<string, conn_state_cb_data *>::iterator iterator;
+       pair<iterator, iterator> iter_pair = g_conn_state_cbs.equal_range(remote_address);
+
+       for (iterator it = iter_pair.first; it != iter_pair.second; ++it) {
+               conn_state_cb_data *cb_data = it->second;
+               if (cb_data && cb_data->func)
+                       cb_data->func(cb_data->gatt, connected, remote_address, cb_data->user_data);
+       }
+}
+
+static int _register_conn_state_changed_cb(vine_gatt_s *gatt,
+               const char *addr, gatt_connection_state_cb func, void *user_data)
+{
+       conn_state_cb_data *cb_data = (conn_state_cb_data *)calloc(1, sizeof(conn_state_cb_data));
+       RET_VAL_IF(cb_data == NULL, VINE_DATA_PATH_ERROR_OUT_OF_MEMORY, "cb_data is NULL");
+
+       cb_data->gatt = gatt;
+       cb_data->func = func;
+       cb_data->user_data = user_data;
+       g_conn_state_cbs.insert(pair<string, conn_state_cb_data *>(addr, cb_data));
+       VINE_LOGD("cb_data[%p] is registered.", cb_data);
+
+       return VINE_DATA_PATH_ERROR_NONE;
+}
+
+static int _unregister_conn_state_changed_cb(vine_gatt_s *gatt, const char *addr, void *user_data)
+{
+       typedef multimap<string, conn_state_cb_data *>::iterator iterator;
+       pair<iterator, iterator> iter_pair = g_conn_state_cbs.equal_range(addr);
+
+       for (iterator it = iter_pair.first; it != iter_pair.second; ++it) {
+               conn_state_cb_data *cb_data = it->second;
+               if (cb_data && cb_data->gatt == gatt && cb_data->user_data == user_data) {
+                       VINE_LOGD("cb_data[%p] is unregistered.", cb_data);
+                       g_conn_state_cbs.erase(it);
+                       cb_data->gatt = NULL;
+                       cb_data->func = NULL;
+                       cb_data->user_data = NULL;
+                       free(cb_data);
+                       break;
+               }
+       }
+       return VINE_DATA_PATH_ERROR_NONE;
+}
+
 static void __handle_connected_client(vine_gatt_s *gatt, const char *address)
 {
        RET_IF(gatt == NULL, "Invalid parameter.");
@@ -465,6 +715,9 @@ static void __handle_connected_client(vine_gatt_s *gatt, const char *address)
        RET_IF(accepted_gatt == NULL, "Failed to create accepted gatt.");
 
        gatt->role.server->client_list->insert(std::make_pair(string(address), accepted_gatt));
+       _register_conn_state_changed_cb(gatt, address,
+                       __gatt_accepted_client_state_changed_cb, (void *)accepted_gatt);
+
        VINE_LOGI("%s is connected.", address);
        __invoke_accepted_cb(accepted_gatt, accepted_gatt->user);
 }
@@ -476,44 +729,16 @@ static void __handle_disconnected_client(vine_gatt_s *gatt, const char *address)
        RET_IF(gatt->role.server == NULL
                        || gatt->role.server->client_list == NULL, "Invalid parameter.");
 
+       // TODO: Get client gatt from parameter.
        vine_gatt_s *client_gatt = __find_client_gatt(gatt, address);
        RET_IF(client_gatt == NULL, "Cannot find a client[%s].", address);
 
        gatt->role.server->client_list->erase(address);
+       _unregister_conn_state_changed_cb(gatt, address, (void *)client_gatt);
        __invoke_terminated_cb(client_gatt->user);
        VINE_LOGI("%s is disconnected.", address);
 }
 
-static void __gatt_connection_state_changed_cb(int result, bool connected,
-                                      const char *remote_address, void *user_data)
-{
-       RET_IF(user_data == NULL, "user_data is NULL");
-
-       vine_gatt_s *gatt = (vine_gatt_s *)user_data;
-
-       VINE_LOGI("gatt[%p] result[%d] connected[%d] remote address[%s] role[%d]",
-                       gatt, result, connected, remote_address, gatt->type);
-
-       if (!connected) {
-               if (gatt->type == VINE_GATT_ROLE_SERVER)
-                       __handle_disconnected_client(gatt, remote_address);
-               else
-                       __invoke_terminated_cb(gatt->user);
-               return;
-       }
-
-       if (gatt->type == VINE_GATT_ROLE_SERVER && gatt->role.server) {
-               __handle_connected_client(gatt, remote_address);
-       } else if (gatt->type == VINE_GATT_ROLE_CLIENT) {
-               if (__update_gatt_service_info(remote_address, gatt) != VINE_DATA_PATH_ERROR_NONE) {
-                       bt_gatt_disconnect(remote_address);
-                       result = -1;
-               }
-               gatt->ready_to_write = true;
-               __invoke_connected_cb(result, gatt->user);
-       }
-}
-
 static vine_gatt_s *_create_gatt(void)
 {
        vine_gatt_s *gatt = (vine_gatt_s *)calloc(1, sizeof(vine_gatt_s));
@@ -526,13 +751,6 @@ static vine_gatt_s *_create_gatt(void)
                return NULL;
        }
 
-       if (bt_gatt_set_connection_state_changed_cb(__gatt_connection_state_changed_cb,
-                               (void *)gatt) != BT_ERROR_NONE) {
-               VINE_LOGE("Failed to register GATT connection state changed callback");
-               free(gatt);
-               return NULL;
-       }
-
        gatt->eventfd = fd;
        g_eventfds[gatt->eventfd] = gatt;
        gatt->recv_buffer = new VineQueue<gatt_data_s *>;
@@ -540,30 +758,33 @@ static vine_gatt_s *_create_gatt(void)
        return gatt;
 }
 
-static bt_gatt_h _add_gatt_characteristic(bt_gatt_h service, vine_dp_plugin_h handle)
+static bt_gatt_h _add_gatt_characteristic(bt_gatt_h service,
+               const char *uuid,
+               bt_gatt_server_read_value_requested_cb read_requested_cb,
+               bt_gatt_server_write_value_requested_cb write_requested_cb,
+               bt_gatt_server_characteristic_notification_state_changed_cb noti_changed_cb,
+               vine_dp_plugin_h handle)
 {
        bt_gatt_h characteristic = NULL;
        char initial_value[1] = {'0'}; // TODO: will be changed.
        int permissions = BT_GATT_PERMISSION_READ | BT_GATT_PERMISSION_WRITE;
        int properties = BT_GATT_PROPERTY_BROADCAST | BT_GATT_PROPERTY_READ |
-               BT_GATT_PROPERTY_WRITE
-               | BT_GATT_PROPERTY_INDICATE;
+               BT_GATT_PROPERTY_WRITE | BT_GATT_PROPERTY_INDICATE;
 
-       if (bt_gatt_characteristic_create(VINE_GATT_CHAR_UUID,
-                               permissions, properties,
+       if (bt_gatt_characteristic_create(uuid, permissions, properties,
                                initial_value, sizeof(initial_value), &characteristic) != BT_ERROR_NONE)
                return NULL;
 
        if (bt_gatt_server_set_read_value_requested_cb(characteristic,
-                               __gatt_server_read_value_requested_cb, handle) != BT_ERROR_NONE)
+                               read_requested_cb, handle) != BT_ERROR_NONE)
                goto ERR;
 
        if (bt_gatt_server_set_write_value_requested_cb(characteristic,
-                               __gatt_server_write_value_requested_cb, handle) != BT_ERROR_NONE)
+                       write_requested_cb, handle) != BT_ERROR_NONE)
                goto ERR;
 
        if (bt_gatt_server_set_characteristic_notification_state_change_cb(characteristic,
-                               __gatt_server_noti_state_changed_cb, handle) != BT_ERROR_NONE)
+                               noti_changed_cb, handle) != BT_ERROR_NONE)
                goto ERR;
 
        if (bt_gatt_service_add_characteristic(service, characteristic) != BT_ERROR_NONE)
@@ -620,11 +841,11 @@ static int __write_to_server(vine_gatt_s *gatt)
        gatt_data_s *data = __get_pending_write_data(gatt);
        RET_VAL_IF(data == NULL, VINE_DATA_PATH_ERROR_NO_DATA, "There is no pending data.");
 
-       int ret = bt_gatt_set_value(gatt->characteristic, (const char *)data->buf, data->len);
+       int ret = bt_gatt_set_value(gatt->read_write_char, (const char *)data->buf, data->len);
        RET_VAL_IF(ret != BT_ERROR_NONE, __convert_bt_error_to_data_path_error(ret),
                        "bt_gatt_set_value() failed.");
 
-       ret = bt_gatt_client_write_value(gatt->characteristic,
+       ret = bt_gatt_client_write_value(gatt->read_write_char,
                        __gatt_client_write_complete_cb, (void *)data);
        RET_VAL_IF(ret != BT_ERROR_NONE, __convert_bt_error_to_data_path_error(ret),
                        "bt_gatt_client_write_value() failed.");
@@ -668,11 +889,11 @@ static int __write_to_client(vine_gatt_s *gatt)
        gatt_data_s *data = __get_pending_write_data(gatt);
        RET_VAL_IF(data == NULL, VINE_DATA_PATH_ERROR_NO_DATA, "There is no pending data.");
 
-       int ret = bt_gatt_set_value(gatt->characteristic, (const char *)data->buf, data->len);
+       int ret = bt_gatt_set_value(gatt->read_write_char, (const char *)data->buf, data->len);
        RET_VAL_IF(ret != BT_ERROR_NONE, __convert_bt_error_to_data_path_error(ret),
                        "bt_gatt_set_value() failed.");
 
-       ret = bt_gatt_server_notify_characteristic_changed_value(gatt->characteristic,
+       ret = bt_gatt_server_notify_characteristic_changed_value(gatt->read_write_char,
                        __gatt_server_noti_sent_cb, gatt->remote_address, (void *)data);
        RET_VAL_IF(ret != BT_ERROR_NONE, __convert_bt_error_to_data_path_error(ret),
                        "bt_gatt_server_notify_characteristic_changed_value() failed.");
@@ -721,7 +942,7 @@ void gatt_process_event(int fd, int events)
                return;
        }
 
-       // TODO: handle pending write events.
+       // Handle pending write events.
        vine_gatt_s *gatt = it->second;
        VINE_LOGI("found gatt[%p]", gatt);
 
@@ -738,6 +959,13 @@ int gatt_create(vine_dp_plugin_h *handle, void *plugin_data, void *user)
        RET_VAL_IF(handle == NULL, VINE_DATA_PATH_ERROR_INVALID_PARAMETER, "handle is NULL");
        RET_VAL_IF(user == NULL, VINE_DATA_PATH_ERROR_INVALID_PARAMETER, "user is NULL");
 
+       // TODO: need to find proper point to register callback.
+       int ret = bt_gatt_set_connection_state_changed_cb(__gatt_connection_state_changed_cb, NULL);
+       if (ret != BT_ERROR_NONE) {
+               VINE_LOGE("Failed to register GATT connection state changed callback");
+               return __convert_bt_error_to_data_path_error(ret);
+       }
+
        vine_gatt_s *gatt = plugin_data ? (vine_gatt_s *)plugin_data :_create_gatt();
        RET_VAL_IF(gatt == NULL, VINE_DATA_PATH_ERROR_OPERATION_FAILED, "Failed to create GATT handle");
 
@@ -757,8 +985,10 @@ int gatt_destroy(vine_dp_plugin_h handle)
                _destroy_gatt_server(gatt->role.server);
                gatt->role.server = NULL;
                bt_gatt_service_destroy(gatt->service);
-               bt_gatt_characteristic_destroy(gatt->characteristic);
-               bt_gatt_descriptor_destroy(gatt->descriptor);
+               bt_gatt_characteristic_destroy(gatt->connection_char);
+               bt_gatt_descriptor_destroy(gatt->connection_desc);
+               bt_gatt_characteristic_destroy(gatt->read_write_char);
+               bt_gatt_descriptor_destroy(gatt->read_write_desc);
        } else if (gatt->type == VINE_GATT_ROLE_CLIENT) {
                // bt_gatt_h handles will be freed during bt_gatt_client_destroy().
                _destroy_gatt_client(gatt->role.client);
@@ -767,8 +997,10 @@ int gatt_destroy(vine_dp_plugin_h handle)
 
        close(gatt->eventfd);
        gatt->service = NULL;
-       gatt->characteristic = NULL;
-       gatt->descriptor = NULL;
+       gatt->connection_char = NULL;
+       gatt->connection_desc = NULL;
+       gatt->read_write_char = NULL;
+       gatt->read_write_desc = NULL;
        free(gatt->remote_address);
        gatt->remote_address = NULL;
        free(gatt);
@@ -785,8 +1017,10 @@ int gatt_open(vine_dp_plugin_h handle, int addr_family,
        vine_gatt_s *gatt = (vine_gatt_s *)handle;
        bt_gatt_server_h server = NULL;
        bt_gatt_h service = NULL;
-       bt_gatt_h characteristic = NULL;
-       bt_gatt_h descriptor = NULL;
+       bt_gatt_h connection_char = NULL;
+       bt_gatt_h connection_desc = NULL;
+       bt_gatt_h read_write_char = NULL;
+       bt_gatt_h read_write_desc = NULL;
        int ret = BT_ERROR_OPERATION_FAILED;
 
        // This returns OPERATION_FAILED when already initialized. ignore it.
@@ -799,30 +1033,44 @@ int gatt_open(vine_dp_plugin_h handle, int addr_family,
        }
 
        ret = bt_gatt_service_create(VINE_GATT_SERVICE_UUID, BT_GATT_SERVICE_TYPE_PRIMARY, &service);
-        if (ret != BT_ERROR_NONE) {
+       if (ret != BT_ERROR_NONE) {
                VINE_LOGE("Failed to create service.");
                goto ERR;
        }
 
-       characteristic = _add_gatt_characteristic(service, handle);
-       if (!characteristic) {
+       connection_char = _add_gatt_characteristic(service, VINE_GATT_CONNECTION_CHAR_UUID,
+                       __gatt_server_read_connection_value_requested_cb,
+                       __gatt_server_write_connection_value_requested_cb,
+                       __gatt_server_noti_connection_state_changed_cb, handle);
+
+       connection_desc = _add_gatt_descriptor(connection_char);
+       if (!connection_desc) {
+               VINE_LOGE("Failed to add descriptor.");
+               goto ERR;
+       }
+
+       read_write_char = _add_gatt_characteristic(service, VINE_GATT_READ_WRITE_CHAR_UUID,
+                       __gatt_server_read_value_requested_cb,
+                       __gatt_server_write_value_requested_cb,
+                       __gatt_server_noti_state_changed_cb, handle);
+       if (!read_write_char) {
                VINE_LOGE("Failed to add characteristc.");
                goto ERR;
        }
 
-       descriptor = _add_gatt_descriptor(characteristic);
-       if (!descriptor) {
+       read_write_desc = _add_gatt_descriptor(read_write_char);
+       if (!read_write_desc) {
                VINE_LOGE("Failed to add descriptor.");
                goto ERR;
        }
 
-    ret = bt_gatt_server_register_service(server, service);
+       ret = bt_gatt_server_register_service(server, service);
        if (ret != BT_ERROR_NONE) {
                VINE_LOGE("Failed to register service.");
                goto ERR;
        }
 
-    ret = bt_gatt_server_start();
+       ret = bt_gatt_server_start();
        if (ret != BT_ERROR_NONE) {
                VINE_LOGE("Failed to start GATT server.");
                goto ERR;
@@ -834,8 +1082,10 @@ int gatt_open(vine_dp_plugin_h handle, int addr_family,
                goto ERR;
 
        gatt->service = service;
-       gatt->characteristic = characteristic;
-       gatt->descriptor = descriptor;
+       gatt->connection_char = connection_char;
+       gatt->connection_desc = connection_desc;
+       gatt->read_write_char = read_write_char;
+       gatt->read_write_desc = read_write_desc;
 
        VINE_LOGI("Succeeded to start GATT server.");
 
@@ -848,16 +1098,20 @@ ERR:
                bt_gatt_server_destroy(server);
        if (service)
                bt_gatt_service_destroy(service);
-       if (characteristic)
-               bt_gatt_characteristic_destroy(characteristic);
-       if (descriptor)
-               bt_gatt_descriptor_destroy(descriptor);
+       if (connection_char)
+               bt_gatt_characteristic_destroy(connection_char);
+       if (connection_desc)
+               bt_gatt_descriptor_destroy(connection_desc);
+       if (read_write_char)
+               bt_gatt_characteristic_destroy(read_write_char);
+       if (read_write_desc)
+               bt_gatt_descriptor_destroy(read_write_desc);
        return ret;
 }
 
 // All parameters except handle and addr are not used in BT GATT.
 int gatt_connect(vine_dp_plugin_h handle, int addr_family,
-               const char *addr, int gatt_port, const char *iface_name, vine_dp_ssl ssl)
+               const char *addr, int port, const char *iface_name, vine_dp_ssl ssl)
 {
        RET_VAL_IF(handle == NULL, VINE_DATA_PATH_ERROR_INVALID_PARAMETER, "handle is NULL");
        RET_VAL_IF(addr == NULL, VINE_DATA_PATH_ERROR_INVALID_PARAMETER, "addr is NULL");
@@ -865,11 +1119,13 @@ int gatt_connect(vine_dp_plugin_h handle, int addr_family,
        vine_gatt_s *gatt = (vine_gatt_s *)handle;
        int ret;
 
+       _register_conn_state_changed_cb(gatt, addr, __gatt_client_state_changed_cb, NULL);
        gatt->type = VINE_GATT_ROLE_CLIENT;
        ret = bt_gatt_connect(addr, false);
        if (ret != BT_ERROR_NONE) {
                VINE_LOGE("Failed to connect remote LE based service.");
                gatt->type = VINE_GATT_ROLE_UNKNOWN;
+               _unregister_conn_state_changed_cb(gatt, addr, NULL);
                return __convert_bt_error_to_data_path_error(ret);
        }
 
@@ -968,7 +1224,7 @@ int gatt_close(vine_dp_plugin_h handle)
                ret = bt_gatt_server_unregister_service(gatt->role.server->server, gatt->service);
        } else if (gatt->type == VINE_GATT_ROLE_CLIENT) {
                VINE_LOGI("Disconnect from %s", gatt->remote_address);
-               ret = bt_gatt_disconnect(gatt->remote_address);
+               // TODO: Send disconnect message.
        }
        return __convert_bt_error_to_data_path_error(ret);
 }