From 1eaf70e7d4b8b76385f42156a83bdd03af5525e4 Mon Sep 17 00:00:00 2001 From: Amit Purwar Date: Thu, 17 May 2018 19:43:41 +0530 Subject: [PATCH] gatt client adaptation feature changes on HAL Change-Id: Idef21c381035f119072575ff28ebd4b07c37c780 Signed-off-by: Amit Purwar --- bt-oal/bluez_hal/inc/bt-hal-msg.h | 71 + bt-oal/bluez_hal/src/bt-hal-dbus-common-utils.c | 36 + bt-oal/bluez_hal/src/bt-hal-dbus-common-utils.h | 4 + bt-oal/bluez_hal/src/bt-hal-event-receiver.c | 2 + bt-oal/bluez_hal/src/bt-hal-gatt-client.c | 2526 ++++++++++++++++++++++- bt-oal/bluez_hal/src/bt-hal-gatt-client.h | 2 + bt-oal/bluez_hal/src/bt-hal-gatt-server.c | 12 +- bt-oal/bluez_hal/src/bt-hal-gatt.c | 228 ++ 8 files changed, 2798 insertions(+), 83 deletions(-) diff --git a/bt-oal/bluez_hal/inc/bt-hal-msg.h b/bt-oal/bluez_hal/inc/bt-hal-msg.h index 5cc1b73..abebee2 100644 --- a/bt-oal/bluez_hal/inc/bt-hal-msg.h +++ b/bt-oal/bluez_hal/inc/bt-hal-msg.h @@ -637,4 +637,75 @@ struct hal_ev_raw_rssi_recieved { int32_t link_type; int32_t rssi; } __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_CONNECTED 0xBD +#define HAL_EV_GATT_CLIENT_DISCONNECTED 0xBE +struct hal_ev_gatt_client_connected { + int32_t conn_id; + int32_t status; + int32_t client_if; + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_SEARCH_RESULT 0xBF +struct hal_ev_gatt_client_search_result { + int32_t conn_id; + int32_t inst_id; + uint8_t uuid[16]; + int32_t is_primary; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_SEARCH_COMPLETE 0xC0 +struct hal_ev_gatt_client_search_complete { + int32_t conn_id; + int32_t status; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_CHARAC_SEARCH_RESULT 0xC1 +struct hal_ev_gatt_client_char_search_result { + int32_t conn_id; + int32_t status; + int32_t is_primary; + int32_t inst_id; + uint8_t svc_uuid[16]; + uint8_t char_uuid[16]; + int32_t char_prop; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_DESC_SEARCH_RESULT 0XC2 +struct hal_ev_gatt_client_desc_search_result { + int32_t conn_id; + int32_t status; + int32_t is_primary; + int32_t inst_id; + uint8_t svc_uuid[16]; + uint8_t char_uuid[16]; + uint8_t desc_uuid[16]; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_READ_CHARAC 0XC3 +#define HAL_EV_GATT_CLIENT_READ_DESC 0XC4 +struct hal_ev_gatt_client_read_data { + int32_t conn_id; + int32_t status; + int32_t is_primary; + int32_t inst_id; + uint8_t svc_uuid[16]; + uint8_t char_uuid[16]; + uint8_t desc_uuid[16]; + uint8_t value[600]; + int32_t len; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_WRITE_CHARAC 0XC5 +#define HAL_EV_GATT_CLIENT_WRITE_DESC 0XC6 +struct hal_ev_gatt_client_write_result { + int32_t conn_id; + int32_t status; + int32_t is_primary; + int32_t inst_id; + uint8_t svc_uuid[16]; + uint8_t char_uuid[16]; + uint8_t desc_uuid[16]; +} __attribute__((packed)); #endif //_BT_HAL_MSG_H_ diff --git a/bt-oal/bluez_hal/src/bt-hal-dbus-common-utils.c b/bt-oal/bluez_hal/src/bt-hal-dbus-common-utils.c index fdf2113..ce445db 100644 --- a/bt-oal/bluez_hal/src/bt-hal-dbus-common-utils.c +++ b/bt-oal/bluez_hal/src/bt-hal-dbus-common-utils.c @@ -1028,6 +1028,42 @@ char *_bt_hal_get_device_object_path(char *address) return object_path; } +GVariant *_bt_hal_get_managed_objects(void) +{ + GDBusConnection *conn; + GDBusProxy *manager_proxy; + GVariant *result = NULL; + GError *error = NULL; + + DBG("+"); + conn = _bt_hal_get_system_gconn(); + if (conn == NULL) + return NULL; + + manager_proxy = _bt_hal_get_manager_proxy(); + if (manager_proxy == NULL) + return NULL; + + result = g_dbus_proxy_call_sync(manager_proxy, "GetManagedObjects", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL); + if (!result) { + ERR("Can't get managed objects"); + return NULL; + } + + if (error) { + ERR("Fail to get ManagedObjects (Error: %s)", error->message); + g_clear_error(&error); + } + + return result; +} + + char *_bt_hal_convert_error_to_string(int error) { switch (error) { diff --git a/bt-oal/bluez_hal/src/bt-hal-dbus-common-utils.h b/bt-oal/bluez_hal/src/bt-hal-dbus-common-utils.h index aa7fcc5..3e63c12 100644 --- a/bt-oal/bluez_hal/src/bt-hal-dbus-common-utils.h +++ b/bt-oal/bluez_hal/src/bt-hal-dbus-common-utils.h @@ -100,7 +100,9 @@ extern "C" { #define BT_HAL_MEDIATRANSPORT_INTERFACE "org.bluez.MediaTransport1" #define BT_HAL_MEDIA_CONTROL_INTERFACE "org.bluez.MediaControl1" #define BT_HAL_PLAYER_CONTROL_INTERFACE "org.bluez.MediaPlayer1" +#define BT_HAL_GATT_SERVICE_INTERFACE "org.bluez.GattService1" #define BT_HAL_GATT_CHAR_INTERFACE "org.bluez.GattCharacteristic1" +#define BT_HAL_GATT_DESC_INTERFACE "org.bluez.GattDescriptor1" #define BT_HAL_NETWORK_INTERFACE "org.bluez.Network" #define BT_HAL_NETWORK_CLIENT_INTERFACE "org.bluez.Network1" #define BT_HAL_NETWORK_SERVER_INTERFACE "org.bluez.NetworkServer1" @@ -474,6 +476,8 @@ extern "C" { gboolean _bt_hal_is_service_enabled(const char *uuid); + GVariant *_bt_hal_get_managed_objects(void); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/bt-oal/bluez_hal/src/bt-hal-event-receiver.c b/bt-oal/bluez_hal/src/bt-hal-event-receiver.c index 8b8604e..fc85885 100644 --- a/bt-oal/bluez_hal/src/bt-hal-event-receiver.c +++ b/bt-oal/bluez_hal/src/bt-hal-event-receiver.c @@ -38,6 +38,7 @@ #include "bt-hal-agent.h" #include "bt-hal-adapter-le.h" #include "bt-hal-gatt-server.h" +#include "bt-hal-gatt-client.h" #define BASELEN_PROP_CHANGED (sizeof(struct hal_ev_adapter_props_changed) \ + sizeof(struct hal_property)) @@ -1400,6 +1401,7 @@ static void __bt_hal_device_property_changed_event(GVariant *msg, const char *pa DBG("@@gatt_connected: %d", gatt_connected); DBG("@@address: %s", address); _bt_hal_gatt_connected_state_event(gatt_connected, address); + _bt_hal_handle_gattc_connected_event(address, gatt_connected); g_free(address); } else if (!g_strcmp0(key, "Paired")) { gboolean paired = FALSE; diff --git a/bt-oal/bluez_hal/src/bt-hal-gatt-client.c b/bt-oal/bluez_hal/src/bt-hal-gatt-client.c index b87a352..5b8b6d9 100644 --- a/bt-oal/bluez_hal/src/bt-hal-gatt-client.c +++ b/bt-oal/bluez_hal/src/bt-hal-gatt-client.c @@ -26,6 +26,9 @@ #include #include #include +#include +#include + #include "bt-hal-log.h" #include "bt-hal-msg.h" @@ -33,6 +36,8 @@ #include "bt-hal-adapter-le.h" #include "bt-hal-gatt-client.h" +#include "bt-hal-dbus-common-utils.h" +#include "bt-hal-utils.h" /************************************************************************************ ** Static variables @@ -46,17 +51,132 @@ extern const btgatt_callbacks_t *bt_gatt_callbacks; DBG("%s", __FUNCTION__);\ } -typedef struct { - uint32_t client_if; - bt_uuid_t uuid; -} hal_registered_client_t; - #ifdef TIZEN_BT_HAL int le_scan_type = BT_GATTC_LE_SCAN_TYPE_PASSIVE; #endif static handle_stack_msg event_cb = NULL; +#define GATT_SERV_INTERFACE "org.bluez.GattService1" +#define MAX_HAL_OBJECT_PATH_LEN 100 + +typedef enum { + HAL_GATT_CHARACTERISTIC_PROPERTY_BROADCAST = 0x01, + HAL_GATT_CHARACTERISTIC_PROPERTY_READ = 0x02, + HAL_GATT_CHARACTERISTIC_PROPERTY_WRITE_NO_RESPONSE = 0x04, + HAL_GATT_CHARACTERISTIC_PROPERTY_WRITE = 0x08, + HAL_GATT_CHARACTERISTIC_PROPERTY_NOTIFY = 0x10, + HAL_GATT_CHARACTERISTIC_PROPERTY_INDICATE = 0x20, + HAL_GATT_CHARACTERISTIC_PROPERTY_SIGNED_WRITE = 0x40, + HAL_GATT_CHARACTERISTIC_PROPERTY_RELIABLE_WRITE = 0x80, + HAL_GATT_CHARACTERISTIC_PROPERTY_WRITABLE_AUXILIARIES = 0x100, + HAL_GATT_CHARACTERISTIC_PROPERTY_ENCRYPT_READ = 0x200, + HAL_GATT_CHARACTERISTIC_PROPERTY_ENCRYPT_WRITE = 0x400, + HAL_GATT_CHARACTERISTIC_PROPERTY_ENCRYPT_AUTHENTICATED_READ = 0x800, + HAL_GATT_CHARACTERISTIC_PROPERTY_ENCRYPT_AUTHENTICATED_WRITE = 0x1000, + HAL_GATT_CHARACTERISTIC_PROPERTY_EXTENDED_PROPS = 0xffff, +} bt_gatt_characteristic_property_t; + + +typedef enum { + HAL_GATT_WRITE_TYPE_WRITE_NO_RESPONSE = 1, + HAL_GATT_WRITE_TYPE_WRITE , +} hal_gatt_write_type_t; + + +typedef enum { + HAL_GATT_PROPERTY_BROADCAST = 0x01, /**< Broadcast property */ + HAL_GATT_PROPERTY_READ = 0x02, /**< Read property */ + HAL_GATT_PROPERTY_WRITE_WITHOUT_RESPONSE = 0x04, /**< Write without response property */ + HAL_GATT_PROPERTY_WRITE = 0x08, /**< Write property */ + HAL_GATT_PROPERTY_NOTIFY = 0x10, /**< Notify property */ + HAL_GATT_PROPERTY_INDICATE = 0x20, /**< Indicate property */ + HAL_GATT_PROPERTY_AUTHENTICATED_SIGNED_WRITES = 0x40, /**< Authenticated signed writes property */ + HAL_GATT_PROPERTY_EXTENDED_PROPERTIES = 0x80, /**< Extended properties */ +} hal_gatt_property_e; + +typedef struct { + int client_if; + bt_bdaddr_t bd_addr; + gboolean auto_connect; +} bt_pending_le_conn_info_s; + +typedef struct { + char *desc_path; + bt_uuid_t desc_uuid; +} hal_gattc_desc_t; + +typedef struct { + char *chr_path; + bt_uuid_t chr_uuid; + unsigned int permission; + GSList *gatt_list_descs; +} hal_gattc_char_t; + +typedef struct { + gchar *svc_path; + bt_uuid_t svc_uuid; + GSList *gatt_list_chars; +} hal_gattc_service_t; + +typedef struct { + bt_bdaddr_t bd_addr; /*remote server address*/ + int inst_id; /*server instance id*/ + GSList *gatt_list_services; +} hal_gattc_server_info_t; + +/* Linked List of connected GATT server */ +static GSList *hal_gattc_server_info_list = NULL; + +typedef struct { + int client_if; + bt_bdaddr_t bd_addr; /*remote server address*/ + int conn_id; + int inst_id; +} hal_gattc_client_info_t; + +/* Linked list of connected GATT client connection */ +static GSList * hal_gattc_client_info_list = NULL; + + +static bt_pending_le_conn_info_s *pending_le_conn_info = NULL; +static guint pending_le_conn_timer_id = 0; +static int bt_conn_id = 0; +static int bt_inst_id = 0; + +#define BT_GATTC_CL_MAX 32 + +typedef struct { + int conn_id; + btgatt_srvc_id_t srvc_id; + btgatt_gatt_id_t char_id; + btgatt_gatt_id_t desc_id; +} hal_gatt_resp_data_t; + + +typedef struct { + int client_if; + bt_uuid_t app_uuid; +} hal_gatt_client_app; + +static GSList * hal_gattc_client_app_list = NULL; + +static int bt_client_if = 0; + + +static bt_status_t __bt_connect_le_device_internal(int client_if, const bt_bdaddr_t *bd_addr, + gboolean auto_connect); +static bt_status_t _bt_hold_current_advertising(); +static gboolean __bt_hold_current_advertising_timeout_cb(gpointer user_data); +static gboolean __bt_connect_le_timer_cb(gpointer user_data); +static void __le_connection_req_cb(GDBusProxy *proxy, GAsyncResult *res, + gpointer user_data); +static hal_gattc_server_info_t *__bt_find_gatt_conn_info_from_conn_id(int conn_id); +static void _bt_hal_send_search_service_result_event(int conn_id, int is_primary, + const char* uuid_str, int inst_id); +static void _bt_hal_send_search_service_complete_event(int conn_id, int status); + + /* To send stack event to hal-av handler */ void _bt_hal_register_gatt_client_handler_cb(handle_stack_msg cb) { @@ -78,13 +198,14 @@ int _bt_hal_gatt_client_get_le_scan_type(void) static gboolean __bt_hal_register_client_cb(gpointer user_data) { struct hal_ev_gatt_client_registered ev; - hal_registered_client_t *client_info = user_data; + hal_gatt_client_app *client_info = user_data; + DBG("+"); /* Prepare to send AV connecting event */ memset(&ev, 0, sizeof(ev)); ev.status = BT_STATUS_SUCCESS; ev.client_if = client_info->client_if; - memcpy(ev.app_uuid, client_info->uuid.uu, sizeof(ev.app_uuid)); + memcpy(ev.app_uuid, client_info->app_uuid.uu, sizeof(ev.app_uuid)); if (!event_cb) ERR("GATT Callback not registered"); @@ -97,35 +218,117 @@ static gboolean __bt_hal_register_client_cb(gpointer user_data) return FALSE; } +static int __hal_generate_client_id() +{ + return ++bt_client_if; +} + +static hal_gatt_client_app *__hal_gattc_add_client_app(bt_uuid_t *app_uuid) +{ + GSList *l; + hal_gatt_client_app *info = NULL; + hal_gatt_client_app *gattc_app = NULL; + + //check if client app is already registered + for (l = hal_gattc_client_app_list; l != NULL; l = g_slist_next(l)) { + info = (hal_gatt_client_app*)l->data; + if (info == NULL) + continue; + + if (memcmp(&info->app_uuid, app_uuid, sizeof(bt_uuid_t)) == 0) { + DBG("gatt client app already registered"); + return info; + } + } + + DBG("adding the gatt client app"); + + //add client app + gattc_app = g_malloc0(sizeof(hal_gatt_client_app)); + if (gattc_app == NULL) { + DBG("Failed to allocate memory"); + return NULL; + } + + gattc_app->client_if = __hal_generate_client_id(); + memcpy(&gattc_app->app_uuid, app_uuid, sizeof(bt_uuid_t)); + + hal_gattc_client_app_list = g_slist_append(hal_gattc_client_app_list, gattc_app); + + return gattc_app; +} + +static bt_status_t __hal_gattc_register_client_app(bt_uuid_t *app_uuid) +{ + hal_gatt_client_app *gattc_app = NULL; + hal_gatt_client_app *client_app_info = NULL; + + DBG("+"); + /* add gatt client in list */ + gattc_app = __hal_gattc_add_client_app(app_uuid); + if (gattc_app == NULL) { + ERR("Failed to register gatt client app"); + return BT_STATUS_FAIL; + } + + /*send event */ + client_app_info = g_malloc0(sizeof(hal_gatt_client_app)); + if (NULL == client_app_info) { + ERR("Failed to allocate memory"); + return BT_STATUS_FAIL; + } + + client_app_info->client_if = gattc_app->client_if; + memcpy(&client_app_info->app_uuid, app_uuid, sizeof(bt_uuid_t)); + g_idle_add(__bt_hal_register_client_cb, (gpointer)client_app_info); + + DBG("registered client client_if [%d]", client_app_info->client_if); + + return BT_STATUS_SUCCESS; +} + /** Registers a GATT client application with the stack */ -bt_status_t register_client(bt_uuid_t *uuid) +bt_status_t btif_gattc_register_client(bt_uuid_t *uuid) +{ + CHECK_BTGATT_INIT(); + DBG("+"); + + return __hal_gattc_register_client_app(uuid); +} + +bt_status_t __hal_gattc_unregister_client(int client_if) { - hal_registered_client_t *client_info = NULL; + GSList *l; + hal_gatt_client_app *info = NULL; + + DBG("registered client count: [%d]", g_slist_length(hal_gattc_client_app_list)); - client_info = g_malloc0(sizeof(hal_registered_client_t)); - /* - * TODO: Actual client_if will be used here later when GATT register_client - * is implemented compeltely. For now return 1 as client_if. - */ - client_info->client_if = 1; - memcpy(client_info->uuid.uu, uuid->uu, sizeof(bt_uuid_t)); + /* remove the gatt client app */ + for (l = hal_gattc_client_app_list; l != NULL; ) { + info = (hal_gatt_client_app*)l->data; + l = g_slist_next(l); - /* - * As we need to provide async callback to user from HAL, simply schedule a - * callback method which will carry actual result - */ - g_idle_add(__bt_hal_register_client_cb, (gpointer)client_info); + if (info == NULL) + continue; - /* If available, then return success, else return error */ + if (info->client_if == client_if) { + DBG("gatt client app found"); + hal_gattc_client_app_list = g_slist_remove(hal_gattc_client_app_list, info); + g_free(info); + } + } + + DBG("registered client count: [%d]", g_slist_length(hal_gattc_client_app_list)); return BT_STATUS_SUCCESS; } -/* TODO: APIs will be implemented in subsequent patches whenever required */ /** Unregister a client application from the stack */ -bt_status_t unregister_client(int client_if) +bt_status_t btif_gattc_unregister_client(int client_if) { CHECK_BTGATT_INIT(); - return BT_STATUS_UNSUPPORTED; + DBG("+"); + + return __hal_gattc_unregister_client(client_if); } /** Start or stop LE device scanning */ @@ -144,19 +347,170 @@ bt_status_t scan(int client_if, bool start) } /** Create a connection to a remote LE or dual-mode device */ -bt_status_t connect(int client_if, const bt_bdaddr_t *bd_addr, +bt_status_t btif_gattc_client_connect(int client_if, const bt_bdaddr_t *bd_addr, bool is_direct) { + int ret = BT_STATUS_SUCCESS; + CHECK_BTGATT_INIT(); - return BT_STATUS_UNSUPPORTED; + DBG("+"); + + if (NULL == bd_addr) + return BT_STATUS_PARM_INVALID; + + ret = _bt_hold_current_advertising(); + if (ret == BT_STATUS_SUCCESS) { + DBG("Current advertising is held"); + pending_le_conn_info = g_malloc0(sizeof(bt_pending_le_conn_info_s)); + pending_le_conn_info->client_if = client_if; + memcpy(pending_le_conn_info->bd_addr.address, bd_addr->address, + BT_HAL_ADDRESS_LENGTH_MAX); + pending_le_conn_info->auto_connect = is_direct; + + pending_le_conn_timer_id = + g_timeout_add(1000, __bt_connect_le_timer_cb, NULL); + + return BT_STATUS_SUCCESS; + } else { + ERR("advertising is not stopped"); + } + + return __bt_connect_le_device_internal(client_if, bd_addr, is_direct); +} + + +static void __le_disconnection_req_cb(GDBusProxy *proxy, GAsyncResult *res, + gpointer user_data) +{ + GError *g_error = NULL; + GVariant *reply = NULL; + hal_gattc_client_info_t *gatt_conn_info = user_data; + int result = BT_STATUS_SUCCESS; + struct hal_ev_gatt_client_connected ev; + + DBG("+"); + + reply = g_dbus_proxy_call_finish(proxy, res, &g_error); + g_object_unref(proxy); + if (reply == NULL) { + ERR("Connect LE Dbus Call Error"); + if (g_error) { + ERR("Error: %s\n", g_error->message); + g_clear_error(&g_error); + } + result = BT_STATUS_FAIL; + } + g_variant_unref(reply); + + if (NULL == gatt_conn_info) { + ERR("server_data is NULL"); + return; + } + + /*send fail event*/ + if (result == BT_STATUS_FAIL) { + memset(&ev, 0, sizeof(ev)); + ev.conn_id = gatt_conn_info->conn_id; + ev.status = result; + ev.client_if = gatt_conn_info->client_if; + memcpy(ev.bdaddr, gatt_conn_info->bd_addr.address, + BT_HAL_ADDRESS_LENGTH_MAX); + + if (!event_cb) { + ERR("gatt client callback not registered"); + } else { + DBG("sending gatt client disconnected event"); + event_cb(HAL_EV_GATT_CLIENT_DISCONNECTED, (void *)&ev, sizeof(ev)); + } + } + + /*remove conn_info*/ + if (gatt_conn_info) + g_free(gatt_conn_info); + + DBG("-"); +} + +bt_status_t _hal_gattc_disconnect(int client_if, const bt_bdaddr_t *bd_addr, + int conn_id) +{ + char device_address[BT_HAL_ADDRESS_STRING_SIZE] = { 0 }; + gchar *device_path; + GDBusProxy *device_proxy; + GDBusConnection *conn; + int ret = BT_STATUS_SUCCESS; + hal_gattc_client_info_t *gattc_data; + + if (NULL == bd_addr) { + ERR("bd_addr is NULL"); + return BT_STATUS_PARM_INVALID; + } + + conn = _bt_hal_get_system_gconn(); + if (NULL == conn) { + ERR("_bt_gdbus_get_system_gconn returned NULL"); + return BT_STATUS_FAIL; + } + + _bt_hal_convert_addr_type_to_string(device_address, + (unsigned char *)bd_addr->address); + device_path = _bt_hal_get_device_object_path(device_address); + if (device_path == NULL) { + DBG("device_path NULL"); + ret = BT_STATUS_FAIL; + return ret; + } + + ERR("device_path:%s", device_path); + + device_proxy = g_dbus_proxy_new_sync(conn, G_DBUS_PROXY_FLAGS_NONE, + NULL, BT_HAL_BLUEZ_NAME, + device_path, BT_HAL_DEVICE_INTERFACE, NULL, NULL); + g_free(device_path); + if (NULL == device_proxy) { + ERR("device_proxy returned NULL"); + return BT_STATUS_FAIL; + } + + gattc_data = g_malloc0(sizeof(hal_gattc_client_info_t)); + if (NULL == gattc_data) { + ERR("Unable to allocate memory"); + ret = BT_STATUS_FAIL; + goto fail; + } + + memcpy(gattc_data->bd_addr.address, bd_addr->address, + BT_HAL_ADDRESS_LENGTH_MAX); + gattc_data->client_if = client_if; + gattc_data->conn_id = conn_id; + + DBG("DisconnectLE [%s]", device_address); + + g_dbus_proxy_call(device_proxy, "DisconnectLE", + NULL, + G_DBUS_CALL_FLAGS_NONE, + BT_HAL_MAX_DBUS_TIMEOUT, + NULL, + (GAsyncReadyCallback)__le_disconnection_req_cb, gattc_data); + return ret; +fail: + if (device_proxy) + g_object_unref(device_proxy); + + g_free(gattc_data); + + return ret; } /** Disconnect a remote device or cancel a pending connection */ -bt_status_t disconnect(int client_if, const bt_bdaddr_t *bd_addr, +bt_status_t btif_gattc_client_disconnect(int client_if, const bt_bdaddr_t *bd_addr, int conn_id) { CHECK_BTGATT_INIT(); - return BT_STATUS_UNSUPPORTED; + + DBG("+"); + + return _hal_gattc_disconnect(client_if, bd_addr, conn_id); } /** Clear the attribute cache for a given device */ @@ -166,16 +520,358 @@ bt_status_t refresh(int client_if, const bt_bdaddr_t *bd_addr) return BT_STATUS_UNSUPPORTED; } +static hal_gattc_service_t* _gattc_find_service_from_uuid(hal_gattc_server_info_t *conn_info, bt_uuid_t *svc_uuid) +{ + DBG("+"); + + GSList *l; + hal_gattc_service_t *info = NULL; + + for (l = conn_info->gatt_list_services; l != NULL; l = g_slist_next(l)) { + info = (hal_gattc_service_t*)l->data; + if (info == NULL) + continue; + + if (!memcmp(&info->svc_uuid, svc_uuid, sizeof(bt_uuid_t))) { + INFO("Found GATT service uuid"); + return info; + } + } + return NULL; +} + + +static hal_gattc_char_t* _gattc_find_char_from_uuid(hal_gattc_service_t *gattc_svc, bt_uuid_t *char_uuid) +{ + DBG("+"); + + GSList *l; + hal_gattc_char_t *info = NULL; + + for (l = gattc_svc->gatt_list_chars; l != NULL; l = g_slist_next(l)) { + info = (hal_gattc_char_t*)l->data; + if (info == NULL) + continue; + + if (!memcmp(&info->chr_uuid, char_uuid, sizeof(bt_uuid_t))) { + INFO("Found GATT char uuid"); + return info; + } + } + return NULL; +} + +static hal_gattc_desc_t* _gattc_find_desc_from_uuid(hal_gattc_char_t *gattc_char, bt_uuid_t *desc_uuid) +{ + DBG("+"); + + GSList *l; + hal_gattc_desc_t *info = NULL; + + for (l = gattc_char->gatt_list_descs; l != NULL; l = g_slist_next(l)) { + info = (hal_gattc_desc_t*)l->data; + if (info == NULL) + continue; + + if (!memcmp(&info->desc_uuid, desc_uuid, sizeof(bt_uuid_t))) { + INFO("Found GATT descriptor uuid"); + return info; + } + } + return NULL; +} + + +static hal_gattc_service_t* _hal_gatt_client_add_service(hal_gattc_server_info_t *conn_info, + const char *uuid_str, char *object_path) +{ + DBG("+"); + hal_gattc_service_t *gattc_service = NULL; + + gattc_service = g_malloc0(sizeof(hal_gattc_service_t)); + gattc_service->svc_path = g_strdup(object_path); + _bt_hal_convert_uuid_string_to_type(gattc_service->svc_uuid.uu, uuid_str); + + DBG("service count[%d]", g_slist_length(conn_info->gatt_list_services)); + + conn_info->gatt_list_services = g_slist_append(conn_info->gatt_list_services, gattc_service); + + DBG("svc path {%s] svc uuid [%s]", object_path, uuid_str); + + return gattc_service; +} + +static void _hal_gattc_add_characteristic(hal_gattc_service_t *gatt_svc, char *char_handle) +{ + DBG("+"); + hal_gattc_char_t *gattc_char = NULL; + + gattc_char = g_malloc0(sizeof(hal_gattc_char_t)); + gattc_char->chr_path = g_strdup(char_handle); + + DBG("svc path: [%s]", gatt_svc->svc_path); + DBG("char path: [%s]", gattc_char->chr_path); + + gatt_svc->gatt_list_chars = g_slist_append(gatt_svc->gatt_list_chars, gattc_char); +} + +static void _gattc_create_new_service(hal_gattc_server_info_t *conn_info, gboolean is_primary, + const char* uuid_str, char *object_path, GPtrArray *gp_char_array) +{ + hal_gattc_service_t* gatt_svc = NULL; + int i; + gchar *gp_char_path = NULL; + + DBG("+"); + + /* add the service */ + gatt_svc = _hal_gatt_client_add_service(conn_info, uuid_str, object_path); + if (gatt_svc == NULL) { + ERR("Failed to add service"); + return; + } + + /* add the characteristic */ + for (i = 0; i < gp_char_array->len; i++) { + gp_char_path = g_ptr_array_index(gp_char_array, i); + _hal_gattc_add_characteristic(gatt_svc, gp_char_path); + } + + g_ptr_array_free(gp_char_array, TRUE); +} + +static void _hal_gattc_add_descriptor(hal_gattc_char_t *gattc_char, char *desc_path) +{ + hal_gattc_desc_t *gattc_desc = NULL; + + gattc_desc = g_malloc0(sizeof(hal_gattc_desc_t)); + gattc_desc->desc_path = g_strdup(desc_path); + + gattc_char->gatt_list_descs = g_slist_append(gattc_char->gatt_list_descs, gattc_desc); + + DBG("char path: [%s]", gattc_char->chr_path); + DBG("desc path: [%s]", gattc_desc->desc_path); +} + +static void _hal_gattc_update_char_property(hal_gattc_char_t *gattc_char, const char* char_uuid_str, + GPtrArray *gp_desc_array, unsigned int char_permission) +{ + gchar *gp_desc_path = NULL; + int i; + + DBG("+"); + + if (char_uuid_str == NULL) { + DBG("char_uuid_str is NULL"); + return; + } + + //update the char uuid + DBG("char UUID: [%s] ", char_uuid_str); + DBG("char path: [%s]", gattc_char->chr_path); + + _bt_hal_convert_uuid_string_to_type(gattc_char->chr_uuid.uu, char_uuid_str); + + //update char permission + gattc_char->permission = char_permission; + + //add the descriptor + for (i = 0; i < gp_desc_array->len; i++) { + gp_desc_path = g_ptr_array_index(gp_desc_array, i); + _hal_gattc_add_descriptor(gattc_char, gp_desc_path); + } +} + +static void _hal_gattc_update_desc_property(hal_gattc_desc_t *gattc_desc, const char* desc_uuid_str) +{ + DBG("+"); + + if (desc_uuid_str == NULL) { + DBG("char_uuid_str is NULL"); + return; + } + + //update the descriptor uuid + DBG("desc UUID: [%s] ", desc_uuid_str); + DBG("desc path: [%s]", gattc_desc->desc_path); + + _bt_hal_convert_uuid_string_to_type(gattc_desc->desc_uuid.uu, desc_uuid_str); +} + +static void browse_service_char(int conn_id) +{ + hal_gattc_server_info_t *conn_info = NULL; + GSList *l; + GSList *k; + GSList *m; + hal_gattc_service_t *svc_info = NULL; + hal_gattc_char_t *char_info = NULL; + hal_gattc_desc_t *desc_info = NULL; + + DBG("+"); + + conn_info = __bt_find_gatt_conn_info_from_conn_id(conn_id); + if (conn_info == NULL) { + DBG("conn_info is NULL"); + return; + } + + DBG("service count[%d]", g_slist_length(conn_info->gatt_list_services)); + + for (l = conn_info->gatt_list_services; l != NULL; l = g_slist_next(l)) { + svc_info = (hal_gattc_service_t*)l->data; + if (svc_info == NULL) + continue; + + DBG("svc path [%s]", svc_info->svc_path); + + /* find characteristic object path */ + for (k = svc_info->gatt_list_chars; k != NULL; k = g_slist_next(k)) { + char_info = (hal_gattc_char_t *)k->data; + if (char_info == NULL) + continue; + + DBG("char path[%s]", char_info->chr_path); + + /* descriptor */ + for (m = char_info->gatt_list_descs; m != NULL; m = g_slist_next(m)) { + desc_info = (hal_gattc_desc_t *)m->data; + if (desc_info == NULL) + continue; + + DBG("desc path[%s]", desc_info->desc_path); + } + } + } +} + + + /** * Enumerate all GATT services on a connected device. * Optionally, the results can be filtered for a given UUID. */ -bt_status_t search_service(int conn_id, bt_uuid_t *filter_uuid) +static bt_status_t _gattc_client_search_service(int conn_id) { CHECK_BTGATT_INIT(); - return BT_STATUS_UNSUPPORTED; + + GVariant *result = NULL; + GVariantIter *iter; + GVariantIter *svc_iter; + GVariantIter *interface_iter; + char *object_path = NULL; + char *interface_str = NULL; + const gchar *key = NULL; + GVariant *value = NULL; + GPtrArray *gp_array = NULL; + char device_address[BT_HAL_ADDRESS_STRING_SIZE] = { 0 }; + char temp_address[BT_HAL_ADDRESS_STRING_SIZE] = { 0 }; + int ret = BT_STATUS_FAIL; + int idx = 0; + const gchar *uuid_str = NULL; + gsize len = 0; + hal_gattc_server_info_t *conn_info = NULL; + gboolean is_primary = FALSE; + int svc_count = 0; + + char *char_handle = NULL; + GVariantIter *char_iter = NULL; + GPtrArray *gp_char_array = NULL; + + DBG("+"); + + conn_info = __bt_find_gatt_conn_info_from_conn_id(conn_id); + if (NULL == conn_info) { + DBG("Failed to get the conn_info"); + return BT_STATUS_FAIL; + } + + _bt_hal_convert_addr_type_to_string(device_address, + (unsigned char *)conn_info->bd_addr.address); + + result = _bt_hal_get_managed_objects(); + if (result == NULL) + return ret; + + gp_array = g_ptr_array_new(); + g_variant_get(result, "(a{oa{sa{sv}}})", &iter); + + while (g_variant_iter_loop(iter, "{&oa{sa{sv}}}", &object_path, + &interface_iter)) { + if (object_path == NULL) + continue; + + _bt_hal_convert_device_path_to_address(object_path, temp_address); + + if (g_strcmp0(temp_address, device_address) != 0) + continue; + + while (g_variant_iter_loop(interface_iter, "{sa{sv}}", + &interface_str, &svc_iter)) { + if (g_strcmp0(interface_str, GATT_SERV_INTERFACE) != 0) + continue; + + DBG("[%d] Object Path : %s", idx++, object_path); + /* for characteristic */ + gp_char_array = g_ptr_array_new(); + while (g_variant_iter_loop(svc_iter, "{sv}", &key, &value)) { + if (g_strcmp0(key, "Primary") == 0) { + is_primary = g_variant_get_boolean(value); + DBG("primary"); + if (is_primary) { + g_ptr_array_add(gp_array, (gpointer)object_path); + svc_count++; + } + } else if (g_strcmp0(key, "UUID") == 0) { + uuid_str = g_variant_get_string(value, &len); + DBG(" UUID: [%s]", uuid_str); + } else if (g_strcmp0(key, "Characteristics") == 0) { + g_variant_get(value, "ao", &char_iter); + if (char_iter != NULL) { + while (g_variant_iter_loop(char_iter, "&o", &char_handle)) { + DBG("char handle : %s", char_handle); + g_ptr_array_add(gp_char_array, (gpointer)char_handle); + } + } + } + } + + DBG("send search service result event"); + _bt_hal_send_search_service_result_event(conn_id, is_primary, + uuid_str, conn_info->inst_id); + + _gattc_create_new_service(conn_info, is_primary, uuid_str, object_path, gp_char_array); + } + } + + if (gp_array->len == 0 || svc_count == 0) { + ERR("gp_array is NULL"); + ret = BT_STATUS_FAIL; + } else { + ret = BT_STATUS_SUCCESS; + } + + browse_service_char(conn_id); + /* send search service complete event */ + _bt_hal_send_search_service_complete_event(conn_id, ret); + + g_ptr_array_free(gp_array, TRUE); + g_variant_iter_free(iter); + g_variant_unref(result); + DBG("-"); + return ret; } +bt_status_t btif_gattc_client_search_service(int conn_id, bt_uuid_t *filter_uuid) +{ + if (NULL == filter_uuid) { + DBG("Browse all the services"); + return _gattc_client_search_service(conn_id); + } else { + DBG("TODO implement it"); + return BT_STATUS_UNSUPPORTED; + } +} /** * Enumerate included services for a given service. * Set start_incl_srvc_id to NULL to get the first included service. @@ -187,64 +883,1191 @@ bt_status_t get_included_service(int conn_id, btgatt_srvc_id_t *srvc_id, return BT_STATUS_UNSUPPORTED; } -/** - * Enumerate characteristics for a given service. - * Set start_char_id to NULL to get the first characteristic. - */ -bt_status_t get_characteristic(int conn_id, - btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *start_char_id) +static void _bt_hal_send_client_char_search_result_event(int conn_id, int status, + btgatt_srvc_id_t *svc_id, bt_uuid_t *char_uuid, int char_prop) { - CHECK_BTGATT_INIT(); - return BT_STATUS_UNSUPPORTED; + struct hal_ev_gatt_client_char_search_result ev; + + if (!event_cb) { + ERR("gatt client callback not registered"); + return; + } + + DBG("sending gatt client search char result event conn_id[%d] status[%d]", conn_id, status); + + memset(&ev, 0, sizeof(ev)); + ev.conn_id = conn_id; + ev.inst_id = svc_id->id.inst_id; + ev.is_primary = svc_id->is_primary; + ev.status = status; + memcpy(ev.svc_uuid, svc_id->id.uuid.uu, sizeof(ev.svc_uuid)); + + if (status == BT_STATUS_SUCCESS) { + DBG("building char uuid"); + memcpy(ev.char_uuid, char_uuid->uu, sizeof(ev.char_uuid)); + ev.char_prop = char_prop; + } + + DBG("sending the char search event"); + + event_cb(HAL_EV_GATT_CLIENT_CHARAC_SEARCH_RESULT, (void *)&ev, sizeof(ev)); +} + +static int _hal_get_permission_flag(char *permission) +{ + int ret = 0; + + if (NULL == permission) { + ERR("gatt permission is NULL"); + return ret; + } + + if (!g_strcmp0(permission, "broadcast")) + ret = HAL_GATT_CHARACTERISTIC_PROPERTY_BROADCAST; + else if (!g_strcmp0(permission, "read")) + ret = HAL_GATT_CHARACTERISTIC_PROPERTY_READ; + else if (!g_strcmp0(permission, "write-without-response")) + ret = HAL_GATT_CHARACTERISTIC_PROPERTY_WRITE_NO_RESPONSE; + else if (!g_strcmp0(permission, "write")) + ret = HAL_GATT_CHARACTERISTIC_PROPERTY_WRITE; + else if (!g_strcmp0(permission, "notify")) + ret = HAL_GATT_CHARACTERISTIC_PROPERTY_NOTIFY; + else if (!g_strcmp0(permission, "indicate")) + ret = HAL_GATT_CHARACTERISTIC_PROPERTY_INDICATE; + else if (!g_strcmp0(permission, "authenticated-signed-writes")) + ret = HAL_GATT_CHARACTERISTIC_PROPERTY_SIGNED_WRITE; + else if (!g_strcmp0(permission, "reliable-write")) + ret = HAL_GATT_CHARACTERISTIC_PROPERTY_RELIABLE_WRITE; + else if (!g_strcmp0(permission, "writable-auxiliaries")) + ret = HAL_GATT_CHARACTERISTIC_PROPERTY_WRITABLE_AUXILIARIES; + else if (!g_strcmp0(permission, "encrypt-read")) + ret = HAL_GATT_CHARACTERISTIC_PROPERTY_ENCRYPT_READ; + else if (!g_strcmp0(permission, "encrypt-write")) + ret = HAL_GATT_CHARACTERISTIC_PROPERTY_ENCRYPT_WRITE; + else if (!g_strcmp0(permission, "encrypt-authenticated-read")) + ret = HAL_GATT_CHARACTERISTIC_PROPERTY_ENCRYPT_AUTHENTICATED_READ; + else if (!g_strcmp0(permission, "encrypt-authenticated-write")) + ret = HAL_GATT_CHARACTERISTIC_PROPERTY_ENCRYPT_AUTHENTICATED_WRITE; + + return ret; +} + + +static bt_status_t _hal_gattc_get_characteristic_info(hal_gattc_char_t *gattc_char) +{ + GDBusProxy *properties_proxy = NULL; + GError *error = NULL; + GVariant *value = NULL; + GVariant *result = NULL; + GDBusConnection *g_conn; + const gchar *key; + char *char_desc_handle = NULL; + gsize len; + GVariantIter *property_iter; + GVariantIter *char_desc_iter; + char* char_handle = NULL; + const gchar *char_uuid_str = NULL; + GPtrArray *gp_desc_array = NULL; + GVariantIter *char_perm_iter; + gchar* permission; + unsigned int char_permission = 0 ; + + DBG("+"); + + if (gattc_char->chr_path == NULL) { + DBG("char path is NULL"); + return BT_STATUS_FAIL; + } + char_handle = gattc_char->chr_path; + + DBG("char path:[%s]", gattc_char->chr_path); + + g_conn = _bt_hal_get_system_gconn(); + if (NULL == g_conn) { + ERR("_bt_gdbus_get_system_gconn returned NULL"); + return BT_STATUS_FAIL; + } + + properties_proxy = g_dbus_proxy_new_sync(g_conn, + G_DBUS_PROXY_FLAGS_NONE, NULL, + BT_HAL_BLUEZ_NAME, + char_handle, + BT_HAL_PROPERTIES_INTERFACE, + NULL, &error); + + if (properties_proxy == NULL) { + ERR("properties_proxy returned NULL"); + return BT_STATUS_FAIL; + } + + result = g_dbus_proxy_call_sync(properties_proxy, + "GetAll", + g_variant_new("(s)", BT_HAL_GATT_CHAR_INTERFACE), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + if (!result) { + if (error != NULL) { + ERR("Fail to get properties (Error: %s)", error->message); + g_clear_error(&error); + } else + ERR("Fail to get properties"); + g_object_unref(properties_proxy); + return BT_STATUS_FAIL; + } + + gp_desc_array = g_ptr_array_new(); + + g_variant_get(result, "(a{sv})", &property_iter); + + while (g_variant_iter_loop(property_iter, "{sv}", &key, &value)) { + if (!g_strcmp0(key, "UUID")) { + char_uuid_str = g_variant_dup_string(value, &len); + DBG("char UUID [%s]", char_uuid_str); + } else if (!g_strcmp0(key, "Flags")) { + g_variant_get(value, "as", &char_perm_iter); + char_permission = 0x00; + + while (g_variant_iter_loop(char_perm_iter, "s", &permission)) { + DBG("char permission: [%s]", permission); + char_permission |= _hal_get_permission_flag(permission); + } + } else if (!g_strcmp0(key, "Descriptors")) { + g_variant_get(value, "ao", &char_desc_iter); + while (g_variant_iter_loop(char_desc_iter, "&o", &char_desc_handle)) { + DBG("char descriptor handle : %s", char_desc_handle); + + g_ptr_array_add(gp_desc_array, (gpointer)char_desc_handle); + } + } + } + + _hal_gattc_update_char_property(gattc_char, char_uuid_str, gp_desc_array, char_permission); + + g_variant_iter_free(property_iter); + g_variant_unref(result); + g_object_unref(properties_proxy); + g_ptr_array_free(gp_desc_array, TRUE); + + return BT_STATUS_SUCCESS; +} + +static bt_status_t _gattc_get_all_characteristic(int conn_id, + btgatt_srvc_id_t *srvc_id) +{ + hal_gattc_server_info_t * conn_info = NULL; + hal_gattc_service_t *gattc_service = NULL; + GSList *l; + hal_gattc_char_t *gattc_char = NULL; + char svc_uuid_str[BT_HAL_UUID_STRING_LEN]; + int status = BT_STATUS_FAIL; + + DBG("+"); + DBG("conn_id[%d]", conn_id); + + conn_info = __bt_find_gatt_conn_info_from_conn_id(conn_id); + if (NULL == conn_info) { + DBG("Failed to get the conn_info for conn_id[%d]", conn_id); + return BT_STATUS_FAIL; + } + + /* find service */ + gattc_service = _gattc_find_service_from_uuid(conn_info, &srvc_id->id.uuid); + if (NULL == gattc_service) { + DBG("Failed to get the gatt service"); + return BT_STATUS_FAIL; + } + + DBG("service path [%s]", gattc_service->svc_path); + + _bt_hal_convert_uuid_type_to_string(svc_uuid_str, gattc_service->svc_uuid.uu); + DBG("service uuid [%s]", svc_uuid_str); + + /* find characteristic object path */ + for (l = gattc_service->gatt_list_chars; l != NULL; l = g_slist_next(l)) { + gattc_char = (hal_gattc_char_t *)l->data; + status = _hal_gattc_get_characteristic_info(gattc_char); + + /* send event */ + if (BT_STATUS_SUCCESS == status) { + DBG("Sending the success charateristics event"); + _bt_hal_send_client_char_search_result_event(conn_id, status, srvc_id, + &gattc_char->chr_uuid, gattc_char->permission); + } + } + + DBG("sending final event"); + + status = BT_STATUS_FAIL; + _bt_hal_send_client_char_search_result_event(conn_id, status, srvc_id, NULL, 0); + + browse_service_char(conn_id); + /* retrive uuid for characteristic and object path for descriptor */ + + return BT_STATUS_SUCCESS; +} + +/** + * Enumerate characteristics for a given service. + * Set start_char_id to NULL to get the first characteristic. + */ +bt_status_t btif_gattc_get_characteristic(int conn_id, + btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *start_char_id) +{ + DBG("+"); + CHECK_BTGATT_INIT(); + + if (start_char_id == NULL) { + DBG("Get all the characteristics"); + return _gattc_get_all_characteristic(conn_id, srvc_id); + + } else { + DBG("TBD Get specific characteristics"); + return BT_STATUS_UNSUPPORTED; + } +} + +static bt_status_t _hal_gattc_get_descriptor_info(hal_gattc_desc_t *gattc_desc) +{ + GDBusProxy *properties_proxy = NULL; + GError *error = NULL; + GVariant *value = NULL; + GVariant *result = NULL; + GDBusConnection *g_conn; + const gchar *key; + gsize len; + GVariantIter *property_iter; + char* desc_handle = NULL; + const gchar *desc_uuid_str = NULL; + + DBG("+"); + + if (gattc_desc->desc_path == NULL) { + DBG("desc path is NULL"); + return BT_STATUS_FAIL; + } + desc_handle = gattc_desc->desc_path; + + DBG("desc path:[%s]", gattc_desc->desc_path); + + g_conn = _bt_hal_get_system_gconn(); + if (NULL == g_conn) { + ERR("_bt_gdbus_get_system_gconn returned NULL"); + return BT_STATUS_FAIL; + } + + properties_proxy = g_dbus_proxy_new_sync(g_conn, + G_DBUS_PROXY_FLAGS_NONE, NULL, + BT_HAL_BLUEZ_NAME, + desc_handle, + BT_HAL_PROPERTIES_INTERFACE, + NULL, &error); + + if (properties_proxy == NULL) { + ERR("properties_proxy returned NULL"); + return BT_STATUS_FAIL; + } + + result = g_dbus_proxy_call_sync(properties_proxy, + "GetAll", + g_variant_new("(s)", BT_HAL_GATT_DESC_INTERFACE), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + if (!result) { + if (error != NULL) { + ERR("Fail to get properties (Error: %s)", error->message); + g_clear_error(&error); + } else + ERR("Fail to get properties"); + g_object_unref(properties_proxy); + return BT_STATUS_FAIL; + } + + g_variant_get(result, "(a{sv})", &property_iter); + + while (g_variant_iter_loop(property_iter, "{sv}", &key, &value)) { + if (!g_strcmp0(key, "UUID")) { + desc_uuid_str = g_variant_dup_string(value, &len); + DBG("desc UUID [%s]", desc_uuid_str); + } + } + + _hal_gattc_update_desc_property(gattc_desc, desc_uuid_str); + + g_variant_iter_free(property_iter); + g_variant_unref(result); + g_object_unref(properties_proxy); + + return BT_STATUS_SUCCESS; +} + +static void _bt_hal_send_client_desc_search_result_event(int conn_id, int status, + btgatt_srvc_id_t *svc_id, btgatt_gatt_id_t *char_id, bt_uuid_t *desc_uuid) +{ + struct hal_ev_gatt_client_desc_search_result ev; + + if (!event_cb) { + ERR("gatt client callback not registered"); + return; + } + + DBG("sending gatt client search desc result event conn_id[%d] status[%d]", conn_id, status); + + memset(&ev, 0, sizeof(ev)); + ev.conn_id = conn_id; + ev.inst_id = svc_id->id.inst_id; + ev.is_primary = svc_id->is_primary; + ev.status = status; + + memcpy(ev.svc_uuid, svc_id->id.uuid.uu, sizeof(ev.svc_uuid)); + memcpy(ev.char_uuid, char_id->uuid.uu, sizeof(ev.char_uuid)); + + if (status == BT_STATUS_SUCCESS) { + DBG("building desc uuid"); + memcpy(ev.desc_uuid, desc_uuid->uu, sizeof(ev.desc_uuid)); + } + + DBG("sending the desc search event"); + + event_cb(HAL_EV_GATT_CLIENT_DESC_SEARCH_RESULT, (void *)&ev, sizeof(ev)); +} + +static bt_status_t _hal_gattc_get_all_descriptor(int conn_id, + btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id) +{ + hal_gattc_server_info_t * conn_info = NULL; + hal_gattc_service_t *gattc_service = NULL; + GSList *l; + hal_gattc_char_t *gattc_char = NULL; + hal_gattc_desc_t *gattc_desc = NULL; + char svc_uuid_str[BT_HAL_UUID_STRING_LEN]; + char char_uuid_str[BT_HAL_UUID_STRING_LEN]; + int status = BT_STATUS_FAIL; + + DBG("+"); + + conn_info = __bt_find_gatt_conn_info_from_conn_id(conn_id); + if (NULL == conn_info) { + DBG("Failed to get the conn_info for conn_id[%d]", conn_id); + return BT_STATUS_FAIL; + } + + /* find service */ + gattc_service = _gattc_find_service_from_uuid(conn_info, &srvc_id->id.uuid); + if (NULL == gattc_service) { + DBG("Failed to get the gatt service"); + return BT_STATUS_FAIL; + } + + DBG("service path [%s]", gattc_service->svc_path); + _bt_hal_convert_uuid_type_to_string(svc_uuid_str, gattc_service->svc_uuid.uu); + DBG("service uuid [%s]", svc_uuid_str); + + /* find characteristics */ + gattc_char = _gattc_find_char_from_uuid(gattc_service, &char_id->uuid); + if (NULL == gattc_char) { + DBG("Failed to get the gatt char"); + return BT_STATUS_FAIL; + } + + DBG("char path [%s]", gattc_char->chr_path); + _bt_hal_convert_uuid_type_to_string(char_uuid_str, gattc_char->chr_uuid.uu); + DBG("char uuid [%s]", char_uuid_str); + + /* get descriptor uuid */ + for (l = gattc_char->gatt_list_descs; l != NULL; l = g_slist_next(l)) { + gattc_desc = (hal_gattc_desc_t *)l->data; + status = _hal_gattc_get_descriptor_info(gattc_desc); + + /* send event */ + if (BT_STATUS_SUCCESS == status) { + DBG("Sending the success descriptor event"); + _bt_hal_send_client_desc_search_result_event(conn_id, status, srvc_id, + char_id, &gattc_desc->desc_uuid); + } + } + + DBG("sending final event"); + + status = BT_STATUS_FAIL; + _bt_hal_send_client_desc_search_result_event(conn_id, status, srvc_id, char_id, NULL); + + browse_service_char(conn_id); + /* retrive uuid for characteristic and object path for descriptor */ + + return BT_STATUS_SUCCESS; +} + +/** + * Enumerate descriptors for a given characteristic. + * Set start_descr_id to NULL to get the first descriptor. + */ +bt_status_t btif_gattc_get_descriptor(int conn_id, + btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, + btgatt_gatt_id_t *start_descr_id) +{ + DBG("+"); + CHECK_BTGATT_INIT(); + + if (start_descr_id == NULL) { + DBG("Get all the descriptors"); + return _hal_gattc_get_all_descriptor(conn_id, srvc_id, char_id); + } else { + DBG("TBD Get specific descriptor"); + return BT_STATUS_UNSUPPORTED; + } +} + +static void __hal_send_char_read_event(hal_gatt_resp_data_t *resp_data, int result, uint8_t *value, int len) +{ + struct hal_ev_gatt_client_read_data ev; + + if (!event_cb) { + ERR("gatt client callback not registered"); + return; + } + + DBG("sending gatt client charac read conn_id[%d] status[%d]", resp_data->conn_id, result); + + memset(&ev, 0, sizeof(ev)); + ev.conn_id = resp_data->conn_id; + ev.inst_id = resp_data->srvc_id.id.inst_id; + ev.is_primary = resp_data->srvc_id.is_primary; + ev.status = result; + + memcpy(ev.svc_uuid, resp_data->srvc_id.id.uuid.uu, sizeof(ev.svc_uuid)); + memcpy(ev.char_uuid, resp_data->char_id.uuid.uu, sizeof(ev.char_uuid)); + + ev.len = len; + if (len > 0) { + DBG("building the char read value [%d]", len); + memcpy(ev.value, value, len); + } + + DBG("sending the gatt client read charac event"); + + event_cb(HAL_EV_GATT_CLIENT_READ_CHARAC, (void *)&ev, sizeof(ev)); +} + +static void __hal_internal_read_char_cb(GObject *source_object, + GAsyncResult *res, gpointer user_data) +{ + GError *error = NULL; + GDBusConnection *system_gconn = NULL; + GVariant *value; + GVariantIter *iter; + GByteArray *gp_byte_array = NULL; + guint8 g_byte; + hal_gatt_resp_data_t *resp_data = user_data; + int result = BT_STATUS_SUCCESS; + int i; + + DBG("+"); + + system_gconn = _bt_hal_get_system_gconn(); + value = g_dbus_connection_call_finish(system_gconn, res, &error); + + if (error) { + ERR("Read Characteristic dbus failed Error:", error->message); + + //send failed event + result = BT_STATUS_FAIL; + __hal_send_char_read_event(resp_data, result, NULL, 0); + g_clear_error(&error); + g_free(resp_data); + return; + } + + gp_byte_array = g_byte_array_new(); + g_variant_get(value, "(ay)", &iter); + + while (g_variant_iter_loop(iter, "y", &g_byte)) + g_byte_array_append(gp_byte_array, &g_byte, 1); + + //print the value + DBG("value is"); + for (i = 0; i < gp_byte_array->len; i++) + DBG("%02x", gp_byte_array->data[i]); + + //send value event + __hal_send_char_read_event(resp_data, result, gp_byte_array->data, gp_byte_array->len); + + g_free(resp_data); + + g_byte_array_free(gp_byte_array, TRUE); + g_variant_iter_free(iter); + g_variant_unref(value); + + DBG("-"); +} + + +static bt_status_t _hal_read_characteristic_value(int conn_id, btgatt_srvc_id_t *srvc_id, + btgatt_gatt_id_t *char_id, int auth_req) +{ + GDBusConnection *g_conn; + hal_gatt_resp_data_t *resp_data; + hal_gattc_service_t *gattc_service = NULL; + GVariantBuilder *builder = NULL; + guint16 offset = 0; + hal_gattc_server_info_t * conn_info = NULL; + hal_gattc_char_t *gattc_char = NULL; + char svc_uuid_str[BT_HAL_UUID_STRING_LEN]; + char char_uuid_str[BT_HAL_UUID_STRING_LEN]; + char* char_handle = NULL; + + DBG("+"); + + /* get the connection info */ + conn_info = __bt_find_gatt_conn_info_from_conn_id(conn_id); + if (NULL == conn_info) { + DBG("Failed to get the conn_info for conn_id[%d]", conn_id); + return BT_STATUS_FAIL; + } + + /* find service */ + gattc_service = _gattc_find_service_from_uuid(conn_info, &srvc_id->id.uuid); + if (NULL == gattc_service) { + DBG("Failed to get the gatt service"); + return BT_STATUS_FAIL; + } + + DBG("service path [%s]", gattc_service->svc_path); + _bt_hal_convert_uuid_type_to_string(svc_uuid_str, gattc_service->svc_uuid.uu); + DBG("service uuid [%s]", svc_uuid_str); + + + /* find characteristic */ + gattc_char = _gattc_find_char_from_uuid(gattc_service, &char_id->uuid); + if (NULL == gattc_char) { + DBG("Failed to get the gatt char"); + return BT_STATUS_FAIL; + } + + DBG("char path [%s]", gattc_char->chr_path); + _bt_hal_convert_uuid_type_to_string(char_uuid_str, gattc_char->chr_uuid.uu); + DBG("char uuid [%s]", char_uuid_str); + + g_conn = _bt_hal_get_system_gconn(); + if (NULL == g_conn) { + ERR("_bt_gdbus_get_system_gconn returned NULL"); + return BT_STATUS_FAIL; + } + + resp_data = g_malloc0(sizeof(hal_gatt_resp_data_t)); + if (NULL == resp_data) { + ERR("failed to get the memory"); + return BT_STATUS_FAIL; + } + + resp_data->conn_id = conn_id; + memcpy(&resp_data->srvc_id, srvc_id, sizeof(btgatt_srvc_id_t)); + memcpy(&resp_data->char_id, char_id, sizeof(btgatt_gatt_id_t)); + + builder = g_variant_builder_new(G_VARIANT_TYPE("a{sv}")); + + /*offset*/ + g_variant_builder_add(builder, "{sv}", "offset", + g_variant_new("q", offset)); + + char_handle = gattc_char->chr_path; + + DBG("calling char read value"); + + g_dbus_connection_call(g_conn, BT_HAL_BLUEZ_NAME, char_handle, BT_HAL_GATT_CHAR_INTERFACE, + "ReadValue", g_variant_new("(a{sv})", builder), + G_VARIANT_TYPE("(ay)"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, + (GAsyncReadyCallback)__hal_internal_read_char_cb, + (gpointer)resp_data); + g_variant_builder_unref(builder); + + return BT_STATUS_SUCCESS; +} + +/** Read a characteristic on a remote device */ +bt_status_t btif_read_characteristic(int conn_id, + btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, + int auth_req) +{ + CHECK_BTGATT_INIT(); + DBG("+"); + + return _hal_read_characteristic_value(conn_id, srvc_id, char_id, auth_req); +} + +static void __hal_send_char_write_event(hal_gatt_resp_data_t *resp_data, int result) +{ + struct hal_ev_gatt_client_write_result ev; + + if (!event_cb) { + ERR("gatt client callback not registered"); + return; + } + + DBG("sending gatt client charac write conn_id[%d] status[%d]", resp_data->conn_id, result); + + memset(&ev, 0, sizeof(ev)); + ev.conn_id = resp_data->conn_id; + ev.inst_id = resp_data->srvc_id.id.inst_id; + ev.is_primary = resp_data->srvc_id.is_primary; + ev.status = result; + + memcpy(ev.svc_uuid, resp_data->srvc_id.id.uuid.uu, sizeof(ev.svc_uuid)); + memcpy(ev.char_uuid, resp_data->char_id.uuid.uu, sizeof(ev.char_uuid)); + + DBG("sending the gatt client write charac event"); + + event_cb(HAL_EV_GATT_CLIENT_WRITE_CHARAC, (void *)&ev, sizeof(ev)); +} + +static void __hal_bluetooth_internal_write_cb(GObject *source_object, + GAsyncResult *res, gpointer user_data) +{ + GError *error = NULL; + GDBusConnection *system_gconn = NULL; + GVariant *value; + hal_gatt_resp_data_t *resp_data = user_data; + int result = BT_STATUS_SUCCESS; + + DBG("+"); + + system_gconn = _bt_hal_get_system_gconn(); + value = g_dbus_connection_call_finish(system_gconn, res, &error); + + if (error) { + ERR("write Characteristic dbus failed Error:", error->message); + + result = BT_STATUS_FAIL; + //send failed event + __hal_send_char_write_event(resp_data, result); + g_clear_error(&error); + g_free(resp_data); + return; + } + + //send write value event + __hal_send_char_write_event(resp_data, result); + + g_free(resp_data); + g_variant_unref(value); + + DBG("-"); +} + +static bt_status_t __hal_get_write_prop(hal_gatt_write_type_t type, hal_gatt_property_e *prop) +{ + switch (type) { + case HAL_GATT_WRITE_TYPE_WRITE: + *prop = HAL_GATT_PROPERTY_WRITE; + break; + case HAL_GATT_WRITE_TYPE_WRITE_NO_RESPONSE: + *prop = HAL_GATT_PROPERTY_WRITE_WITHOUT_RESPONSE; + break; + default: + ERR("Unknow write type : %d", type); + return BT_STATUS_FAIL; + } + + return BT_STATUS_SUCCESS; +} + +static bt_status_t _hal_write_characteristic_value(int conn_id, btgatt_srvc_id_t *srvc_id, + btgatt_gatt_id_t *char_id, int write_type, int length, int auth_req, char* value) +{ + GVariant *val, *options; + GVariantBuilder *builder1; + GVariantBuilder *builder2; + GDBusConnection *g_conn; + guint16 offset = 0; + int i = 0; + hal_gatt_resp_data_t *resp_data; + hal_gattc_service_t *gattc_service = NULL; + hal_gattc_server_info_t * conn_info = NULL; + hal_gattc_char_t *gattc_char = NULL; + char svc_uuid_str[BT_HAL_UUID_STRING_LEN]; + char char_uuid_str[BT_HAL_UUID_STRING_LEN]; + char* char_handle = NULL; + hal_gatt_property_e write_prop = HAL_GATT_PROPERTY_WRITE; + int ret = BT_STATUS_SUCCESS; + + DBG("+"); + + ret = __hal_get_write_prop(write_type, &write_prop); + if (BT_STATUS_FAIL == ret) { + DBG("received invalid write type:[%d] ", write_type); + return BT_STATUS_FAIL; + } + + /* get the connection info */ + conn_info = __bt_find_gatt_conn_info_from_conn_id(conn_id); + if (NULL == conn_info) { + DBG("Failed to get the conn_info for conn_id[%d]", conn_id); + return BT_STATUS_FAIL; + } + + /* find service */ + gattc_service = _gattc_find_service_from_uuid(conn_info, &srvc_id->id.uuid); + if (NULL == gattc_service) { + DBG("Failed to get the gatt service"); + return BT_STATUS_FAIL; + } + + DBG("service path [%s]", gattc_service->svc_path); + _bt_hal_convert_uuid_type_to_string(svc_uuid_str, gattc_service->svc_uuid.uu); + DBG("service uuid [%s]", svc_uuid_str); + + /* find characteristic */ + gattc_char = _gattc_find_char_from_uuid(gattc_service, &char_id->uuid); + if (NULL == gattc_char) { + DBG("Failed to get the gatt char"); + return BT_STATUS_FAIL; + } + + DBG("char path [%s]", gattc_char->chr_path); + _bt_hal_convert_uuid_type_to_string(char_uuid_str, gattc_char->chr_uuid.uu); + DBG("char uuid [%s]", char_uuid_str); + + g_conn = _bt_hal_get_system_gconn(); + if (NULL == g_conn) { + ERR("_bt_gdbus_get_system_gconn returned NULL"); + return BT_STATUS_FAIL; + } + + resp_data = g_malloc0(sizeof(hal_gatt_resp_data_t)); + if (NULL == resp_data) { + ERR("failed to get the memory"); + return BT_STATUS_FAIL; + } + + resp_data->conn_id = conn_id; + memcpy(&resp_data->srvc_id, srvc_id, sizeof(btgatt_srvc_id_t)); + memcpy(&resp_data->char_id, char_id, sizeof(btgatt_gatt_id_t)); + + char_handle = gattc_char->chr_path; + + builder1 = g_variant_builder_new(G_VARIANT_TYPE("ay")); + + for (i = 0; i < length; i++) + g_variant_builder_add(builder1, "y", value[i]); + + val = g_variant_new("ay", builder1); + + builder2 = g_variant_builder_new(G_VARIANT_TYPE("a{sv}")); + /*offset*/ + g_variant_builder_add(builder2, "{sv}", "offset", + g_variant_new_uint16(offset)); + + options = g_variant_new("a{sv}", builder2); + + g_dbus_connection_call(g_conn, BT_HAL_BLUEZ_NAME, char_handle, BT_HAL_GATT_CHAR_INTERFACE, + "WriteValuebyType", + g_variant_new("(y@ay@a{sv})", write_prop, val, options), + NULL, + G_DBUS_CALL_FLAGS_NONE, -1, NULL, + (GAsyncReadyCallback)__hal_bluetooth_internal_write_cb, + (gpointer)resp_data); + + g_variant_builder_unref(builder1); + g_variant_builder_unref(builder2); + + return BT_STATUS_SUCCESS; +} + +/** Write a remote characteristic */ +bt_status_t btif_write_characteristic(int conn_id, + btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, + int write_type, int len, int auth_req, + char* p_value) +{ + CHECK_BTGATT_INIT(); + + DBG("+"); + + DBG("svc isntance id:[%d]", srvc_id->id.inst_id); + return _hal_write_characteristic_value(conn_id, srvc_id, char_id, write_type, + len, auth_req, p_value); +} + +static void __hal_send_desc_read_event(hal_gatt_resp_data_t *resp_data, int result, uint8_t *value, int len) +{ + struct hal_ev_gatt_client_read_data ev; + + if (!event_cb) { + ERR("gatt client callback not registered"); + return; + } + + DBG("sending gatt client desc read conn_id[%d] status[%d]", resp_data->conn_id, result); + + memset(&ev, 0, sizeof(ev)); + ev.conn_id = resp_data->conn_id; + ev.inst_id = resp_data->srvc_id.id.inst_id; + ev.is_primary = resp_data->srvc_id.is_primary; + ev.status = result; + + memcpy(ev.svc_uuid, resp_data->srvc_id.id.uuid.uu, sizeof(ev.svc_uuid)); + memcpy(ev.char_uuid, resp_data->char_id.uuid.uu, sizeof(ev.char_uuid)); + memcpy(ev.desc_uuid, resp_data->desc_id.uuid.uu, sizeof(ev.char_uuid)); + + ev.len = len; + if (len > 0) { + DBG("building the desc read value [%d]", len); + memcpy(ev.value, value, len); + } + + DBG("sending the gatt client read descriptor event"); + + event_cb(HAL_EV_GATT_CLIENT_READ_DESC, (void *)&ev, sizeof(ev)); +} + +static void __hal_internal_read_desc_cb(GObject *source_object, + GAsyncResult *res, gpointer user_data) +{ + GError *error = NULL; + GDBusConnection *system_gconn = NULL; + GVariant *value; + GVariantIter *iter; + GByteArray *gp_byte_array = NULL; + guint8 g_byte; + hal_gatt_resp_data_t *resp_data = user_data; + int result = BT_STATUS_SUCCESS; + int i; + + DBG("+"); + + system_gconn = _bt_hal_get_system_gconn(); + value = g_dbus_connection_call_finish(system_gconn, res, &error); + + if (error) { + ERR("Read descriptor dbus failed Error:", error->message); + + //send failed event + result = BT_STATUS_FAIL; + __hal_send_desc_read_event(resp_data, result, NULL, 0); + g_clear_error(&error); + g_free(resp_data); + return; + } + + gp_byte_array = g_byte_array_new(); + g_variant_get(value, "(ay)", &iter); + + while (g_variant_iter_loop(iter, "y", &g_byte)) + g_byte_array_append(gp_byte_array, &g_byte, 1); + + //print the value + DBG("value is"); + for (i = 0; i < gp_byte_array->len; i++) + DBG("%02x", gp_byte_array->data[i]); + + //send value event + __hal_send_desc_read_event(resp_data, result, gp_byte_array->data, gp_byte_array->len); + + g_free(resp_data); + + g_byte_array_free(gp_byte_array, TRUE); + g_variant_iter_free(iter); + g_variant_unref(value); + + DBG("-"); +} + +static bt_status_t _hal_read_descriptor_value(int conn_id, btgatt_srvc_id_t *srvc_id, + btgatt_gatt_id_t *char_id, btgatt_gatt_id_t *desc_id, int auth_req) +{ + GDBusConnection *g_conn; + hal_gatt_resp_data_t *resp_data; + hal_gattc_service_t *gattc_service = NULL; + GVariantBuilder *builder = NULL; + guint16 offset = 0; + hal_gattc_server_info_t * conn_info = NULL; + hal_gattc_char_t *gattc_char = NULL; + char svc_uuid_str[BT_HAL_UUID_STRING_LEN]; + char char_uuid_str[BT_HAL_UUID_STRING_LEN]; + char desc_uuid_str[BT_HAL_UUID_STRING_LEN]; + char* desc_handle = NULL; + + hal_gattc_desc_t *gattc_desc = NULL; + + DBG("+"); + + /* get the connection info */ + conn_info = __bt_find_gatt_conn_info_from_conn_id(conn_id); + if (NULL == conn_info) { + DBG("Failed to get the conn_info for conn_id[%d]", conn_id); + return BT_STATUS_FAIL; + } + + /* find service */ + gattc_service = _gattc_find_service_from_uuid(conn_info, &srvc_id->id.uuid); + if (NULL == gattc_service) { + DBG("Failed to get the gatt service"); + return BT_STATUS_FAIL; + } + + DBG("service path [%s]", gattc_service->svc_path); + _bt_hal_convert_uuid_type_to_string(svc_uuid_str, gattc_service->svc_uuid.uu); + DBG("service uuid [%s]", svc_uuid_str); + + /* find characteristic */ + gattc_char = _gattc_find_char_from_uuid(gattc_service, &char_id->uuid); + if (NULL == gattc_char) { + DBG("Failed to get the gatt char"); + return BT_STATUS_FAIL; + } + + DBG("char path [%s]", gattc_char->chr_path); + _bt_hal_convert_uuid_type_to_string(char_uuid_str, gattc_char->chr_uuid.uu); + DBG("char uuid [%s]", char_uuid_str); + + /* find descriptor */ + gattc_desc = _gattc_find_desc_from_uuid(gattc_char, &desc_id->uuid); + if (NULL == gattc_desc) { + DBG("Failed to get the gatt desc"); + return BT_STATUS_FAIL; + } + + DBG("desc path [%s]", gattc_desc->desc_path); + _bt_hal_convert_uuid_type_to_string(desc_uuid_str, gattc_desc->desc_uuid.uu); + DBG("desc uuid [%s]", desc_uuid_str); + + g_conn = _bt_hal_get_system_gconn(); + if (NULL == g_conn) { + ERR("_bt_gdbus_get_system_gconn returned NULL"); + return BT_STATUS_FAIL; + } + + resp_data = g_malloc0(sizeof(hal_gatt_resp_data_t)); + if (NULL == resp_data) { + ERR("failed to get the memory"); + return BT_STATUS_FAIL; + } + + resp_data->conn_id = conn_id; + memcpy(&resp_data->srvc_id, srvc_id, sizeof(btgatt_srvc_id_t)); + memcpy(&resp_data->char_id, char_id, sizeof(btgatt_gatt_id_t)); + memcpy(&resp_data->desc_id, desc_id, sizeof(btgatt_gatt_id_t)); + + builder = g_variant_builder_new(G_VARIANT_TYPE("a{sv}")); + + /*offset*/ + g_variant_builder_add(builder, "{sv}", "offset", + g_variant_new("q", offset)); + + desc_handle = gattc_desc->desc_path; + + DBG("calling desc read value"); + + g_dbus_connection_call(g_conn, BT_HAL_BLUEZ_NAME, desc_handle, BT_HAL_GATT_DESC_INTERFACE, + "ReadValue", g_variant_new("(a{sv})", builder), + G_VARIANT_TYPE("(ay)"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, + (GAsyncReadyCallback)__hal_internal_read_desc_cb, + (gpointer)resp_data); + g_variant_builder_unref(builder); + + return BT_STATUS_SUCCESS; } -/** - * Enumerate descriptors for a given characteristic. - * Set start_descr_id to NULL to get the first descriptor. - */ -bt_status_t get_descriptor(int conn_id, +/** Read the descriptor for a given characteristic */ +bt_status_t btif_read_descriptor(int conn_id, btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, - btgatt_gatt_id_t *start_descr_id) + btgatt_gatt_id_t *descr_id, int auth_req) { CHECK_BTGATT_INIT(); - return BT_STATUS_UNSUPPORTED; + + return _hal_read_descriptor_value(conn_id, srvc_id, char_id, descr_id, auth_req); } -/** Read a characteristic on a remote device */ -bt_status_t read_characteristic(int conn_id, - btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, - int auth_req) +static void __hal_send_desc_write_event(hal_gatt_resp_data_t *resp_data, int result) { - CHECK_BTGATT_INIT(); - return BT_STATUS_UNSUPPORTED; + struct hal_ev_gatt_client_write_result ev; + + if (!event_cb) { + ERR("gatt client callback not registered"); + return; + } + + DBG("sending gatt client charac write conn_id[%d] status[%d]", resp_data->conn_id, result); + + memset(&ev, 0, sizeof(ev)); + ev.conn_id = resp_data->conn_id; + ev.inst_id = resp_data->srvc_id.id.inst_id; + ev.is_primary = resp_data->srvc_id.is_primary; + ev.status = result; + + memcpy(ev.svc_uuid, resp_data->srvc_id.id.uuid.uu, sizeof(ev.svc_uuid)); + memcpy(ev.char_uuid, resp_data->char_id.uuid.uu, sizeof(ev.char_uuid)); + memcpy(ev.desc_uuid, resp_data->desc_id.uuid.uu, sizeof(ev.desc_uuid)); + + DBG("sending the gatt client write charac event"); + + event_cb(HAL_EV_GATT_CLIENT_WRITE_DESC, (void *)&ev, sizeof(ev)); } -/** Write a remote characteristic */ -bt_status_t write_characteristic(int conn_id, - btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, - int write_type, int len, int auth_req, - char* p_value) +static void __hal_bluetooth_internal_desc_write_cb(GObject *source_object, + GAsyncResult *res, gpointer user_data) { - CHECK_BTGATT_INIT(); - return BT_STATUS_UNSUPPORTED; + GError *error = NULL; + GDBusConnection *system_gconn = NULL; + GVariant *value; + hal_gatt_resp_data_t *resp_data = user_data; + int result = BT_STATUS_SUCCESS; + + DBG("+"); + + system_gconn = _bt_hal_get_system_gconn(); + value = g_dbus_connection_call_finish(system_gconn, res, &error); + + if (error) { + ERR("write descriptor dbus failed Error:", error->message); + + //send failed event + result = BT_STATUS_FAIL; + __hal_send_desc_write_event(resp_data, result); + g_clear_error(&error); + g_free(resp_data); + return; + } + + //send write value event + __hal_send_desc_write_event(resp_data, result); + + g_free(resp_data); + g_variant_unref(value); + + DBG("-"); } -/** Read the descriptor for a given characteristic */ -bt_status_t read_descriptor(int conn_id, - btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, - btgatt_gatt_id_t *descr_id, int auth_req) +static bt_status_t _hal_write_descriptor_value(int conn_id, btgatt_srvc_id_t *srvc_id, + btgatt_gatt_id_t *char_id, btgatt_gatt_id_t *descr_id, + int write_type, int length, int auth_req, char* value) { - CHECK_BTGATT_INIT(); - return BT_STATUS_UNSUPPORTED; + GVariant *val, *options; + GVariantBuilder *builder1; + GVariantBuilder *builder2; + GDBusConnection *g_conn; + guint16 offset = 0; + int i = 0; + hal_gatt_resp_data_t *resp_data; + hal_gattc_service_t *gattc_service = NULL; + hal_gattc_server_info_t * conn_info = NULL; + hal_gattc_char_t *gattc_char = NULL; + hal_gattc_desc_t *gattc_desc = NULL; + char svc_uuid_str[BT_HAL_UUID_STRING_LEN]; + char char_uuid_str[BT_HAL_UUID_STRING_LEN]; + char desc_uuid_str[BT_HAL_UUID_STRING_LEN]; + char* desc_handle = NULL; + hal_gatt_property_e write_prop = HAL_GATT_PROPERTY_WRITE; + int ret = BT_STATUS_SUCCESS; + + DBG("+"); + + ret = __hal_get_write_prop(write_type, &write_prop); + if (BT_STATUS_FAIL == ret) { + DBG("received invalid write type:[%d] ", write_type); + return BT_STATUS_FAIL; + } + + /* get the connection info */ + conn_info = __bt_find_gatt_conn_info_from_conn_id(conn_id); + if (NULL == conn_info) { + DBG("Failed to get the conn_info for conn_id[%d]", conn_id); + return BT_STATUS_FAIL; + } + + /* find service */ + gattc_service = _gattc_find_service_from_uuid(conn_info, &srvc_id->id.uuid); + if (NULL == gattc_service) { + DBG("Failed to get the gatt service"); + return BT_STATUS_FAIL; + } + + DBG("service path [%s]", gattc_service->svc_path); + _bt_hal_convert_uuid_type_to_string(svc_uuid_str, gattc_service->svc_uuid.uu); + DBG("service uuid [%s]", svc_uuid_str); + + /* find characteristic */ + gattc_char = _gattc_find_char_from_uuid(gattc_service, &char_id->uuid); + if (NULL == gattc_char) { + DBG("Failed to get the gatt char"); + return BT_STATUS_FAIL; + } + + DBG("char path [%s]", gattc_char->chr_path); + _bt_hal_convert_uuid_type_to_string(char_uuid_str, gattc_char->chr_uuid.uu); + DBG("char uuid [%s]", char_uuid_str); + + /* find descriptor */ + gattc_desc = _gattc_find_desc_from_uuid(gattc_char, &descr_id->uuid); + if (NULL == gattc_desc) { + DBG("Failed to get the gatt char"); + return BT_STATUS_FAIL; + } + + DBG("desc path [%s]", gattc_desc->desc_path); + _bt_hal_convert_uuid_type_to_string(desc_uuid_str, gattc_desc->desc_uuid.uu); + DBG("char uuid [%s]", desc_uuid_str); + + g_conn = _bt_hal_get_system_gconn(); + if (NULL == g_conn) { + ERR("_bt_gdbus_get_system_gconn returned NULL"); + return BT_STATUS_FAIL; + } + + resp_data = g_malloc0(sizeof(hal_gatt_resp_data_t)); + if (NULL == resp_data) { + ERR("failed to get the memory"); + return BT_STATUS_FAIL; + } + + resp_data->conn_id = conn_id; + memcpy(&resp_data->srvc_id, srvc_id, sizeof(btgatt_srvc_id_t)); + memcpy(&resp_data->char_id, char_id, sizeof(btgatt_gatt_id_t)); + memcpy(&resp_data->desc_id, descr_id, sizeof(btgatt_gatt_id_t)); + + desc_handle = gattc_desc->desc_path; + + builder1 = g_variant_builder_new(G_VARIANT_TYPE("ay")); + + for (i = 0; i < length; i++) + g_variant_builder_add(builder1, "y", value[i]); + + val = g_variant_new("ay", builder1); + + builder2 = g_variant_builder_new(G_VARIANT_TYPE("a{sv}")); + /*offset*/ + g_variant_builder_add(builder2, "{sv}", "offset", + g_variant_new_uint16(offset)); + + options = g_variant_new("a{sv}", builder2); + + g_dbus_connection_call(g_conn, BT_HAL_BLUEZ_NAME, desc_handle, BT_HAL_GATT_DESC_INTERFACE, + "WriteValue", + g_variant_new("(@ay@a{sv})", val, options), + NULL, + G_DBUS_CALL_FLAGS_NONE, -1, NULL, + (GAsyncReadyCallback)__hal_bluetooth_internal_desc_write_cb, + (gpointer)resp_data); + + g_variant_builder_unref(builder1); + g_variant_builder_unref(builder2); + + return BT_STATUS_SUCCESS; } + /** Write a remote descriptor for a given characteristic */ -bt_status_t write_descriptor(int conn_id, +bt_status_t btif_write_descriptor(int conn_id, btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, btgatt_gatt_id_t *descr_id, int write_type, int len, int auth_req, char* p_value) { CHECK_BTGATT_INIT(); - return BT_STATUS_UNSUPPORTED; + + return _hal_write_descriptor_value(conn_id, srvc_id, char_id, descr_id, write_type, + len, auth_req, p_value); } /** Execute a prepared write operation */ @@ -296,11 +2119,79 @@ int get_device_type(const bt_bdaddr_t *bd_addr) return BT_STATUS_UNSUPPORTED; } +static bt_status_t __hal_update_conn_parameter(bt_bdaddr_t *bd_addr, + int min_int, int max_int, int latency, int timeout) +{ + gchar *device_path = NULL; + GError *error = NULL; + GDBusProxy *device_proxy = NULL; + GDBusConnection *conn; + GVariant *reply; + int ret = BT_STATUS_SUCCESS; + char device_address[BT_HAL_ADDRESS_STRING_SIZE] = { 0 }; + + INFO("Min interval: %d, Max interval: %d, Latency: %d, Supervision timeout: %d", + min_int, max_int, latency, timeout); + + conn = _bt_hal_get_system_gconn(); + if (conn == NULL) { + ERR("conn NULL"); + return BT_STATUS_FAIL; + } + + _bt_hal_convert_addr_type_to_string(device_address, + (unsigned char *)bd_addr->address); + device_path = _bt_hal_get_device_object_path(device_address); + + if (device_path == NULL) { + ERR("device_path NULL : [%s]", device_address); + return BT_STATUS_FAIL; + } + + device_proxy = g_dbus_proxy_new_sync(conn, G_DBUS_PROXY_FLAGS_NONE, + NULL, BT_HAL_BLUEZ_NAME, + device_path, BT_HAL_DEVICE_INTERFACE, NULL, NULL); + + g_free(device_path); + if (NULL == device_proxy) { + ERR("device_proxy returned NULL"); + return BT_STATUS_FAIL; + } + + INFO("### LeConnUpdate"); + reply = g_dbus_proxy_call_sync(device_proxy, "LeConnUpdate", + g_variant_new("(uuuu)", min_int, max_int, latency, timeout), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + + g_object_unref(device_proxy); + if (reply == NULL) { + if (error) { + ERR("Error %s[%s]", error->message, device_address); + if (g_strrstr(error->message, "In Progress")) + ret = BT_STATUS_SUCCESS; + else + ret = BT_STATUS_FAIL; + g_error_free(error); + return ret; + } + } + g_variant_unref(reply); + + INFO("LE Connection parameter Updated"); + return ret; +} + /** Request a connection parameter update */ -bt_status_t conn_parameter_update(bt_bdaddr_t *bd, int min_int, int max_int, int latency, int timeout) +bt_status_t btif_gattc_conn_parameter_update(bt_bdaddr_t *bd, int min_int, int max_int, int latency, int timeout) { CHECK_BTGATT_INIT(); - return BT_STATUS_UNSUPPORTED; + + DBG("+"); + + return __hal_update_conn_parameter(bd, min_int, max_int, latency, timeout); } /** Test mode interface */ @@ -327,7 +2218,6 @@ bt_status_t scan_filter_param_setup(int client_if, int action, int filt_index, i return BT_STATUS_UNSUPPORTED; } - /** Configure a scan filter condition */ bt_status_t scan_filter_add_remove(int client_if, int action, int filt_type, int filt_index, int company_id, @@ -406,27 +2296,27 @@ bt_status_t batchscan_read_reports(int client_if, int scan_mode) } const btgatt_client_interface_t btgatt_client_interface = { - register_client, - unregister_client, + btif_gattc_register_client, + btif_gattc_unregister_client, scan, - connect, - disconnect, + btif_gattc_client_connect, + btif_gattc_client_disconnect, refresh, - search_service, + btif_gattc_client_search_service, get_included_service, - get_characteristic, - get_descriptor, - read_characteristic, - write_characteristic, - read_descriptor, - write_descriptor, + btif_gattc_get_characteristic, + btif_gattc_get_descriptor, + btif_read_characteristic, + btif_write_characteristic, + btif_read_descriptor, + btif_write_descriptor, execute_write, register_for_notification, deregister_for_notification, read_remote_rssi, ota_fw_update, get_device_type, - conn_parameter_update, + btif_gattc_conn_parameter_update, test_command, configure_mtu, scan_filter_param_setup, @@ -439,3 +2329,475 @@ const btgatt_client_interface_t btgatt_client_interface = { batchscan_dis_batch_scan, batchscan_read_reports }; + +static hal_gattc_server_info_t *__bt_find_gatt_conn_info(bt_bdaddr_t *serv_addr) +{ + DBG("+"); + + GSList *l; + hal_gattc_server_info_t *info = NULL; + + for (l = hal_gattc_server_info_list; l != NULL; l = g_slist_next(l)) { + info = (hal_gattc_server_info_t*)l->data; + if (info == NULL) + continue; + + if (!memcmp(&info->bd_addr, serv_addr, sizeof(bt_bdaddr_t))) { + INFO("GATT connection found addr"); + return info; + } + } + return NULL; +} + +static hal_gattc_client_info_t *__bt_find_gatt_client_info(bt_bdaddr_t *serv_addr) +{ + DBG("+"); + + GSList *l; + hal_gattc_client_info_t *info = NULL; + + for (l = hal_gattc_client_info_list; l != NULL; l = g_slist_next(l)) { + info = (hal_gattc_client_info_t*)l->data; + if (info == NULL) + continue; + + if (!memcmp(&info->bd_addr, serv_addr, sizeof(bt_bdaddr_t))) { + INFO("GATT client info found addr"); + return info; + } + } + return NULL; +} + +static hal_gattc_client_info_t *__bt_find_gatt_client_info_from_conn_id(int conn_id) +{ + DBG("+"); + + GSList *l; + hal_gattc_client_info_t *info = NULL; + + for (l = hal_gattc_client_info_list; l != NULL; l = g_slist_next(l)) { + info = (hal_gattc_client_info_t*)l->data; + if (info == NULL) + continue; + + if (info->conn_id == conn_id) { + INFO("GATT client info found for conn_id [%d]", conn_id); + return info; + } + } + return NULL; +} + +static hal_gattc_server_info_t *__bt_find_gatt_conn_info_from_conn_id(int conn_id) +{ + DBG("+"); + + GSList *l; + hal_gattc_server_info_t *info = NULL; + hal_gattc_client_info_t *gattc_client = NULL; + + gattc_client = __bt_find_gatt_client_info_from_conn_id(conn_id); + if (gattc_client == NULL) { + INFO("GATT client conn info not found"); + return NULL; + } + + for (l = hal_gattc_server_info_list; l != NULL; l = g_slist_next(l)) { + info = (hal_gattc_server_info_t*)l->data; + if (info == NULL) + continue; + + if ((info->inst_id == gattc_client->inst_id) && + !memcmp(&info->bd_addr, &gattc_client->bd_addr, sizeof(bt_bdaddr_t))) { + INFO("GATT connection found for conn_id [%d]", conn_id); + return info; + } + } + return NULL; +} + +static bt_status_t __bt_connect_le_device_internal(int client_if, const bt_bdaddr_t *bd_addr, + gboolean auto_connect) +{ + char device_address[BT_HAL_ADDRESS_STRING_SIZE] = { 0 }; + gchar *device_path = NULL; + GDBusProxy *device_proxy = NULL; + GDBusConnection *conn; + int ret = BT_STATUS_SUCCESS; + hal_gattc_client_info_t *gattc_data; + + DBG("+"); + + if (NULL == bd_addr) { + ERR("bd_addr is NULL"); + return BT_STATUS_PARM_INVALID; + } + + conn = _bt_hal_get_system_gconn(); + if (NULL == conn) { + ERR("_bt_gdbus_get_system_gconn returned NULL"); + return BT_STATUS_FAIL; + } + + _bt_hal_convert_addr_type_to_string(device_address, + (unsigned char *)bd_addr->address); + device_path = _bt_hal_get_device_object_path(device_address); + if (device_path == NULL) { + ERR("device_path NULL : [%s]", device_address); + ret = BT_STATUS_FAIL; + return ret; + } + ERR("device_path:%s", device_path); + + device_proxy = g_dbus_proxy_new_sync(conn, G_DBUS_PROXY_FLAGS_NONE, + NULL, BT_HAL_BLUEZ_NAME, + device_path, BT_HAL_DEVICE_INTERFACE, NULL, NULL); + g_free(device_path); + if (NULL == device_proxy) { + ERR("device_proxy returned NULL"); + return BT_STATUS_FAIL; + } + + gattc_data = g_malloc0(sizeof(hal_gattc_client_info_t)); + if (gattc_data == NULL) { + ERR("Unable to allocate memory"); + ret = BT_STATUS_NOMEM; + goto fail; + } + memcpy(gattc_data->bd_addr.address, bd_addr->address, + BT_HAL_ADDRESS_LENGTH_MAX); + + DBG("Connect LE [%s]", device_address); + + gattc_data->client_if = client_if; + + g_dbus_proxy_call(device_proxy, "ConnectLE", + g_variant_new("(b)", auto_connect), + G_DBUS_CALL_FLAGS_NONE, + BT_HAL_MAX_DBUS_TIMEOUT, + NULL, + (GAsyncReadyCallback)__le_connection_req_cb, gattc_data); + + return ret; + +fail: + if (device_proxy) + g_object_unref(device_proxy); + + g_free(gattc_data); + + return ret; +} + +static bt_status_t _bt_hold_current_advertising() +{ + int ret = BT_STATUS_FAIL; + gboolean is_advertising = FALSE; + DBG("+"); + + is_advertising = _bt_hal_is_advertising_in_slot(0); + if (is_advertising) { + DBG("+ Stop current advertising"); + + ret = _bt_hal_enable_advertising(0, FALSE, FALSE); + g_timeout_add(2000, __bt_hold_current_advertising_timeout_cb, NULL); + } + + return ret; +} + +static gboolean __bt_hold_current_advertising_timeout_cb(gpointer user_data) +{ + DBG("+ start current advertising"); + + _bt_hal_enable_advertising(0, TRUE, FALSE); + + return FALSE; +} + +static gboolean __bt_connect_le_timer_cb(gpointer user_data) +{ + DBG("Try to initiate pending LE connection"); + + pending_le_conn_timer_id = 0; + + __bt_connect_le_device_internal(pending_le_conn_info->client_if, + &pending_le_conn_info->bd_addr, + pending_le_conn_info->auto_connect); + + g_free(pending_le_conn_info); + pending_le_conn_info = NULL; + + return FALSE; +} + +static int __hal_generate_conn_id() +{ + return ++bt_conn_id; +} + +static int __hal_generate_server_instance_id() +{ + return ++bt_inst_id; +} + +static void __le_connection_req_cb(GDBusProxy *proxy, GAsyncResult *res, + gpointer user_data) +{ + GError *g_error = NULL; + GVariant *reply = NULL; + hal_gattc_client_info_t *gattc_data = user_data; + int result = BT_STATUS_SUCCESS; + struct hal_ev_gatt_client_connected ev; + hal_gattc_server_info_t *gatt_conn_info = NULL; + + DBG("+"); + + reply = g_dbus_proxy_call_finish(proxy, res, &g_error); + g_object_unref(proxy); + if (reply == NULL) { + ERR("Connect LE Dbus Call Error"); + if (g_error) { + ERR("Error: %s\n", g_error->message); + g_clear_error(&g_error); + } + result = BT_STATUS_FAIL; + } + g_variant_unref(reply); + + if (NULL == gattc_data) { + ERR("server_data is NULL"); + return; + } + + /*send fail event*/ + if (result == BT_STATUS_FAIL) { + memset(&ev, 0, sizeof(ev)); + ev.conn_id = -1; + ev.status = result; + ev.client_if = gattc_data->client_if; + memcpy(ev.bdaddr, gattc_data->bd_addr.address, + BT_HAL_ADDRESS_LENGTH_MAX); + + if (!event_cb) { + ERR("gatt client callback not registered"); + } else { + DBG("sending gatt client connected event"); + event_cb(HAL_EV_GATT_CLIENT_CONNECTED, (void *)&ev, sizeof(ev)); + } + + goto fail; + } + + DBG("adding the server conn info in list"); + gattc_data->conn_id = __hal_generate_conn_id() ; + gattc_data->inst_id = __hal_generate_server_instance_id(); + + hal_gattc_client_info_list = g_slist_append(hal_gattc_client_info_list, gattc_data); + + /*add gatt server connection info*/ + gatt_conn_info = g_malloc0(sizeof(hal_gattc_server_info_t)); + if (gatt_conn_info == NULL) { + ERR("Failed to allocate memory"); + goto fail; + } + + memcpy(gatt_conn_info->bd_addr.address, gattc_data->bd_addr.address, BT_HAL_ADDRESS_LENGTH_MAX); + gatt_conn_info->inst_id = gattc_data->inst_id; + hal_gattc_server_info_list = g_slist_append(hal_gattc_server_info_list, gatt_conn_info); + + DBG("-"); + return; + +fail: + /*remove conn_info*/ + if (gattc_data) + g_free(gattc_data); +} + +void __hal_gattc_free_svc_info(hal_gattc_service_t *svc_info) +{ + g_free(svc_info->svc_path); + g_free(svc_info); +} + +void __hal_gattc_free_char_info(hal_gattc_char_t *char_info) +{ + g_free(char_info->chr_path); + g_free(char_info); +} + +void __hal_gattc_free_desc_info(hal_gattc_desc_t *desc_info) +{ + g_free(desc_info->desc_path); + g_free(desc_info); +} + +void __hal_clean_gattc_server_info(hal_gattc_server_info_t *conn_info) +{ + GSList *l; + GSList *m; + GSList *k; + hal_gattc_service_t *svc_info = NULL; + hal_gattc_char_t *char_info = NULL; + hal_gattc_desc_t *desc_info = NULL; + + DBG("+"); + + for (l = conn_info->gatt_list_services; l != NULL;) { + svc_info = (hal_gattc_service_t*)l->data; + if (svc_info == NULL) + continue; + l = g_slist_next(l); + + for (m = svc_info->gatt_list_chars; m != NULL; ) { + char_info = (hal_gattc_char_t*)m->data; + if (char_info == NULL) + continue; + m = g_slist_next(m); + + for (k = char_info->gatt_list_descs; k != NULL; ) { + desc_info = (hal_gattc_desc_t*)k->data; + if (desc_info == NULL) + continue; + k = g_slist_next(k); + + /*remove desc element*/ + char_info->gatt_list_descs = g_slist_remove(char_info->gatt_list_descs, desc_info); + __hal_gattc_free_desc_info(desc_info); + } + + /*remove desc list*/ + g_slist_free(char_info->gatt_list_descs); + char_info->gatt_list_descs = NULL; + + /*remove char element*/ + svc_info->gatt_list_chars = g_slist_remove(svc_info->gatt_list_chars, char_info); + __hal_gattc_free_char_info(char_info); + } + + /*remove char list*/ + g_slist_free(svc_info->gatt_list_chars); + svc_info->gatt_list_chars = NULL; + + /*remove svc element*/ + conn_info->gatt_list_services = g_slist_remove(conn_info->gatt_list_services, svc_info); + __hal_gattc_free_svc_info(svc_info); + } + + /*remove svc list */ + g_slist_free(conn_info->gatt_list_services); + conn_info->gatt_list_services = NULL; + + /*remove conn info*/ + g_free(conn_info); +} + +void _bt_hal_handle_gattc_connected_event(char* address, gboolean gatt_connected) +{ + int result = BT_STATUS_SUCCESS; + struct hal_ev_gatt_client_connected ev; + hal_gattc_server_info_t *conn_info = NULL; + bt_bdaddr_t bd_addr; + int event; + hal_gattc_client_info_t *gattc_client = NULL; + int inst_id = -1; + + + DBG("+ connected device address [%s]", address); + + event = gatt_connected ? HAL_EV_GATT_CLIENT_CONNECTED : + HAL_EV_GATT_CLIENT_DISCONNECTED; + + _bt_hal_convert_addr_string_to_type(bd_addr.address, address); + /* find the gatt client info */ + gattc_client = __bt_find_gatt_client_info(&bd_addr); + if (NULL == gattc_client) { + ERR("Fail to get gatt client info"); + return; + } + + //send event + memset(&ev, 0, sizeof(ev)); + ev.conn_id = gattc_client->conn_id; + ev.status = result; + ev.client_if = gattc_client->client_if; + memcpy(ev.bdaddr, gattc_client->bd_addr.address, + BT_HAL_ADDRESS_LENGTH_MAX); + + if (!event_cb) { + ERR("gatt client callback not registered"); + } else { + DBG("sending gatt client connected status event"); + event_cb(event, (void *)&ev, sizeof(ev)); + } + + if (!gatt_connected) { + inst_id = gattc_client->inst_id; + + /* remove the gatt client info from the client list also*/ + hal_gattc_client_info_list = g_slist_remove(hal_gattc_client_info_list, gattc_client); + g_free(gattc_client); + + //find the connected server info + conn_info = __bt_find_gatt_conn_info(&bd_addr); + if (NULL == conn_info) { + ERR("Fail to get gatt server info"); + return; + } + + if (inst_id != conn_info->inst_id) { + ERR("server instance is different"); + return; + } + + //remove gatt conn info from the server list + DBG("remove the server conn_info from list after gatt disconnection"); + hal_gattc_server_info_list = g_slist_remove(hal_gattc_server_info_list, conn_info); + __hal_clean_gattc_server_info(conn_info); + } + + DBG("-"); +} + +static void _bt_hal_send_search_service_result_event(int conn_id, int is_primary, + const char* uuid_str, int inst_id) +{ + struct hal_ev_gatt_client_search_result ev; + + if (!event_cb) { + ERR("gatt client callback not registered"); + return; + } + + DBG("sending gatt client search service result event conn_id[%d]", conn_id); + + memset(&ev, 0, sizeof(ev)); + ev.conn_id = conn_id; + ev.inst_id = inst_id; + ev.is_primary = is_primary; + _bt_hal_convert_uuid_string_to_type(ev.uuid, uuid_str); + + event_cb(HAL_EV_GATT_CLIENT_SEARCH_RESULT, (void *)&ev, sizeof(ev)); +} + +static void _bt_hal_send_search_service_complete_event(int conn_id, int status) +{ + struct hal_ev_gatt_client_search_complete ev; + + if (!event_cb) { + ERR("gatt client callback not registered"); + return; + } + + DBG("sending gatt client search service complete event conn_id[%d]", conn_id); + + + memset(&ev, 0, sizeof(ev)); + ev.conn_id = conn_id; + ev.status = status; + + event_cb(HAL_EV_GATT_CLIENT_SEARCH_COMPLETE, (void *)&ev, sizeof(ev)); +} diff --git a/bt-oal/bluez_hal/src/bt-hal-gatt-client.h b/bt-oal/bluez_hal/src/bt-hal-gatt-client.h index cb7f2f2..82e85f0 100644 --- a/bt-oal/bluez_hal/src/bt-hal-gatt-client.h +++ b/bt-oal/bluez_hal/src/bt-hal-gatt-client.h @@ -38,6 +38,8 @@ extern "C" { void _bt_hal_register_gatt_client_handler_cb(handle_stack_msg cb); void _bt_hal_unregister_gatt_client_handler_cb(void); +void _bt_hal_handle_gattc_connected_event(char* address, gboolean connected); + #ifdef TIZEN_BT_HAL int _bt_hal_gatt_client_get_le_scan_type(void); #endif diff --git a/bt-oal/bluez_hal/src/bt-hal-gatt-server.c b/bt-oal/bluez_hal/src/bt-hal-gatt-server.c index 97529fd..a3c3a18 100644 --- a/bt-oal/bluez_hal/src/bt-hal-gatt-server.c +++ b/bt-oal/bluez_hal/src/bt-hal-gatt-server.c @@ -234,8 +234,9 @@ static const gchar characteristics_introspection_xml[] = " " " " " " -" " +" " " " +" " " " " " " " @@ -1089,6 +1090,7 @@ static void __bt_gatt_char_method_call(GDBusConnection *connection, gboolean response_needed = FALSE; struct hal_ev_gatt_server_write_req ev; int char_hdl = -1; + int len; struct gatt_service_info *svc_info = NULL; struct gatt_req_info *req_info = NULL; @@ -1116,6 +1118,14 @@ static void __bt_gatt_char_method_call(GDBusConnection *connection, return; } + len = g_variant_get_size(var); + if (len > 0) { + char *data; + data = (char *)g_variant_get_data(var); + memcpy(ev.value, data, len); + ev.length = len; + } + if (response_needed) { /* Store requets information */ req_info = g_new0(struct gatt_req_info, 1); diff --git a/bt-oal/bluez_hal/src/bt-hal-gatt.c b/bt-oal/bluez_hal/src/bt-hal-gatt.c index 8b8df90..15683e3 100644 --- a/bt-oal/bluez_hal/src/bt-hal-gatt.c +++ b/bt-oal/bluez_hal/src/bt-hal-gatt.c @@ -42,6 +42,7 @@ #include "bt-hal-adapter-le.h" #include "bt-hal-gatt-server.h" #include "bt-hal-gatt-client.h" +#include "bt_gatt_types.h" /* Advertising report event types */ #define BT_LE_ADV_IND 0x00 @@ -51,6 +52,7 @@ #define BT_LE_ADV_SCAN_RSP 0x04 #define BT_HAL_ADV_DATA_MAX_SIZE 31 +#define BT_HAL_GATTC_READ_VALUE_TYPE_VALUE 0x0000 /* Attribute value itself */ typedef struct { char addr[18]; uint8_t addr_type; @@ -71,6 +73,16 @@ static GSList *adv_ind_list = NULL; /****************************************** Forward Declarations *************************************/ static void __bt_handle_gatt_client_registered(void *buf, uint16_t len); static void __bt_hal_handle_gatt_client_scan_result(void *buf, uint16_t len); +static void __bt_handle_gatt_client_connected(void *buf, uint16_t len); +static void __bt_handle_gatt_client_disconnected(void *buf, uint16_t len); +static void __bt_handle_gatt_client_search_result(void *buf, uint16_t len); +static void __bt_handle_gatt_client_search_complete(void *buf, uint16_t len); +static void __bt_handle_gatt_client_search_char_result(void *buf, uint16_t len); +static void __bt_handle_gatt_client_search_desc_result(void *buf, uint16_t len); +static void __bt_handle_gatt_client_read_charac(void *buf, uint16_t len); +static void __bt_handle_gatt_client_read_desc(void *buf, uint16_t len); +static void __bt_handle_gatt_client_write_char(void *buf, uint16_t len); +static void __bt_handle_gatt_client_write_desc(void *buf, uint16_t len); /*****************************************************************************************************/ static bool interface_ready(void) @@ -312,6 +324,46 @@ static void __bt_hal_gatt_events(int message, void *buf, uint16_t len) __bt_hal_handle_gatt_server_indicate_confirmed(buf, len); break; } + case HAL_EV_GATT_CLIENT_CONNECTED: { + __bt_handle_gatt_client_connected(buf, len); + break; + } + case HAL_EV_GATT_CLIENT_DISCONNECTED: { + __bt_handle_gatt_client_disconnected(buf, len); + break; + } + case HAL_EV_GATT_CLIENT_SEARCH_RESULT: { + __bt_handle_gatt_client_search_result(buf, len); + break; + } + case HAL_EV_GATT_CLIENT_SEARCH_COMPLETE: { + __bt_handle_gatt_client_search_complete(buf, len); + break; + } + case HAL_EV_GATT_CLIENT_CHARAC_SEARCH_RESULT: { + __bt_handle_gatt_client_search_char_result(buf, len); + break; + } + case HAL_EV_GATT_CLIENT_DESC_SEARCH_RESULT: { + __bt_handle_gatt_client_search_desc_result(buf, len); + break; + } + case HAL_EV_GATT_CLIENT_READ_CHARAC: { + __bt_handle_gatt_client_read_charac(buf, len); + break; + } + case HAL_EV_GATT_CLIENT_READ_DESC: { + __bt_handle_gatt_client_read_desc(buf, len); + break; + } + case HAL_EV_GATT_CLIENT_WRITE_CHARAC: { + __bt_handle_gatt_client_write_char(buf, len); + break; + } + case HAL_EV_GATT_CLIENT_WRITE_DESC: { + __bt_handle_gatt_client_write_desc(buf, len); + break; + } default: DBG("Event Currently not handled!!"); break; @@ -341,6 +393,182 @@ static void __bt_hal_send_le_scan_result_event(bt_hal_le_adv_info_t *adv_info) &bd_addr, adv_info->rssi, adv_info->data); } +static void __bt_handle_gatt_client_connected(void *buf, uint16_t len) +{ + struct hal_ev_gatt_client_connected *ev = buf; + bt_bdaddr_t bd_addr; + + memcpy(bd_addr.address, ev->bdaddr, 6); + if (bt_gatt_callbacks->client->open_cb) + bt_gatt_callbacks->client->open_cb(ev->conn_id, + ev->status, ev->client_if, &bd_addr); +} + +static void __bt_handle_gatt_client_disconnected(void *buf, uint16_t len) +{ + struct hal_ev_gatt_client_connected *ev = buf; + bt_bdaddr_t bd_addr; + + memcpy(bd_addr.address, ev->bdaddr, 6); + if (bt_gatt_callbacks->client->close_cb) + bt_gatt_callbacks->client->close_cb(ev->conn_id, + ev->status, ev->client_if, &bd_addr); +} + +static void __bt_handle_gatt_client_search_result(void *buf, uint16_t len) +{ + struct hal_ev_gatt_client_search_result *ev = buf; + btgatt_srvc_id_t gatt_srvc_id; + + gatt_srvc_id.is_primary = ev->is_primary; + gatt_srvc_id.id.inst_id = ev->inst_id; + memcpy(gatt_srvc_id.id.uuid.uu, ev->uuid, 16); + + if (bt_gatt_callbacks->client->search_result_cb) + bt_gatt_callbacks->client->search_result_cb(ev->conn_id, + &gatt_srvc_id); +} + +static void __bt_handle_gatt_client_search_complete(void *buf, uint16_t len) +{ + struct hal_ev_gatt_client_search_complete *ev = buf; + + if (bt_gatt_callbacks->client->search_complete_cb) + bt_gatt_callbacks->client->search_complete_cb(ev->conn_id, + ev->status); +} + +static void __bt_handle_gatt_client_search_char_result(void *buf, uint16_t len) +{ + struct hal_ev_gatt_client_char_search_result *ev = buf; + btgatt_srvc_id_t gatt_srvc_id; + btgatt_gatt_id_t gatt_char_id; + + gatt_srvc_id.is_primary = ev->is_primary; + gatt_srvc_id.id.inst_id = ev->inst_id; + memcpy(gatt_srvc_id.id.uuid.uu, ev->svc_uuid, 16); + + if (BT_STATUS_SUCCESS == ev->status) { + memcpy(gatt_char_id.uuid.uu, ev->char_uuid, 16); + gatt_char_id.inst_id = ev->inst_id; + } + + if (bt_gatt_callbacks->client->get_characteristic_cb) + bt_gatt_callbacks->client->get_characteristic_cb(ev->conn_id, + ev->status, &gatt_srvc_id, &gatt_char_id, ev->char_prop); +} + +static void __bt_handle_gatt_client_search_desc_result(void *buf, uint16_t len) +{ + struct hal_ev_gatt_client_desc_search_result *ev = buf; + btgatt_srvc_id_t gatt_srvc_id; + btgatt_gatt_id_t gatt_char_id; + btgatt_gatt_id_t gatt_desc_id; + + + gatt_srvc_id.is_primary = ev->is_primary; + gatt_srvc_id.id.inst_id = ev->inst_id; + memcpy(gatt_srvc_id.id.uuid.uu, ev->svc_uuid, 16); + + memcpy(gatt_char_id.uuid.uu, ev->char_uuid, 16); + gatt_char_id.inst_id = ev->inst_id; + + if (BT_STATUS_SUCCESS == ev->status) { + memcpy(gatt_desc_id.uuid.uu, ev->desc_uuid, 16); + gatt_desc_id.inst_id = ev->inst_id; + } + + if (bt_gatt_callbacks->client->get_descriptor_cb) + bt_gatt_callbacks->client->get_descriptor_cb(ev->conn_id, + ev->status, &gatt_srvc_id, &gatt_char_id, &gatt_desc_id); +} + +static void __bt_handle_gatt_client_read_charac(void *buf, uint16_t len) +{ + struct hal_ev_gatt_client_read_data *ev = buf; + btgatt_read_params_t char_read_parm; + + char_read_parm.srvc_id.is_primary = ev->is_primary; + char_read_parm.srvc_id.id.inst_id = ev->inst_id; + memcpy(char_read_parm.srvc_id.id.uuid.uu, ev->svc_uuid, 16); + + char_read_parm.char_id.inst_id = ev->inst_id; + memcpy(char_read_parm.char_id.uuid.uu, ev->char_uuid, 16); + + char_read_parm.value.len = ev->len; + if (ev->len > 0) { + memcpy(char_read_parm.value.value, ev->value, ev->len); + char_read_parm.value_type = BT_HAL_GATTC_READ_VALUE_TYPE_VALUE; + } + + if (bt_gatt_callbacks->client->read_characteristic_cb) + bt_gatt_callbacks->client->read_characteristic_cb(ev->conn_id, + ev->status, &char_read_parm); +} + +static void __bt_handle_gatt_client_read_desc(void *buf, uint16_t len) +{ + struct hal_ev_gatt_client_read_data *ev = buf; + btgatt_read_params_t desc_read_parm; + + desc_read_parm.srvc_id.is_primary = ev->is_primary; + desc_read_parm.srvc_id.id.inst_id = ev->inst_id; + memcpy(desc_read_parm.srvc_id.id.uuid.uu, ev->svc_uuid, 16); + + desc_read_parm.char_id.inst_id = ev->inst_id; + memcpy(desc_read_parm.char_id.uuid.uu, ev->char_uuid, 16); + + desc_read_parm.descr_id.inst_id = ev->inst_id; + memcpy(desc_read_parm.descr_id.uuid.uu, ev->desc_uuid, 16); + + desc_read_parm.value.len = ev->len; + if (ev->len > 0) { + memcpy(desc_read_parm.value.value, ev->value, ev->len); + desc_read_parm.value_type = BT_HAL_GATTC_READ_VALUE_TYPE_VALUE; + } + + if (bt_gatt_callbacks->client->read_descriptor_cb) + bt_gatt_callbacks->client->read_descriptor_cb(ev->conn_id, + ev->status, &desc_read_parm); +} + +static void __bt_handle_gatt_client_write_char(void *buf, uint16_t len) +{ + struct hal_ev_gatt_client_write_result *ev = buf; + btgatt_write_params_t char_write_parm; + + char_write_parm.srvc_id.is_primary = ev->is_primary; + char_write_parm.srvc_id.id.inst_id = ev->inst_id; + memcpy(char_write_parm.srvc_id.id.uuid.uu, ev->svc_uuid, 16); + + char_write_parm.char_id.inst_id = ev->inst_id; + memcpy(char_write_parm.char_id.uuid.uu, ev->char_uuid, 16); + + if (bt_gatt_callbacks->client->write_characteristic_cb) + bt_gatt_callbacks->client->write_characteristic_cb(ev->conn_id, + ev->status, &char_write_parm); +} + +static void __bt_handle_gatt_client_write_desc(void *buf, uint16_t len) +{ + struct hal_ev_gatt_client_write_result *ev = buf; + btgatt_write_params_t desc_write_parm; + + desc_write_parm.srvc_id.is_primary = ev->is_primary; + desc_write_parm.srvc_id.id.inst_id = ev->inst_id; + memcpy(desc_write_parm.srvc_id.id.uuid.uu, ev->svc_uuid, 16); + + desc_write_parm.char_id.inst_id = ev->inst_id; + memcpy(desc_write_parm.char_id.uuid.uu, ev->char_uuid, 16); + + desc_write_parm.descr_id.inst_id = ev->inst_id; + memcpy(desc_write_parm.descr_id.uuid.uu, ev->desc_uuid, 16); + + if (bt_gatt_callbacks->client->write_descriptor_cb) + bt_gatt_callbacks->client->write_descriptor_cb(ev->conn_id, + ev->status, &desc_write_parm); +} + static bt_hal_le_adv_info_t *__bt_hal_get_adv_ind_info(char *addr) { GSList *l; -- 2.7.4