From 0f08cb8c0d7f9dd20702618dab70f061b6a8e014 Mon Sep 17 00:00:00 2001 From: Gowtham Anandha Babu Date: Tue, 13 Jun 2017 16:03:28 +0530 Subject: [PATCH] [OTP] Expose Object Create API Change-Id: Ie344d4b176889b1acb99dfea234ece1c5b337621 Signed-off-by: Gowtham Anandha Babu --- include/bluetooth_internal.h | 9 +- include/bluetooth_type_internal.h | 8 ++ src/bluetooth-otp.c | 255 ++++++++++++++++++++++++++++++++++++-- test/bt_unit_test.c | 67 ++++++++++ test/bt_unit_test.h | 1 + 5 files changed, 328 insertions(+), 12 deletions(-) diff --git a/include/bluetooth_internal.h b/include/bluetooth_internal.h index d9a8e32..c6e73f6 100644 --- a/include/bluetooth_internal.h +++ b/include/bluetooth_internal.h @@ -4108,7 +4108,14 @@ int bt_otp_client_select_object(bt_otp_client_h otp_client, unsigned long long i */ int bt_otp_client_read_object_contents(bt_otp_client_h otp_client, bt_otp_client_read_object_complete_cb callback, void *user_data); - +/** + * @internal + * @brief OTP client API to create object on remote server + */ +int bt_otp_client_create_object(bt_otp_client_h otp_client, + const char *file_path, + bt_otp_client_object_create_cb callback, + void *user_data); /** * @} */ diff --git a/include/bluetooth_type_internal.h b/include/bluetooth_type_internal.h index 8438238..3319304 100644 --- a/include/bluetooth_type_internal.h +++ b/include/bluetooth_type_internal.h @@ -980,6 +980,14 @@ typedef void (*bt_otp_client_read_object_complete_cb) (int result, const char *remote_address, char *file_path, void *user_data); /** + * @internal + * @since_tizen 4.0 + * @brief OTP Client profile object create callback + */ +typedef void (*bt_otp_client_object_create_cb) + (int result, const char *remote_address, unsigned long long obj_id, void *user_data); + +/** * @} */ diff --git a/src/bluetooth-otp.c b/src/bluetooth-otp.c index b2755e1..26e0127 100644 --- a/src/bluetooth-otp.c +++ b/src/bluetooth-otp.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include "bluetooth.h" @@ -119,21 +120,30 @@ #define BT_OTP_FEATURE_LENGTH 8 #define BT_ADDR_LENGTH 18 -#define BT_OTC_CONNECTION_MAX_TIMEOUT 30000 /* Timeout for OTC Connection in msec */ +#define BT_OTC_CONNECTION_MAX_TIMEOUT 10000 /* Timeout for OTC Connection in msec */ #define BT_L2CAP_BUFFER_LEN 672 #define BT_OTP_IS_OACP_SUPPORTED(feature) feature & 0xffffffff00000000 #define BT_OTP_IS_OLCP_SUPPORTED(feature) feature & 0x00000000ffffffff #define BT_OTP_IS_OACP_READ_SUPPORTED(feature) feature & 0x0800000000000000 +#define BT_OTP_IS_OACP_CREATE_SUPPORTED(feature) feature & 0x8000000000000000 #define BT_OTP_IS_READ_PERMITTED(props) props & OBJECT_READ +/* OTP Object Type Custom UUIDs */ +/* In SIG Assigned numbers not available */ +#define UNSUPPORTED_OBJECT_TYPE_UUID "7fb0" +#define FIRMWARE_UUID "7fb1" +#define ROUTE_GPX_UUID "7fb2" +#define TRACK_GPX_UUID "7fb3" + typedef enum { BT_OTP_NO_OPERATION = 0, BT_OTP_OBJECT_DISCOVERY, BT_OTP_OBJECT_READ, BT_OTP_OBJECT_SELECT, + BT_OTP_OBJECT_CREATE, } bt_otp_api_info_e; typedef struct { @@ -193,7 +203,16 @@ typedef struct { FILE *fp; } bt_otp_client_read_op; +typedef struct { + char *filename; + char *type_uuid; + time_t first_created; + uint64_t id; + uint32_t size; +} bt_otp_client_create_op; + static bool is_otp_server_initialized = false; +bt_otp_client_create_op *oacp_create_op = NULL; bt_otp_client_read_op *oacp_read_op = NULL; bool otc_connection_status = FALSE; bt_otp_object_list_s *obj_list; @@ -209,6 +228,8 @@ static void _bt_otp_client_notify_read_object_status(int result, void __bt_otp_reset_api_info(bt_otp_client_s *otp_client_s); static void _bt_otp_client_send_select_object_callback(int result, bt_otp_client_s *otp_client_s); +static void _bt_otp_send_create_object_callback(int result, + uint64_t id, bt_otp_client_s *otp_client_s); int __bt_check_otp_server_init_status(void) { @@ -346,6 +367,20 @@ static void __bt_otp_client_reset_server_data(bt_otp_client_s *otp_client_s) g_free(obj_list); obj_list = NULL; } + + if (oacp_read_op) { + fclose(oacp_read_op->fp); + g_free(oacp_read_op->file_path); + g_free(oacp_read_op); + oacp_read_op = NULL; + } + + if (oacp_create_op) { + g_free(oacp_create_op->filename); + g_free(oacp_create_op->type_uuid); + g_free(oacp_create_op); + oacp_create_op = NULL; + } } int bt_otp_client_set_connection_state_changed_cb(bt_otp_client_h otp_client, @@ -1047,6 +1082,19 @@ void _bt_otp_client_read_value_response(int result, char *char_path, BT_ERR("Failed to write control point : %s(0x%08x)", _bt_convert_error_to_string(error_code), error_code); goto read_fail; } + } else if (otp_client_s->curr_op == BT_OTP_OBJECT_CREATE) { + metadata = g_malloc0(sizeof(object_metadata)); + metadata->name = g_strdup(oacp_create_op->filename); + metadata->type = g_strdup(oacp_create_op->type_uuid); + metadata->curr_size = oacp_create_op->size; + metadata->alloc_size = oacp_create_op->size; + /* Dont hard code */ + metadata->props = OBJECT_READ | OBJECT_WRITE; + metadata->first_created = oacp_create_op->first_created; + metadata->last_modified = oacp_create_op->first_created; + metadata->id = id; + otp_client_s->object_id = metadata->id; + otp_client_s->object_list = g_slist_append(otp_client_s->object_list, metadata); } } read_fail: @@ -1059,6 +1107,11 @@ read_fail: __bt_otp_reset_api_info(otp_client_s); } } + if (otp_client_s->curr_op == BT_OTP_OBJECT_CREATE) { + _bt_otp_send_create_object_callback(result, + otp_client_s->object_id, otp_client_s); + __bt_otp_reset_api_info(otp_client_s); + } } } @@ -1073,18 +1126,77 @@ void _bt_otp_client_write_value_response(int result, char *handle) if (result != BLUETOOTH_ERROR_NONE) { BT_DBG("OTP Write Failed"); - if (otp_client_s->curr_op == BT_OTP_OBJECT_DISCOVERY) { - _bt_otp_send_discovery_callback(result, otp_client_s); - __bt_otp_reset_api_info(otp_client_s); - } else if (otp_client_s->curr_op == BT_OTP_OBJECT_READ) { - _bt_otp_client_notify_read_object_status(result, - NULL, otp_client_s); - __bt_otp_reset_api_info(otp_client_s); - } else if (otp_client_s->curr_op == BT_OTP_OBJECT_SELECT) { - _bt_otp_client_send_select_object_callback(result, otp_client_s); - __bt_otp_reset_api_info(otp_client_s); + goto fail; + } + + if (otp_client_s->curr_op == BT_OTP_OBJECT_CREATE) { + if ((!g_strcmp0(otp_client_s->otp_name_obj_path, handle) || + !g_strcmp0(otp_client_s->otp_first_created_obj_path, handle)) && + oacp_create_op) { + struct tm fc_tm; + uint8_t value[8]; + int error_code; + memset(value, 0, 8); + localtime_r(&(oacp_create_op->first_created), &fc_tm); + + value[1] = ((fc_tm.tm_year+1900) >> 8) & 0xFF; + value[0] = (fc_tm.tm_year+1900) & 0xFF; + value[2] = (fc_tm.tm_mon+1) & 0xFF; + value[3] = fc_tm.tm_mday & 0xFF; + value[4] = fc_tm.tm_hour & 0xFF; + value[5] = fc_tm.tm_min & 0xFF; + value[6] = fc_tm.tm_sec & 0xFF; + + if (!g_strcmp0(otp_client_s->otp_name_obj_path, handle)) { + /* Write First-Created */ + error_code = bluetooth_otp_write_characteristics_value(otp_client_s->otp_first_created_obj_path, + value, sizeof(value)); + } else { + /* Write Last-Modified */ + error_code = bluetooth_otp_write_characteristics_value(otp_client_s->otp_last_modified_obj_path, + value, sizeof(value)); + } + + if (error_code != BT_ERROR_NONE) { + BT_ERR("Failed to write first_created / last_modified : %s(0x%08x)", + _bt_convert_error_to_string(error_code), error_code); + result = error_code; + goto fail; + } + } else if (!g_strcmp0(otp_client_s->otp_last_modified_obj_path, handle)) { + /* Read Object ID */ + int error_code; + error_code = bluetooth_otp_read_characteristic_value(otp_client_s->otp_id_obj_path); + if (error_code != BLUETOOTH_ERROR_NONE) { + BT_INFO("Read object ID Failed %s(0x%08x)", _bt_convert_error_to_string(error_code), error_code); + result = error_code; + goto fail; + } } } + return; +fail: + switch (otp_client_s->curr_op) { + case BT_OTP_OBJECT_DISCOVERY: + _bt_otp_send_discovery_callback(result, otp_client_s); + __bt_otp_reset_api_info(otp_client_s); + break; + case BT_OTP_OBJECT_READ: + _bt_otp_client_notify_read_object_status(result, + NULL, otp_client_s); + __bt_otp_reset_api_info(otp_client_s); + break; + case BT_OTP_OBJECT_SELECT: + _bt_otp_client_send_select_object_callback(result, otp_client_s); + __bt_otp_reset_api_info(otp_client_s); + break; + case BT_OTP_OBJECT_CREATE: + _bt_otp_send_create_object_callback(result, 0, otp_client_s); + __bt_otp_reset_api_info(otp_client_s); + break; + case BT_OTP_NO_OPERATION: + break; + } } void _bt_otp_client_notification_enabled(int result, char *handle) @@ -1152,6 +1264,21 @@ void _bt_otp_client_indication(int result, bluetooth_otp_resp_info_t *info) switch (req_opcode) { case OACP_CREATE: + BT_INFO("OACP_CREATE Indication received"); + if ((otp_client_s->curr_op == BT_OTP_OBJECT_CREATE) && oacp_create_op) { + int len = strlen(oacp_create_op->filename); + uint8_t *value = (uint8_t *)malloc(len * sizeof(uint8_t)); + memcpy(value, oacp_create_op->filename, len); + error_code = bluetooth_otp_write_characteristics_value(otp_client_s->otp_name_obj_path, + value, len); + if (error_code != BT_ERROR_NONE) { + BT_ERR("Failed to write object name : %s(0x%08x)", + _bt_convert_error_to_string(error_code), error_code); + result = error_code; + goto oacp_done; + } + } + break; case OACP_DELETE: case OACP_CALC_CHECKSUM: case OACP_EXECUTE: @@ -1187,6 +1314,9 @@ oacp_done: if (otp_client_s->curr_op == BT_OTP_OBJECT_READ) { _bt_otp_client_notify_read_object_status(result, NULL, otp_client_s); __bt_otp_reset_api_info(otp_client_s); + } else if (otp_client_s->curr_op == BT_OTP_OBJECT_CREATE) { + _bt_otp_send_create_object_callback(result, 0, otp_client_s); + __bt_otp_reset_api_info(otp_client_s); } } else if (!g_strcmp0(otp_client_s->otp_olcp_control_point, info->handle)) { uint8_t resp_code = info->data[0]; @@ -1668,3 +1798,106 @@ int bt_otp_client_read_object_contents(bt_otp_client_h otp_client, return error_code; } + +static void _bt_otp_send_create_object_callback(int result, + uint64_t id, bt_otp_client_s *otp_client_s) +{ + + ((bt_otp_client_object_create_cb)otp_client_s->callback)( + result, otp_client_s->remote_address, + id, otp_client_s->user_data); + + if (oacp_create_op) { + g_free(oacp_create_op->filename); + g_free(oacp_create_op->type_uuid); + g_free(oacp_create_op); + oacp_create_op = NULL; + } +} + +int bt_otp_client_create_object(bt_otp_client_h otp_client, + const char *file_path, + bt_otp_client_object_create_cb callback, + void *user_data) +{ + int error_code; + bt_otp_client_s *otp_client_s = (bt_otp_client_s *)otp_client; + struct stat st; + uint32_t size; + char *file_name, *last_token; + time_t curr_time; + char *type_uuid; + + BT_CHECK_LE_SUPPORT(); + BT_CHECK_INIT_STATUS(); + BT_CHECK_INPUT_PARAMETER(otp_client_s); + + if (_bt_otp_client_find(otp_client_s->remote_address) == NULL) + return BT_ERROR_NOT_INITIALIZED; + + if (otp_client_s->connected == false) { + BT_ERR("Remote device [%s] is not conencted", otp_client_s->remote_address); + return BT_ERROR_OPERATION_FAILED; + } + + if (otp_client_s->curr_op != BT_OTP_NO_OPERATION) { + BT_DBG("OTP Client is busy"); + return BT_ERROR_OPERATION_FAILED; + } + + if (~BT_OTP_IS_OACP_CREATE_SUPPORTED(otp_client_s->otp_feature)) { + BT_INFO("OACP Create not supported"); + return BT_ERROR_OPERATION_FAILED; + } + + otp_client_s->callback = callback; + otp_client_s->user_data = user_data; + otp_client_s->curr_op = BT_OTP_OBJECT_CREATE; + + BT_DBG("OTP client create object [%s] in Remote device [%s]", + file_path, otp_client_s->remote_address); + + /* Get file_name & size from file_path */ + if (stat(file_path, &st) == -1) { + BT_INFO("stat failed: %s (%d)\n", strerror(errno), errno); + return BLUETOOTH_ERROR_INTERNAL; + } + + size = (uint32_t)st.st_size; + last_token = strrchr(file_path, '/'); + file_name = last_token + 1; + + BT_INFO("Filepath [%s], Filename [%s], Size[%llu]\n", + file_path, file_name, size); + + oacp_create_op = g_malloc0(sizeof(bt_otp_client_create_op)); + oacp_create_op->size = size; + oacp_create_op->filename = g_strdup(file_name); + + time(&curr_time); + oacp_create_op->first_created = curr_time; + + /* There isn't clear description for Object Type in spec */ + type_uuid = g_strdup(UNSUPPORTED_OBJECT_TYPE_UUID); + oacp_create_op->type_uuid = g_strdup(type_uuid); + + /* UUIDs can be 128/64/16 bits */ + uint8_t value[40] = {0x00}; + value[0] = OACP_CREATE; + value[1] = size & 0xFF; + value[2] = (size >> 8) & 0xFF; + value[3] = (size >> 16) & 0xFF; + value[4] = (size >> 24) & 0xFF; + + memcpy(value + 5, type_uuid, strlen(type_uuid)); + + error_code = bluetooth_otp_write_characteristics_value(otp_client_s->otp_oacp_control_point, + value, 5 + strlen(type_uuid)); + if (error_code != BT_ERROR_NONE) { + BT_ERR("Failed to write control point : %s(0x%08x)", + _bt_convert_error_to_string(error_code), error_code); + __bt_otp_reset_api_info(otp_client_s); + } + g_free(type_uuid); + return error_code; +} diff --git a/test/bt_unit_test.c b/test/bt_unit_test.c index 53ea832..9ff394c 100644 --- a/test/bt_unit_test.c +++ b/test/bt_unit_test.c @@ -1182,6 +1182,8 @@ tc_table_t tc_otp[] = { , BT_UNIT_TEST_FUNCTION_OTP_CLIENT_SELECT_OBJ}, {"bt_otp_client_read_object_contents" , BT_UNIT_TEST_FUNCTION_OTP_CLIENT_READ_OBJ_CONTENTS}, + {"bt_otp_client_create_object" + , BT_UNIT_TEST_FUNCTION_OTP_CLIENT_CREATE_OBJ}, {NULL , 0x0000}, }; @@ -3293,6 +3295,15 @@ static void __bt_otp_client_read_object_complete_cb(int result, const char *remo BT_ERR("Read object failed!"); } +static void __bt_otp_client_object_create_cb(int result, const char *remote_address, + unsigned long long obj_id, void *user_data) +{ + if (result == BT_ERROR_NONE) { + TC_PRT("Object[%llu] created successfully in remote server!", obj_id); + } else + BT_ERR("Object creation failed!"); +} + static void __bt_initialize_all(void) { int ret; @@ -4952,6 +4963,41 @@ int test_set_params(int test_id, char *param) break; } + case BT_UNIT_TEST_FUNCTION_OTP_CLIENT_CREATE_OBJ: { + if (param_index == 0) { + g_test_param.param_count = 1; + g_test_param.params = g_malloc0(sizeof(char *) *g_test_param.param_count); + } + + if (param_index > 0) { + int len = strlen(param); + g_test_param.params[param_index - 1] = g_malloc0(len + 1); + /* Remove new line character */ + param[len - 1] = '\0'; + strncpy(g_test_param.params[param_index - 1], param, strlen(param)); + } + + if (param_index == g_test_param.param_count) { + need_to_set_params = false; +#ifdef ARCH64 + test_input_callback((void *)(uintptr_t)test_id); +#else + test_input_callback((void *)test_id); +#endif + param_index = 0; + return 0; + } + + switch (param_index) { + case 0: + TC_PRT("Input Value in string"); + break; + } + + param_index++; + + break; + } default: TC_PRT("There is no param to set\n"); need_to_set_params = false; @@ -10378,6 +10424,27 @@ int test_input_callback(void *data) } break; } + case BT_UNIT_TEST_FUNCTION_OTP_CLIENT_CREATE_OBJ: { + char *file_path = NULL; + if (g_test_param.param_count < 1) { + TC_PRT("Input parameters first"); + break; + } + + file_path = g_test_param.params[0]; + TC_PRT("%s", file_path); + + if (otp_client) { + ret = bt_otp_client_create_object(otp_client, + file_path, __bt_otp_client_object_create_cb, NULL); + if (ret < BT_ERROR_NONE) + TC_PRT("failed with [0x%04x]", ret); + else if (ret == BT_ERROR_NONE) + TC_PRT("Success"); + } + __bt_free_test_param(&g_test_param); + break; + } case BT_UNIT_TEST_FUNCTION_ACTIVATE_FLAG_TO_SET_PARAMETERS: need_to_set_params = true; TC_PRT("Select the function again"); diff --git a/test/bt_unit_test.h b/test/bt_unit_test.h index e591e11..4f09eba 100644 --- a/test/bt_unit_test.h +++ b/test/bt_unit_test.h @@ -496,6 +496,7 @@ typedef enum { BT_UNIT_TEST_FUNCTION_OTP_CLIENT_DISCOVER_ALL_OBJECTS, BT_UNIT_TEST_FUNCTION_OTP_CLIENT_SELECT_OBJ, BT_UNIT_TEST_FUNCTION_OTP_CLIENT_READ_OBJ_CONTENTS, + BT_UNIT_TEST_FUNCTION_OTP_CLIENT_CREATE_OBJ, BT_UNIT_TEST_FUNCTION_ACTIVATE_FLAG_TO_SET_PARAMETERS = 0XFF, } bt_unit_test_function_e; -- 2.7.4