Keep client list in GATT server handle 53/261053/3
authorSeonah Moon <seonah1.moon@samsung.com>
Fri, 9 Jul 2021 06:10:24 +0000 (15:10 +0900)
committerSeonah Moon <seonah1.moon@samsung.com>
Mon, 12 Jul 2021 07:05:21 +0000 (16:05 +0900)
BT GATT server api doens't create a new handle each connection.
But DataPath is designed that new handle is created whenever connected with a peer.
In this patch, to compatibility, GATT server will manage a client list.

Change-Id: Ica3971031ae2e348ad1726057336de830b0ced0b

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

index e428706..45a1793 100755 (executable)
@@ -16,6 +16,7 @@
 */
 
 #include <bluetooth.h>
+#include <map>
 
 #include "vine-data-path-plugin.h"
 #include "vine-log.h"
@@ -37,6 +38,8 @@ typedef enum {
        VINE_GATT_ROLE_CLIENT,
 } vine_gatt_role_e;
 
+typedef struct vine_gatt_s vine_gatt_s;
+
 typedef struct {
        char *buf;
        size_t len;
@@ -45,22 +48,30 @@ typedef struct {
 } gatt_data_s;
 
 typedef struct {
+       bt_gatt_server_h server;
+       std::map<std::string, vine_gatt_s *> *client_list;
+} gatt_server_s;
+
+typedef struct {
+       bt_gatt_client_h client;
+       bool is_accepted;
+} gatt_client_s;
+
+struct vine_gatt_s {
        bt_gatt_h service;
        bt_gatt_h characteristic;
        bt_gatt_h descriptor;
 
        vine_gatt_role_e type;
        union {
-               bt_gatt_server_h server;
-               bt_gatt_client_h client;
+               gatt_server_s *server;
+               gatt_client_s *client;
        } role;
 
        VineQueue<gatt_data_s *> *recv_buffer;
-
-       char *remote_address; // this is used for VINE_GATT_ROLE_CLIENT only.
-       bool is_accepted;
+       char *remote_address;
        void *user; // vine_data_path_h
-} vine_gatt_s;
+};
 
 static vine_dp_plugin_callbacks g_callbacks = {
     .pollfd_cb = NULL,
@@ -117,26 +128,64 @@ static void __destroy_gatt_data(gatt_data_s *data)
        free(data);
 }
 
-static void __invoke_accepted_cb(const char *remote_address, vine_gatt_s *gatt, void *user_data)
+static gatt_server_s *_create_gatt_server(bt_gatt_server_h gatt_server)
 {
-       if (!g_callbacks.accepted_cb)
-               return;
+       RET_VAL_IF(gatt_server == NULL, NULL, "Invalid parameter.");
 
-       vine_gatt_s *accepted_gatt = _create_gatt();
-       RET_IF(accepted_gatt == NULL, "Failed to create accepted_gatt.");
+       gatt_server_s *server = (gatt_server_s *)calloc(1, sizeof(gatt_server_s));
+       RET_VAL_IF(server == NULL, NULL, "Out of memory.");
 
-       accepted_gatt->type = VINE_GATT_ROLE_CLIENT;
-       accepted_gatt->remote_address = STRDUP(remote_address);
+       server->server = gatt_server;
+       server->client_list = new std::map<std::string, vine_gatt_s *>;
+       return server;
+}
 
-       // refer to server's gatt handles.
-       accepted_gatt->service = gatt->service;
-       accepted_gatt->characteristic = gatt->characteristic;
-       accepted_gatt->descriptor = gatt->descriptor;
+static void _destroy_gatt_server(gatt_server_s *server)
+{
+       RET_IF(server == NULL, "Invalid parameter.");
+       bt_gatt_server_destroy(server->server);
+       server->server = NULL;
+       delete server->client_list;
+}
 
-       accepted_gatt->is_accepted = true;
-       accepted_gatt->user = user_data;
+static gatt_client_s *_create_gatt_client(bt_gatt_client_h gatt_client, bool is_accepted)
+{
+       gatt_client_s *client = (gatt_client_s *)calloc(1, sizeof(gatt_client_s));
+       RET_VAL_IF(client == NULL, NULL, "Out of memory.");
 
-       g_callbacks.accepted_cb(VINE_DP_NOT_IP, accepted_gatt->remote_address, 0, accepted_gatt, user_data);
+       client->client = gatt_client; // gatt_client can be NULL.
+       client->is_accepted = is_accepted;
+       return client;
+}
+
+static void _destroy_gatt_client(gatt_client_s *client)
+{
+       RET_IF(client == NULL, "Invalid parameter.");
+       bt_gatt_client_destroy(client->client);
+       client->client = NULL;
+}
+
+static vine_gatt_s *__find_client_gatt(vine_gatt_s *gatt, const char *address)
+{
+       RET_VAL_IF(gatt == NULL, NULL, "Invalid parameter.");
+       RET_VAL_IF(address == NULL, NULL, "Invalid parameter.");
+       RET_VAL_IF(gatt->role.server == NULL, NULL, "Invalid parameter.");
+       RET_VAL_IF(gatt->role.server->client_list == NULL, NULL, "Invalid parameter.");
+
+       std::map<std::string, vine_gatt_s *>::iterator it;
+
+       it = gatt->role.server->client_list->find(address);
+       if (it == gatt->role.server->client_list->end())
+               return NULL;
+
+       return (vine_gatt_s *)it->second;
+}
+
+static void __invoke_accepted_cb(vine_gatt_s *gatt, void *user_data)
+{
+       if (!g_callbacks.accepted_cb)
+               return;
+       g_callbacks.accepted_cb(VINE_DP_NOT_IP, gatt->remote_address, 0, gatt, user_data);
 }
 
 static void __invoke_connected_cb(int result, void *user_data)
@@ -174,12 +223,14 @@ static void __gatt_server_write_value_requested_cb(const char *remote_address,
        VINE_LOGI("Got write value requestment from %s.", remote_address);
 
        vine_gatt_s *gatt = (vine_gatt_s *)user_data;
+       vine_gatt_s *client_gatt = __find_client_gatt(gatt, remote_address);
+       RET_IF(client_gatt == NULL, "Cannot find a client[%s].", remote_address);
 
        // TODO: Need to handle splited data using offset.
        gatt_data_s *recv_data = __create_gatt_data(value, len, true);
        RET_IF(recv_data == NULL, "recv_data is NULL");
 
-       gatt->recv_buffer->push(recv_data);
+       client_gatt->recv_buffer->push(recv_data);
 
        VINE_LOGI("Write value requested. offset[%d] len[%d] reponse_needed[%d]",
                        offset, len, response_needed);
@@ -189,7 +240,7 @@ static void __gatt_server_write_value_requested_cb(const char *remote_address,
                VINE_LOGE("bt_gatt_server_send_response() is failed.");
 
        if (g_callbacks.received_cb)
-               g_callbacks.received_cb(len, gatt->user);
+               g_callbacks.received_cb(len, client_gatt->user);
 }
 
 void __gatt_server_noti_state_changed_cb(bool notify, bt_gatt_server_h server,
@@ -256,7 +307,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 characteristic = NULL;
-       int ret;
+       int ret = BT_ERROR_OPERATION_FAILED;
 
        ret = bt_gatt_client_create(remote_address, &client);
        if (ret != BT_ERROR_NONE || !client)
@@ -279,11 +330,15 @@ static int __update_gatt_service_info(const char *remote_address, vine_gatt_s *g
        if (ret != BT_ERROR_NONE)
                goto ERR;
 
-       gatt->role.client = client;
+       gatt->role.client = _create_gatt_client(client, false);
+       if (!gatt->role.client)
+               goto ERR;
+
        gatt->service = service;
        gatt->characteristic = characteristic;
+       gatt->remote_address = STRDUP(remote_address);
 
-       VINE_LOGE("Succeeded to update GATT service info.");
+       VINE_LOGI("Succeeded to update GATT service info.");
        return VINE_DATA_PATH_ERROR_NONE;
 
 ERR:
@@ -292,6 +347,57 @@ ERR:
        return __convert_bt_error_to_data_path_error(ret);
 }
 
+static vine_gatt_s *__create_accepted_gatt(const char *remote_address, vine_gatt_s *gatt, void *user)
+{
+       vine_gatt_s *accepted_gatt = _create_gatt();
+       RET_VAL_IF(accepted_gatt == NULL, NULL, "Failed to create accepted_gatt.");
+
+       accepted_gatt->type = VINE_GATT_ROLE_CLIENT;
+       accepted_gatt->role.client = _create_gatt_client(NULL, true);
+       if (!accepted_gatt->role.client) {
+               VINE_LOGE("Failed to create accepted_gatt.");
+               free(accepted_gatt);
+               return NULL;
+       }
+
+       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->user = user;
+       return accepted_gatt;
+}
+
+static void __handle_connected_client(vine_gatt_s *gatt, const char *address)
+{
+       RET_IF(gatt == NULL, "Invalid parameter.");
+       RET_IF(address == NULL, "Invalid parameter.");
+       RET_IF(gatt->role.server == NULL || gatt->role.server->client_list == NULL, "Invalid parameter.");
+
+       vine_gatt_s *accepted_gatt = __create_accepted_gatt(address, gatt, gatt->user);
+       RET_IF(accepted_gatt == NULL, "Failed to create accepted gatt.");
+
+       gatt->role.server->client_list->insert(std::make_pair(string(address), accepted_gatt));
+       __invoke_accepted_cb(accepted_gatt, accepted_gatt->user);
+       VINE_LOGI("%s is connected.", address);
+}
+
+static void __handle_disconnected_client(vine_gatt_s *gatt, const char *address)
+{
+       RET_IF(gatt == NULL, "Invalid parameter.");
+       RET_IF(address == NULL, "Invalid parameter.");
+       RET_IF(gatt->role.server == NULL || gatt->role.server->client_list, "Invalid 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);
+       __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)
 {
@@ -299,16 +405,19 @@ static void __gatt_connection_state_changed_cb(int result, bool connected,
 
        vine_gatt_s *gatt = (vine_gatt_s *)user_data;
 
-       VINE_LOGI("gatt[%p] result[%d] connected[%d] remote address[%s]",
-                       gatt, result, connected, remote_address);
+       VINE_LOGI("gatt[%p] result[%d] connected[%d] remote address[%s] role[%d]",
+                       gatt, result, connected, remote_address, gatt->type);
 
        if (!connected) {
-               __invoke_terminated_cb(gatt->user);
+               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) {
-               __invoke_accepted_cb(remote_address, gatt, gatt->user);
+       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);
@@ -460,14 +569,14 @@ int gatt_destroy(vine_dp_plugin_h handle)
        vine_gatt_s *gatt = (vine_gatt_s *)handle;
 
        if (gatt->type == VINE_GATT_ROLE_SERVER) {
-               bt_gatt_server_destroy(gatt->role.server);
+               _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);
        } else if (gatt->type == VINE_GATT_ROLE_CLIENT) {
                // bt_gatt_h handles will be freed during bt_gatt_client_destroy().
-               bt_gatt_client_destroy(gatt->role.client);
+               _destroy_gatt_client(gatt->role.client);
                gatt->role.client = NULL;
        }
 
@@ -534,7 +643,10 @@ int gatt_open(vine_dp_plugin_h handle, int addr_family,
        }
 
        gatt->type = VINE_GATT_ROLE_SERVER;
-       gatt->role.server = server;
+       gatt->role.server = _create_gatt_server(server);
+       if (gatt->role.server == NULL)
+               goto ERR;
+
        gatt->service = service;
        gatt->characteristic = characteristic;
        gatt->descriptor = descriptor;
@@ -566,19 +678,16 @@ int gatt_connect(vine_dp_plugin_h handle, int addr_family,
        RET_VAL_IF(addr == NULL, VINE_DATA_PATH_ERROR_INVALID_PARAMETER, "addr is NULL");
 
        vine_gatt_s *gatt = (vine_gatt_s *)handle;
-       bt_gatt_client_h client = NULL;
        int ret;
 
+       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;
                return __convert_bt_error_to_data_path_error(ret);
        }
 
-       gatt->type = VINE_GATT_ROLE_CLIENT;
-       gatt->role.client = client;
-       gatt->remote_address = STRDUP(addr);
-
        VINE_LOGI("Succeed to request to GATT connect.");
        return VINE_DATA_PATH_ERROR_NONE;
 }
@@ -645,7 +754,11 @@ int gatt_write(vine_dp_plugin_h handle, unsigned char *buf, size_t len)
                ret =_gatt_write_to_client(gatt, buf, len);
                break;
        case VINE_GATT_ROLE_CLIENT:
-               ret = gatt->is_accepted ? _gatt_write_to_client(gatt, buf, len)
+               if (!gatt->role.client) {
+                       ret = VINE_DATA_PATH_ERROR_OPERATION_FAILED;
+                       break;
+               }
+               ret = gatt->role.client->is_accepted ? _gatt_write_to_client(gatt, buf, len)
                        : _gatt_write_to_server(gatt, buf, len);
                break;
        case VINE_GATT_ROLE_UNKNOWN: