/** * Copyright (2017) Baidu Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * File: lightduer.c * Auth: Su Hao (suhao@baidu.com) * Desc: Light duer APIS. */ #include #include "lightduer_connagent.h" #include "lightduer_log.h" #include "lightduer_lib.h" #include "lightduer_adapter.h" #include "lightduer_memory.h" #include "lightduer_events.h" #include "lightduer_engine.h" #include "lightduer_ds_report.h" #include "lightduer_ds_report_ca.h" #include "lightduer_ds_log_ca.h" #include "lightduer_ds_log_cache.h" #include "lightduer_timestamp.h" #include "lightduer_event_emitter.h" #include "lightduer_random.h" #include "lightduer_mutex.h" #ifdef DUER_EVENT_HANDLER #include "lightduer_handler.h" #endif // DUER_EVENT_HANDLER #include "lightduer_ap_info.h" extern void duer_voice_initialize(void); extern void duer_voice_finalize(void); extern int duer_voice_terminate(void); extern int duer_data_send(void); extern int duer_engine_enqueue_response( const duer_msg_t *req, int msg_code, const void *data, duer_size_t size); extern int duer_engine_enqueue_seperate_response( const char *token, duer_size_t token_len, int msg_code, const baidu_json *data); extern void duer_engine_send_directly(int what, void *object); extern void duer_session_initialize(void); extern void duer_session_finalize(void); static duer_event_callback g_event_callback = NULL; #ifndef DISABLE_SPLIT_SEND static volatile duer_u32_t s_split_id; //used to keep the s_split_id is not negative when adding to json as int static const int SPLIT_ID_MASK = 0x7fffffff; static duer_mutex_t s_split_mutex; #endif // begin device status report static duer_u32_t s_connect_start_time = 0; static duer_u32_t s_connect_end_time = 0; static duer_u32_t s_connect_count = 0; // end device status report // MAX num message in CA queue #ifndef DUER_MAX_MSG_NUM_IN_CA_QUEUE #define DUER_MAX_MSG_NUM_IN_CA_QUEUE 20 #endif //DUER_MAX_MSG_NUM_IN_CA_QUEUE // begin whether clear the message queue #ifndef MAX_INTERVAL_KEEP_CA_QUEUE #define MAX_INTERVAL_KEEP_CA_QUEUE 4000 // ms #endif // MAX_INTERVAL_KEEP_CA_QUEUE // begin whether clear the voice data #ifndef MAX_INTERVAL_KEEP_VOICE_QUEUE #define MAX_INTERVAL_KEEP_VOICE_QUEUE 1900 //ms when interval > 2s,the server will stop immediately. #endif // MAX_INTERVAL_KEEP_VOICE_QUEUE static duer_u32_t s_first_stopped_time = 0; #ifdef DUER_BJSON_PRINT_WITH_ESTIMATED_SIZE #define DUER_PRINT_BJSON_ESTIMATED_SIZE_FOR_REPORT_DATA (1024) #endif // invoke this after CA connected static duer_status_t duer_ca_try_clear_queue() { duer_u32_t current_time = duer_timestamp(); if (s_first_stopped_time > 0 && (current_time < s_first_stopped_time || current_time - s_first_stopped_time > MAX_INTERVAL_KEEP_CA_QUEUE)) { duer_clear_data(duer_timestamp()); duer_ds_log_ca_info(DUER_DS_LOG_CA_CLEAR_DATA, NULL); } #if !defined(__TARGET_TRACKER__) if (s_first_stopped_time > 0 && (current_time < s_first_stopped_time || current_time - s_first_stopped_time > MAX_INTERVAL_KEEP_VOICE_QUEUE)) { duer_voice_terminate(); } #endif s_first_stopped_time = 0; return DUER_OK; } // end whether clear the message queue static char *g_event_name[] = { "DUER_EVT_CREATE", "DUER_EVT_START", "DUER_EVT_ADD_RESOURCES", "DUER_EVT_SEND_DATA", "DUER_EVT_DATA_AVAILABLE", "DUER_EVT_STOP", "DUER_EVT_DESTROY", "DUER_EVENTS_TOTAL" }; static void duer_event_notify(int event_id, int event_code, void *object) { if (g_event_callback) { duer_event_t event; event._event = event_id; event._code = event_code; event._object = object; g_event_callback(&event); } } static void duer_set_coap_response_callback(void) { //duer_emitter_emit(duer_engine_set_response_callback, 0, duer_coap_response_callback); duer_engine_set_response_callback(0, duer_coap_response_callback); } #ifndef DISABLE_SPLIT_SEND static void duer_split_id_initialize() { if (!s_split_mutex) { s_split_mutex = duer_mutex_create(); } duer_mutex_lock(s_split_mutex); s_split_id = duer_random() & SPLIT_ID_MASK; duer_mutex_unlock(s_split_mutex); } static void duer_split_id_finalize() { if (s_split_mutex) { duer_mutex_destroy(s_split_mutex); s_split_mutex = NULL; } } #endif static void duer_engine_notify_callback(int event, int status, int what, void *object) { static duer_status_t reason_to_start = DUER_OK; duer_status_t wifi_status = DUER_OK; if (status != DUER_OK && status != DUER_ERR_TRANS_WOULD_BLOCK && status != DUER_ERR_CONNECT_TIMEOUT && status != DUER_ERR_HAS_STOPPED) { DUER_LOGI("=====> event: %d[%s], status: %d", event, g_event_name[event], status); } switch (event) { case DUER_EVT_CREATE: duer_session_initialize(); duer_set_coap_response_callback(); #ifndef DISABLE_SPLIT_SEND duer_split_id_initialize(); #endif break; case DUER_EVT_START: if (object) { DUER_FREE(object); } if (status == DUER_OK) { duer_ds_log_init(); #if !defined(__TARGET_TRACKER__) duer_voice_initialize(); #endif DUER_LOGI("connect started!"); duer_ca_try_clear_queue(); s_connect_end_time = duer_timestamp(); duer_u32_t connect_time_cost = (s_connect_end_time - s_connect_start_time); duer_ds_log_ca_started(reason_to_start, s_connect_count, connect_time_cost); s_connect_count = 0; reason_to_start = DUER_OK; duer_ds_log_cache_report(); duer_event_notify(DUER_EVENT_STARTED, status, NULL); if (duer_engine_qcache_length() > 0) { duer_data_send(); } duer_ds_report_start(DUER_DS_PERIODIC, 0);// use the default interval } else if (status == DUER_ERR_TRANS_WOULD_BLOCK) { DUER_LOGI("will start latter(DUER_ERR_TRANS_WOULD_BLOCK)"); } else if (status == DUER_ERR_CONNECT_TIMEOUT) { int rs = duer_emitter_emit(duer_engine_start, 0, NULL); if (rs != DUER_OK) { DUER_LOGW("retry connect fail!!"); } } else if (status == DUER_ERR_HAS_STARTED) { DUER_LOGW("Duer CA engine has started!"); } else { DUER_LOGE("start fail! status:%d", status); } break; case DUER_EVT_ADD_RESOURCES: if (status == DUER_OK) { DUER_LOGI("add resource successfully!!"); } if (object) { duer_res_t *resources = (duer_res_t *)object; for (int i = 0; i < what; ++i) { if (resources[i].path) { DUER_FREE(resources[i].path);// more info in duer_add_resources resources[i].path = NULL; } } DUER_FREE(object); } break; case DUER_EVT_SEND_DATA: if (object) { DUER_LOGI("data sent : %s", (const char *)object); baidu_json_release(object); } //DUER_LOGI("====status:%d, len:%d=", status, duer_engine_qcache_length()); if (status >= 0) { if (duer_engine_qcache_length() > 0) { DUER_LOGD("###QCACHE_LEN====%d.", duer_engine_qcache_length()); duer_data_send(); } } else if (status == DUER_ERR_TRANS_WOULD_BLOCK) { DUER_LOGD("!!!Send WOULD_BLOCK"); } else { //do nothing } break; case DUER_EVT_DATA_AVAILABLE: break; case DUER_EVT_STOP: if (status == DUER_ERR_TRANS_TIMEOUT) { duer_clear_data(duer_timestamp()); #if !defined(__TARGET_TRACKER__) duer_voice_terminate(); #endif duer_ds_log_ca_info(DUER_DS_LOG_CA_CLEAR_DATA, NULL); } else { if (s_first_stopped_time == 0) { s_first_stopped_time = duer_timestamp(); } } duer_ds_log_ca_debug(DUER_DS_LOG_CA_STOPPED, NULL); DUER_LOGI("connect stopped, status:%d!!", status); #if !defined(__TARGET_TRACKER__) duer_voice_finalize(); #endif duer_event_notify(DUER_EVENT_STOPPED, status, NULL); break; case DUER_EVT_DESTROY: duer_ds_report_destroy(); duer_session_finalize(); duer_split_id_finalize(); duer_ds_log_cache_finalize(); baidu_ca_adapter_finalize(); #ifdef DUER_EVENT_HANDLER duer_handler_destroy(); #endif // DUER_EVENT_HANDLER #if 0 //线程不能自杀 duer_emitter_destroy(); #else duer_emitter_request_destroy(); #endif s_first_stopped_time = 0; break; default: DUER_LOGW("unknown event!!"); break; } if ((event < DUER_EVT_STOP && status != DUER_OK && status != DUER_ERR_TRANS_WOULD_BLOCK && status != DUER_ERR_CONNECT_TIMEOUT && status != DUER_ERR_HAS_STARTED && status != DUER_ERR_HAS_STOPPED) || (event == DUER_EVT_STOP)) { duer_ds_report_stop(); if (reason_to_start == DUER_OK) { // only record the first stop reason reason_to_start = status; wifi_status = duer_wifi_status_get(); if (wifi_status != DUER_OK && wifi_status != DUER_ERR_FAILED) { reason_to_start = wifi_status; } DUER_LOGD("Wifi status: %d", wifi_status); } DUER_LOGE("Action failed: event: %d, status: %d", event, status); if (event != DUER_EVT_STOP) { duer_engine_stop(status, NULL); } } } void duer_initialize() { baidu_ca_adapter_initialize(); duer_emitter_create(); #ifdef DUER_EVENT_HANDLER duer_handler_create(); #endif // DUER_EVENT_HANDLER duer_engine_register_notify(duer_engine_notify_callback); int res = duer_emitter_emit(duer_engine_create, 0, NULL); if (res != DUER_OK) { DUER_LOGE("duer_initialize fail! res:%d", res); } int ret = duer_ds_register_report_function(duer_ds_report_ca_status); if (ret != DUER_OK) { DUER_LOGE("report ca status register failed ret: %d", ret); } duer_ds_log_cache_initialize(); duer_ds_log_ca_debug(DUER_DS_LOG_CA_INIT, NULL); } void duer_set_event_callback(duer_event_callback callback) { if (g_event_callback != callback) { g_event_callback = callback; } } int duer_start(const void *data, size_t size) { if (data == NULL || size == 0) { DUER_LOGE("duer_start invalid arguments"); return DUER_ERR_INVALID_PARAMETER; } duer_ds_log_ca_debug(DUER_DS_LOG_CA_START, NULL); ++s_connect_count; void *profile = DUER_MALLOC(size); // ~= 1439 if (profile == NULL) { DUER_LOGE("memory alloc fail!"); DUER_DS_LOG_CA_MEMORY_OVERFLOW(); return DUER_ERR_MEMORY_OVERFLOW; } DUER_MEMCPY(profile, data, size); DUER_LOGD("use the profile to start engine"); int rs = duer_emitter_emit(duer_engine_start, (int)size, profile); if (rs != DUER_OK) { DUER_LOGE("emit duer_engine_start failed!"); DUER_FREE(profile); } s_connect_start_time = duer_timestamp(); return rs; } int duer_add_resources(const duer_res_t *res, size_t length) { if (duer_engine_is_started() != DUER_TRUE) { DUER_LOGW("please add control point after the CA started"); return DUER_ERR_FAILED; } if (length == 0 || res == NULL) { DUER_LOGW("no resource will be added, length is 0"); return DUER_ERR_FAILED; } duer_bool malloc_failed = DUER_FALSE; duer_res_t *list = (duer_res_t *)DUER_MALLOC(length * sizeof(duer_res_t)); // ~= length * 16 if (list == NULL) { malloc_failed = DUER_TRUE; goto malloc_fail; } DUER_MEMSET(list, 0, length * sizeof(duer_res_t)); for (int i = 0; i < length; ++i) { list[i].mode = res[i].mode; list[i].allowed = res[i].allowed; size_t path_len = DUER_STRLEN(res[i].path); if (list[i].mode == DUER_RES_MODE_STATIC) { // malloc the path&static data once, Note only need free once list[i].path = DUER_MALLOC(path_len + res[i].res.s_res.size + 1); if (list[i].path == NULL) { malloc_failed = DUER_TRUE; goto malloc_fail; } DUER_MEMCPY(list[i].path, res[i].path, path_len); list[i].path[path_len] = '\0'; list[i].res.s_res.size = res[i].res.s_res.size; list[i].res.s_res.data = list[i].path + 1 + path_len; DUER_MEMCPY(list[i].res.s_res.data, res[i].res.s_res.data, list[i].res.s_res.size); duer_ds_log_ca_register_cp(list[i].path, DUER_CA_CP_TYPE_STATIC); } else { list[i].path = DUER_MALLOC(path_len + 1); if (list[i].path == NULL) { malloc_failed = DUER_TRUE; goto malloc_fail; } DUER_MEMCPY(list[i].path, res[i].path, path_len); list[i].path[path_len] = '\0'; list[i].res.f_res = res[i].res.f_res; duer_ds_log_ca_register_cp(list[i].path, DUER_CA_CP_TYPE_DYNAMIC); } } if (malloc_failed == DUER_FALSE) { int rs = duer_emitter_emit(duer_engine_add_resources, (int)length, (void *)list); if (rs == DUER_OK) { return DUER_OK; } else { DUER_LOGW("add resource fail!!"); goto add_fail; } } malloc_fail: DUER_LOGW("memory malloc fail!!"); DUER_DS_LOG_CA_MEMORY_OVERFLOW(); add_fail: if (list) { for (int i = 0; i < length; ++i) { if (list[i].path) { DUER_FREE(list[i].path); list[i].path = NULL; } } DUER_FREE(list); list = NULL; } return DUER_ERR_FAILED; } int duer_data_send(void) { //DUER_LOGI("===%s===", __func__); return duer_emitter_emit(duer_engine_send, 0, NULL); } static int duer_data_real_send(void) { //DUER_LOGI("===%s===", __func__); return duer_emitter_emit(duer_engine_send_directly, 0, NULL); } static int duer_after_enqueue_handler(int rs) { if (rs > 0) { if (rs == 1) { rs = duer_data_send(); } else if (rs > 10) { DUER_LOGW("cached more than 10 message!!"); rs = duer_data_real_send(); } else { rs = DUER_OK; } } return rs; } #ifndef DISABLE_SPLIT_SEND static int duer_copy_split_data(char *des, int size, char *source) { int des_str_len = 0; int special_char_count = 0; if (!des || !source || size <= 0) { DUER_LOGE("Invalid param"); return 0; } while (source[des_str_len] != '\0') { if ((source[des_str_len] == '\"') || (source[des_str_len] == '\\')) { special_char_count++; } if (des_str_len + special_char_count + 1 >= size - 1) { break; } des[des_str_len] = source[des_str_len]; des_str_len++; } des[des_str_len] = '\0'; return des_str_len; } static int duer_data_get_split_id() { duer_u32_t ret = 0; duer_mutex_lock(s_split_mutex); s_split_id = (s_split_id + 1) & SPLIT_ID_MASK; ret = s_split_id; duer_mutex_unlock(s_split_mutex); return ret; } #endif int duer_data_report_async(duer_context_t *context, const baidu_json *data) { #ifdef DISABLE_SPLIT_SEND int rs = duer_engine_enqueue_report_data(context, data); //DUER_LOGI("===%s===, context:%p, rs:%d", __func__, context, rs); return duer_after_enqueue_handler(rs); #else int rs = DUER_OK; int block = 0; int content_len = 0; int send_len = 0; int remain_len = 0; duer_u32_t id = 0; const int MAX_SEG_LEN = 990; const int MAX_PAYLOAD_LEN = 900; char *buf = NULL; char *content = NULL; baidu_json *value = NULL; baidu_json *payload = NULL; do { #ifdef DUER_BJSON_PRINT_WITH_ESTIMATED_SIZE content = baidu_json_PrintBuffered(data, DUER_PRINT_BJSON_ESTIMATED_SIZE_FOR_REPORT_DATA, 0); #else content = baidu_json_PrintUnformatted(data); #endif // DUER_BJSON_PRINT_WITH_ESTIMATED_SIZE id = duer_data_get_split_id(); if (!content) { rs = DUER_ERR_MEMORY_OVERFLOW; break; } content_len = DUER_STRLEN(content); if (content_len <= MAX_SEG_LEN) { baidu_json_release(content); content = NULL; rs = duer_engine_enqueue_report_data(context, data); //DUER_LOGI("===%s===, context:%p, rs:%d", __func__, context, rs); rs = duer_after_enqueue_handler(rs); break; } DUER_LOGD("split send data, %d", content_len); buf = DUER_MALLOC(MAX_PAYLOAD_LEN + 1); // ~= 901 if (!buf) { rs = DUER_ERR_MEMORY_OVERFLOW; break; } value = baidu_json_CreateObject(); if (!value) { rs = DUER_ERR_MEMORY_OVERFLOW; break; } payload = baidu_json_CreateObject(); if (!payload) { rs = DUER_ERR_MEMORY_OVERFLOW; break; } baidu_json_AddItemToObjectCS(value, "duer_blockwise", payload); baidu_json_AddNumberToObjectCS(payload, "id", (int)id); baidu_json_AddNumberToObjectCS(payload, "eof", 0); remain_len = content_len; while (remain_len > 0) { baidu_json_DeleteItemFromObject(payload, "block"); baidu_json_AddNumberToObjectCS(payload, "block", block++); send_len = duer_copy_split_data(buf, MAX_PAYLOAD_LEN + 1, content + content_len - remain_len); if (send_len == 0) { rs = DUER_ERR_FAILED; break; } DUER_LOGD("remain_len:%d, block:%d, send_len:%d", remain_len, block, send_len); remain_len -= send_len; if (remain_len == 0) { baidu_json_DeleteItemFromObject(payload, "eof"); baidu_json_AddNumberToObjectCS(payload, "eof", 1); } baidu_json_DeleteItemFromObject(payload, "block_data"); baidu_json_AddStringToObjectCS(payload, "block_data", buf); rs = duer_engine_enqueue_report_data(context, value); //DUER_LOGI("===%s===, context:%p, rs:%d", __func__, context, rs); rs = duer_after_enqueue_handler(rs); //sleep needed if the invoke thread has the same priority with lightduer_ca, //sleep can trigger lightduer_ca to do the real send work //#include "lightduer_sleep.h" //duer_sleep(1); if (rs != DUER_OK) { break; } } } while (0); if (content) { baidu_json_release(content); } if (buf) { DUER_FREE(buf); } if (value) { baidu_json_Delete(value); } return rs; #endif } int duer_data_report(const baidu_json *data) { if (duer_engine_qcache_length() > DUER_MAX_MSG_NUM_IN_CA_QUEUE) { DUER_LOGW("CA queue max num(%d) reached!", DUER_MAX_MSG_NUM_IN_CA_QUEUE); return DUER_ERR_FAILED; } return duer_data_report_async(NULL, data); } int duer_response(const duer_msg_t *msg, int msg_code, const void *data, duer_size_t size) { int rs = duer_engine_enqueue_response(msg, msg_code, data, size); //DUER_LOGI("===%s=== rs:%d", __func__, rs); return duer_after_enqueue_handler(rs); } int duer_seperate_response(const char *token, duer_size_t token_len, int msg_code, const baidu_json *data) { int rs = duer_engine_enqueue_seperate_response(token, token_len, msg_code, data); return duer_after_enqueue_handler(rs); } int duer_data_available() { return duer_emitter_emit(duer_engine_data_available, 0, NULL); } int duer_clear_data(duer_u32_t timestamp) { return duer_emitter_emit((duer_events_func)duer_engine_clear_data, timestamp, NULL); } int duer_stop() { return duer_emitter_emit(duer_engine_stop, 0, NULL); } int duer_stop_with_reason(int reason) { return duer_emitter_emit(duer_engine_stop, reason, NULL); } void duer_coap_response_callback(duer_context ctx, duer_msg_t *msg, duer_addr_t *addr) { // to handle response with unauthorized code 129 = 0x81(4.01) if (msg->msg_type == DUER_MSG_TYPE_ACKNOWLEDGEMENT && msg->msg_code == DUER_MSG_RSP_UNAUTHORIZED) { // try to stop CA DUER_LOGW("DUER_MSG_RSP_UNAUTHORIZED code for coap ACK message, try to stop"); duer_stop_with_reason(DUER_MSG_RSP_UNAUTHORIZED); } } int duer_finalize(void) { return duer_emitter_emit(duer_engine_destroy, 0, NULL); }