blob: cdfa6fe2fd288d8b781235799d7c321e75228aa6 [file] [log] [blame]
/*-----------------------------------------------------------------------------------------------*/
/**
@file ql_sms.h
@brief SMS service API.
*/
/*-----------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------
Copyright (c) 2019 Quectel Wireless Solution, Co., Ltd. All Rights Reserved.
Quectel Wireless Solution Proprietary and Confidential.
-------------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------
EDIT HISTORY
This section contains comments describing changes made to the file.
Notice that changes are listed in reverse chronological order.
$Header: $
when who what, where, why
-------- --- ----------------------------------------------------------
20200107 solomon.cui Add GSM-7bit and ISO 8859-1 conversion.
20191225 solomon.cui Modify fucntion description.
20191017 solomon.cui Free async reponse not user data.
20190815 solomon.cui Add service type for sending message.
20190627 solomon.cui Support asynchronously send msg and pdu
20190625 solomon.cui Convert timestamp frome hex to dec.
20190614 solomon.cui Created .
-------------------------------------------------------------------------------------------------*/
#include "mbtk_type.h"
#include "mbtk_pdu_sms.h"
#include "mbtk_ril_api.h"
#include "mbtk_log.h"
#include "ql_sms.h"
#include "ql_type.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#define MBTK_ERR_OK 0
#define TELEPHONE_NUM_MAX 16
#define MSM_NUMBER_MAX 2048+1
#define RES_NUM_MIN 128
#define TIME_ZONE_CN 32
#define HAL_SMS_CONTENT_LEN 1024
typedef struct {
mbtk_ril_handle* handle;
ql_sms_msg_recv_cb_f recv_cb;
ql_sms_service_error_cb_f server_cb;
}ql_sms_info_handle_t;
static ql_sms_info_handle_t* sms_handle = NULL;
static mbtk_sim_type_enum s_sms_slot = MBTK_SIM_1;
static bool check_slot_valid(QL_SIM_SLOT_E slot)
{
if (slot !=QL_SIM_SLOT_1 && slot != QL_SIM_SLOT_2)
{
LOGE("bad slot: %d, slot should be 1 or 2", slot);
return false;
}
return true;
}
static void ql_slot_convert_to_mbtk(QL_SIM_SLOT_E ql_slot,mbtk_sim_type_enum *mbtk_slot)
{
if(ql_slot == QL_SIM_SLOT_1)
{
*mbtk_slot = MBTK_SIM_1;
}
if(ql_slot == QL_SIM_SLOT_2)
{
*mbtk_slot = MBTK_SIM_2;
}
return;
}
int ql_set_sms_slot(QL_SIM_SLOT_E log_slot)
{
if(!check_slot_valid(log_slot))
{
LOGE("[%s] check_slot_valid failed.", __func__);
return QL_ERR_INVALID_ARG;
}
ql_slot_convert_to_mbtk(log_slot,&s_sms_slot);
LOGE("s_net_slot is %d",s_sms_slot);
return QL_ERR_OK;
}
static void ql_sms_state_change_cb(const void* data, int data_len)
{
LOGV("sms_state_change_cb()----------start\n");
mbtk_ril_sms_state_info_t *ptr = (mbtk_ril_sms_state_info_t *)data;
ql_sms_msg_t sms_msg;
ql_sms_timestamp_t sms_timestamp;
ql_sms_user_data_head_t sms_user_data_head;
memset(&sms_msg,0x00, sizeof(ql_sms_msg_t));
memset(&sms_timestamp,0x00, sizeof(ql_sms_timestamp_t));
memset(&sms_user_data_head,0x00, sizeof(ql_sms_user_data_head_t));
char smsc[MDAPI_MAX_PDU_SIZE] = {0};
char received_pdu[MDAPI_MAX_PDU_SIZE] = {0};
char msg[MDAPI_MAX_PDU_SIZE] = {0};
char num[MDAPI_MAX_PDU_SIZE] = {0};
uint8 year[8] = {0};
uint8 month[4] = {0};
uint8 day[4] = {0};
uint8 hour[4] = {0};
uint8 minutes[4]= {0};
uint8 seconds[4]= {0};
//char timezone[4] = {0};
char date[32] = {0};
int charset = 0;
int ret = -1;
int curr_pack = 1;
int total_pack = 1;
if(ptr == NULL)
{
LOGE("ptr is null");
}
LOGE("ptr: %s\n,data_len = %d\n", (char *)ptr->pdu, data_len);
if(data_len > MDAPI_MAX_PDU_SIZE)
{
strncpy(received_pdu, (const char *)ptr->pdu, MDAPI_MAX_PDU_SIZE-1);
}
else
{
strncpy(received_pdu, (const char *)ptr->pdu, data_len);
}
ret = smsPduDecode((const char *)received_pdu, data_len, num, smsc, msg, &charset, &curr_pack, &total_pack,date);
if(ret != 0)
{
LOGE("smsPduDecode fail ret: %d\n",ret);
return ;
}
LOGE("[EVENT][MT_SMS]PDU decode:smsc: %s\n, phone number: %s\ncharset: %d\n msg_len: %d\n message content: %s\n curr_pack: %d\n total_pack: %d\n date: %s", smsc, num, charset, strlen(msg), msg, curr_pack, total_pack, date);
sms_msg.format = charset;
memcpy(sms_msg.addr, num, strlen(num));
sms_msg.content_size = strlen(msg);
memcpy(sms_msg.content, msg, strlen(msg));
if(sscanf(date, "%4[^-]-%2[^-]-%2[^ ] %2[^:]:%2[^:]:%2s", year, month, day, hour, minutes, seconds)<=0)
{
LOGE("sscanf failed\n");
}
int tmp_year = atoi((char *)year);
sms_timestamp.year = tmp_year-2000;
sms_timestamp.month = atoi((char *)month);
sms_timestamp.day = atoi((char *)day);
sms_timestamp.hours = atoi((char *)hour);
sms_timestamp.minutes = atoi((char *)minutes);
sms_timestamp.seconds = atoi((char *)seconds);
sms_timestamp.timezone = TIME_ZONE_CN;
LOGE("Year: %d\n", sms_timestamp.year);
LOGE("Month: %d\n", sms_timestamp.month);
LOGE("Day: %d\n", sms_timestamp.day);
LOGE("Hour: %d\n", sms_timestamp.hours);
LOGE("Minute: %d\n", sms_timestamp.minutes);
LOGE("Second: %d\n", sms_timestamp.seconds);
if(total_pack > 1 && curr_pack <= total_pack)
{
sms_user_data_head.valid = TRUE;
sms_user_data_head.total_seg = total_pack;
sms_user_data_head.cur_seg_num = curr_pack;
}
else
{
sms_user_data_head.valid = FALSE;
sms_user_data_head.total_seg = 0;
sms_user_data_head.cur_seg_num = 0;
}
if(sms_handle->recv_cb)
{
sms_handle->recv_cb(&sms_msg, &sms_timestamp, &sms_user_data_head);
}
}
static void ql_sms_server_change_cb(const void* data, int data_len)
{
if(data)
{
mbtk_ril_ser_state_enum server_state = *(mbtk_ril_ser_state_enum *)data;
if(sms_handle->server_cb && server_state == MBTK_RIL_SER_STATE_EXIT)
{
sms_handle->server_cb(QL_ERR_ABORTED);
}
}
}
/*-----------------------------------------------------------------------------------------------*/
/**
@brief Initializes SMS service.
@return Whether the SMS service was initialized successfully.
@retval QL_ERR_OK successful.
@retval QL_ERR_UNKNOWN unknown error, failed to connect to service.
@retval QL_ERR_SERVICE_NOT_READY service is not ready, need to retry.
@retval Other error code defined by ql_type.h.
*/
/*-----------------------------------------------------------------------------------------------*/
int ql_sms_init(void)
{
if(NULL == sms_handle)
{
sms_handle = (ql_sms_info_handle_t*)malloc(sizeof(ql_sms_info_handle_t));
if(NULL == sms_handle)
{
LOGE("[ql_sms_init] sms handle malloc fail.");
return QL_ERR_FAILED;
}
sms_handle->handle = mbtk_ril_open(MBTK_AT_PORT_DEF);
if(NULL == sms_handle->handle)
{
LOGE("[ql_sms_init] mbtk handle init fail.");
if(sms_handle)
{
free(sms_handle);
sms_handle = NULL;
return QL_ERR_FAILED;
}
}
int err = mbtk_ds_sms_cnmi_set(sms_handle->handle,MBTK_SIM_1);
if(err)
{
LOGE("set cnmi fail");
return QL_ERR_FAILED;
}
err = mbtk_ds_sms_cnmi_set(sms_handle->handle,MBTK_SIM_2);
if(err)
{
LOGE("set cnmi fail");
return QL_ERR_FAILED;
}
int ret = mbtk_ds_sms_state_change_cb_reg(MBTK_SIM_1,ql_sms_state_change_cb);
if(ret != MBTK_ERR_OK)
{
LOGE("[ql_sms_init] set sms state cb fail.[%d]", ret);
if(sms_handle->handle)
{
mbtk_ril_close(MBTK_AT_PORT_DEF);
sms_handle->handle = NULL;
return QL_ERR_FAILED;
}
}
ret = mbtk_ds_sms_state_change_cb_reg(MBTK_SIM_2,ql_sms_state_change_cb);
if(ret != MBTK_ERR_OK)
{
LOGE("[ql_sms_init] set sms state cb fail.[%d]", ret);
if(sms_handle->handle)
{
mbtk_ril_close(MBTK_AT_PORT_DEF);
sms_handle->handle = NULL;
return QL_ERR_FAILED;
}
}
ret = mbtk_ril_ser_state_change_cb_reg(ql_sms_server_change_cb);
if(ret != MBTK_ERR_OK)
{
LOGE("[%s] mbtk_ril_ser_state_change_cb_reg fail.[%d]", __func__, ret);
if(sms_handle->handle)
{
mbtk_ril_close(MBTK_AT_PORT_DEF);
sms_handle->handle = NULL;
return QL_ERR_FAILED;
}
}
sms_handle->recv_cb = NULL;
sms_handle->server_cb = NULL;
}
return QL_ERR_OK;
}
/*-----------------------------------------------------------------------------------------------*/
/**
@brief Deinitializes SMS service.
@return Whether the SMS service was deinitialized successfully.
@retval QL_ERR_OK successful.
@retval QL_ERR_UNKNOWN unknown error, failed to connect to service.
@retval QL_ERR_SERVICE_NOT_READY service is not ready, need to retry.
@retval Other error code defined by ql_type.h.
*/
/*-----------------------------------------------------------------------------------------------*/
int ql_sms_deinit(void)
{
if(NULL == sms_handle)
{
LOGE("[ql_sms_deinit] sms handle not init.");
return QL_ERR_NOT_INIT;
}
int ret = 0;
sms_handle->server_cb = NULL;
sms_handle->recv_cb = NULL;
if(NULL != sms_handle->handle)
{
ret = mbtk_ril_close(MBTK_AT_PORT_DEF);
if(ret != MBTK_ERR_OK)
{
LOGE("[ql_sms_deinit] mbtk handle deinit fail.[%d]", ret);
return QL_ERR_FAILED;
}
sms_handle->handle = NULL;
}
if(ret != QL_ERR_OK)
{
LOGE("[ql_sms_deinit] cb thread free deinit fail.");
return QL_ERR_FAILED;
}
free(sms_handle);
sms_handle = NULL;
return QL_ERR_OK;
}
/*-----------------------------------------------------------------------------------------------*/
/**
@brief Sets the service center address.
@param[in] addr service center address.
@param[in] len service center address length.
@return Whether the service center address was set successfully.
@retval QL_ERR_OK successful.
@retval QL_ERR_INVALID_ARG invalid argument.
@retval QL_ERR_UNKNOWN unknown error, failed to connect to service.
@retval QL_ERR_SERVICE_NOT_READY service is not ready, need to retry.
@retval Other error code defined by ql_type.h.
*/
/*-----------------------------------------------------------------------------------------------*/
int ql_sms_set_service_center_addr(char *addr, int len);
/*-----------------------------------------------------------------------------------------------*/
/**
@brief Gets the service center address.
@param[out] addr service center address.
@param[in] len service center address length.
@return Whether the service center address was successfully obtained.
@retval QL_ERR_OK successful.
@retval QL_ERR_INVALID_ARG invalid argument.
@retval QL_ERR_UNKNOWN unknown error, failed to connect to service.
@retval QL_ERR_SERVICE_NOT_READY service is not ready, need to retry.
@retval Other error code defined by ql_type.h.
*/
/*-----------------------------------------------------------------------------------------------*/
int ql_sms_get_service_center_addr(char *addr, int len);
/*-----------------------------------------------------------------------------------------------*/
/**
@brief Sends message synchronously.
@param[in] p_msg pointer to ql_sms_msg_t.
@return Whether the message was successfully sent synchronously.
@retval QL_ERR_OK successful.
@retval QL_ERR_INVALID_ARG invalid argument.
@retval QL_ERR_UNKNOWN unknown error, failed to connect to service.
@retval QL_ERR_SERVICE_NOT_READY service is not ready, need to retry.
@retval Other error code defined by ql_type.h.
*/
/*-----------------------------------------------------------------------------------------------*/
int ql_sms_send_msg(ql_sms_msg_t *p_msg);
/*-----------------------------------------------------------------------------------------------*/
/**
@brief Sends message asynchronously.
@param[in] p_msg pointer to ql_sms_msg_t
@param[out] id id for this async operation
@param[in] cb async callback
@return Whether the message was successfully sent asynchronously.
@retval QL_ERR_OK successful.
@retval QL_ERR_INVALID_ARG invalid argument.
@retval QL_ERR_UNKNOWN unknown error, failed to connect to service.
@retval QL_ERR_SERVICE_NOT_READY service is not ready, need to retry.
@retval Other error code defined by ql_type.h.
*/
/*-----------------------------------------------------------------------------------------------*/
int ql_sms_send_msg_async(ql_sms_msg_t *p_msg, int *id, ql_sms_msg_async_cb_f cb)
{
if (NULL == sms_handle)
{
LOGE("handle NULL");
return QL_ERR_FAILED;
}
if (NULL == p_msg)
{
LOGE("p_msgt NULL");
return QL_ERR_FAILED;
}
if(NULL == id)
{
LOGE("id NULL");
return QL_ERR_FAILED;
}
int8 *phone_num = NULL;
int8 *msg = NULL;
int32 msg_len;
char cmgs[MSM_NUMBER_MAX] = {0};
char resp[RES_NUM_MIN] = {0};
char smscPDU[30] = {0};
char **pdu = NULL;
char smsc[4] = {0};
char msg_e_b[HAL_SMS_CONTENT_LEN] = {0};// +1 for end of string.*2:A char array contains two elements of a string for each value
int char_set = 0;
//int lsms_flag = 0;
int err = 0;
int i = 0;
msg = (int8 *)p_msg->content;
msg_len = strlen(p_msg->content);
phone_num = (int8 *)p_msg->addr;
if (p_msg->format == 0)//7
char_set = 0;
else if (p_msg->format == 1)//8
char_set = 1;
else if (p_msg->format == 2)//UCS2
char_set = 2;
else
{
LOGE("ql_sms_send_msg_async format error");
return QL_ERR_FAILED;
}
if(strcmp((char*)msg,"") == 0 || msg[0] == '\0')
{
LOGE("ql_sms_send_msg_async msg [%s]",msg);
return QL_ERR_FAILED;
}
if(strcmp((char*)phone_num,"") == 0 || phone_num[0] == '\0')
{
LOGE("ql_sms_send_msg_async phone_num [%s]",phone_num);
return QL_ERR_FAILED;
}
kal_int32 msg_num = 0;
kal_int32 pdu_msg_len = 0;
kal_int32 status = MDAPI_RET_ERROR;
kal_int32 index = 0;
if(char_set == 1) //8bit
{
ArrayToStr((unsigned char *)msg, (unsigned int)msg_len, msg_e_b);
status = _mdapi_sms_get_msg_num(msg_e_b, char_set, &msg_num, &pdu_msg_len);
}
else //7bit usc2
{
status = _mdapi_sms_get_msg_num((char *)msg, char_set, &msg_num, &pdu_msg_len);
}
LOGE("msg_len = [%d] ,msg_num=[%d]",msg_len, msg_num);
if(status == MDAPI_RET_ERROR)
{
LOGE("get message number failed");
return QL_ERR_FAILED;
}
else
{
//allocate memery for **pdu
pdu = (char **)malloc(sizeof(char *) * msg_num);
if(pdu == NULL)
{
LOGE("allocate memory for pdu failed");
return QL_ERR_FAILED;
}
else
{
for(index = 0; index < msg_num; index++)
{
pdu[index] = (char *)malloc(sizeof(char)*MAX_PDU_SIZE);
if(pdu[index] == NULL)
{
for(i = 0; i < index; i++)
{
free(pdu[i]);
pdu[i] = NULL;
}
free(pdu);
pdu = NULL;
LOGE("allocate memory for pdu[%d] failed",index);
return QL_ERR_FAILED;
}
else
{
memset(pdu[index], 0, MAX_PDU_SIZE);
LOGE("pdu[%d} init value is: %s ",index, pdu[index]);
}
}
}
}
//allocate memory for **pdu success
if(index == msg_num)
{
if(char_set == 1)//8bit
{
smsPduEncode(smsc, (char *)phone_num, msg_e_b, char_set, smscPDU, pdu);
}
else
{
smsPduEncode(smsc, (char *)phone_num, (char *)msg, char_set, smscPDU, pdu);
}
for(index = 0; index < msg_num; index++)
{
char pdu_data[MAX_PDU_SIZE] = {0};
char *p = pdu_data;
LOGE("index:%d",index);
LOGE("smscPDU: %s, pdu: %s",smscPDU, pdu[index]);
sprintf(p, "%s",smscPDU);
LOGE("pdu_data:%s\n", pdu_data);
int sc = strlen(pdu_data);
sprintf(p+strlen(p), "%s", pdu[index]);
LOGE("pdu_data:%s", pdu_data);
int t = strlen(pdu_data);
sprintf(cmgs, "%d,%s", (t-sc)/2, pdu_data);
LOGE("cmgs:%s\n", cmgs);
memset(resp, 0, sizeof(resp));
err = mbtk_sms_cmgf_set(sms_handle->handle, 0);
if(err)
{
LOGE("cmgf set error : %d", err);
for(index = 0; index < msg_num; index++){
free(pdu[index]);
pdu[index] = NULL;
}
free(pdu);
pdu = NULL;
return QL_ERR_FAILED;
}
else
{
LOGD("cmgf set success");
}
err = mbtk_ds_sms_cmgs_set(sms_handle->handle,s_sms_slot, cmgs, resp);
if(err)
{
LOGE("cmgs send fail (%d)",err);
for(index = 0; index < msg_num; index++){
free(pdu[index]);
pdu[index] = NULL;
}
free(pdu);
pdu = NULL;
return QL_ERR_FAILED;
}
else
{
LOGD("cmgs send success, resp:%s", resp);
}
}
}
for(index = 0; index < msg_num; index++){
free(pdu[index]);
pdu[index] = NULL;
}
free(pdu);
pdu = NULL;
*id = 1;
cb(1, 1);
return QL_ERR_OK;
}
/*-----------------------------------------------------------------------------------------------*/
/**
@brief Sets SMS message reception callback hanlder.
@param[in] cb message reception callback handler.
@return Whether the message reception callback hanlder was set successfully.
@retval QL_ERR_OK successful.
@retval QL_ERR_INVALID_ARG invalid argument.
@retval QL_ERR_UNKNOWN unknown error, failed to connect to service.
@retval QL_ERR_SERVICE_NOT_READY service is not ready, need to retry.
@retval Other error code defined by ql_type.h.
*/
/*-----------------------------------------------------------------------------------------------*/
int ql_sms_set_msg_recv_cb(ql_sms_msg_recv_cb_f cb)
{
if(NULL == sms_handle)
{
LOGE("[ql_sms_set_msg_recv_cb] sms handle not init.");
return QL_ERR_NOT_INIT;
}
sms_handle->recv_cb = cb;
return QL_ERR_OK;
}
/*-----------------------------------------------------------------------------------------------*/
/**
@brief Sends PDU synchronously.
@param[in] p_pdu SMS PDU.
@return Whether the PDU was successfully sent synchronously.
@retval QL_ERR_OK successful.
@retval QL_ERR_INVALID_ARG invalid argument.
@retval QL_ERR_UNKNOWN unknown error, failed to connect to service.
@retval QL_ERR_SERVICE_NOT_READY service is not ready, need to retry.
@retval Other error code defined by ql_type.h.
*/
/*-----------------------------------------------------------------------------------------------*/
int ql_sms_send_pdu(ql_sms_pdu_t *p_pdu);
/*-----------------------------------------------------------------------------------------------*/
/**
@brief Sends PDU asynchronously.
@param[in] p_pdu sms pdu.
@param[out] id id for this async operation.
@param[in] cb async callback.
@return Whether the PDU was successfully sent asynchronously.
@retval QL_ERR_OK successful.
@retval QL_ERR_INVALID_ARG invalid argument.
@retval QL_ERR_UNKNOWN unknown error, failed to connect to service.
@retval QL_ERR_SERVICE_NOT_READY service is not ready, need to retry.
@retval Other error code defined by ql_type.h.
*/
/*-----------------------------------------------------------------------------------------------*/
int ql_sms_send_pdu_async(ql_sms_pdu_t *p_pdu, int *id, ql_sms_pdu_async_cb_f cb);
/*-----------------------------------------------------------------------------------------------*/
/**
@brief Sets SMS PDU reception callback hanlder.
@param[in] cb PDU reception callback handler.
@return Whether the PDU reception callback hanlder was set successfully.
@retval QL_ERR_OK successful.
@retval QL_ERR_INVALID_ARG invalid argument.
@retval QL_ERR_UNKNOWN unknown error, failed to connect to service.
@retval QL_ERR_SERVICE_NOT_READY service is not ready, need to retry.
@retval Other error code defined by ql_type.h.
*/
/*-----------------------------------------------------------------------------------------------*/
int ql_sms_set_pdu_recv_cb(ql_sms_pdu_recv_cb_f cb);
/*-----------------------------------------------------------------------------------------------*/
/**
@brief Registration server error callback. Currently, only if the server exits abnormally,
the callback function will be executed, and the error code is QL_ERR_ABORTED;
@param[in] cb Callback function
@return
QL_ERR_OK - successful
Other - error code defined by ql_type.h
*/
/*-----------------------------------------------------------------------------------------------*/
int ql_sms_set_service_error_cb(ql_sms_service_error_cb_f cb)
{
if(NULL == sms_handle)
{
LOGE("[ql_sms_set_service_error_cb] sms handle not init.");
return QL_ERR_NOT_INIT;
}
sms_handle->server_cb = cb;
return QL_ERR_OK;
}
/*-----------------------------------------------------------------------------------------------*/
/**
@brief Binds the current control point to a specific subscription.
@param[in] sub Subscription type.
@return Whether the subscription was successfully bound.
@retval QL_ERR_OK successful.
@retval QL_ERR_INVALID_ARG invalid argument.
@retval QL_ERR_UNKNOWN unknown error, failed to connect to service.
@retval QL_ERR_SERVICE_NOT_READY service is not ready, need to retry.
@retval Other error code defined by ql_type.h.
*/
/*-----------------------------------------------------------------------------------------------*/
int ql_sms_bind_subscription(QL_SMS_SUBSCRIPTION_E sub);