blob: e344cb71f928adae0cf833c88579c4c204f6fd54 [file] [log] [blame]
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <sys/sysinfo.h>
#include <sys/stat.h>
#include <cutils/properties.h>
#include <paths_defs.h>
#include <limits.h>
#include "ml_utils.h"
#include "hawk_lib.h"
#include "hawk_api.h"
#include "hawk_sender.h"
#include "hawk_rild.h"
/* Global variables */
struct hawk_object g_hawk;
char hawk_dir[512];
char hawk_mount[256];
/*hawk_lib internal veriables*/
/*connection type from Eshel to Tablet default to adb*/ ;
static struct hawk_host_apis g_host_apis;
unsigned long hawk_uptime(void)
{
struct sysinfo info;
info.uptime = 0;
sysinfo(&info);
return info.uptime;
}
void hawk_set_timestamp(void)
{
struct timeval tv;
struct tm result;
size_t n;
time_t curtime;
char str[50 + 50 + 1];
memset(&tv, 0, sizeof(tv));
memset(&result, 0, sizeof(result));
gettimeofday(&tv, NULL);
curtime = tv.tv_sec;
n = strftime(str, 50, "[%H:%M:%S]", localtime_r(&curtime, &result));
snprintf(str + n, 50, "[%08lu.00]", hawk_uptime());
pthread_mutex_lock(&g_hawk.general_mutex);
ml_str2file(HAWK_UPTIME_FILE, str, strlen(str) + 1);
pthread_mutex_unlock(&g_hawk.general_mutex);
}
int hawk_get_current_mcc()
{
return ml_get_property(HAWK_PROP_MCC);
}
int hawk_get_last_cell_location(int *mnc, int *mcc)
{
static char buf[64];
if (ml_file2str(HAWK_CELL_INFO_FILE,buf,sizeof(buf),0)==NULL)
return 0;
if ((sscanf(buf,"%d;%d", mnc, mcc))==2)
return 1;
return 0;
}
/**
* void hawk_save_cell_info(const char* mcc, const char* mnc)
*
* Save network location in HAWK_CELL_INFO_FILE to be used in event data
* note: we use the file for all events, to avoid unneeded access to CP
*
* @param mnc - input - the mobile network code.
* mcc - input - the mobile contry code.
* lac - input - location area code.
* cid - input - cell ID.
* @author Yaniv Yizhak
* @version 1.0
*/
void hawk_save_cell_info(const char* mcc, const char* mnc)
{
char str[64];
ENTER;
/* Set the mrvl.hawk.mcc for the selection of the server */
property_set(HAWK_PROP_MCC, mcc);
snprintf(str, sizeof(str), "%s;%s;", mnc, mcc);
pthread_mutex_lock(&g_hawk.general_mutex);
ml_str2file(HAWK_CELL_INFO_FILE, str, strlen(str) + 1);
pthread_mutex_unlock(&g_hawk.general_mutex);
}
/* lib API functions */
/**
* hawk_lib_init
* This is the init function for the hawk librery.
* this function does all the internal initialisation and setup
* function must be called prior to using any hawk lib function.
*
* @param connection_type - mode of comunication with the hawk sender
* @return 0 for success, none zero value for faile
*/
int hawk_lib_init(enum hawk_host_connection_type connection_type)
{
int rc = 1;
int retries = 16;
char *imei_str = NULL;
struct ml_imei imei;
char *cpver_str = NULL;
struct ml_cp_ver cpver;
int mcc = 0;
char error_info[32];
if (!hawk_set_log_dir()){
ml_log_error("Can't set log dir");
strncpy(error_info, "set_log_dir_failed", sizeof(error_info));
goto out_assert;
}
hawk_set_host_connection_type(connection_type);
if(!hawk_rild_register()) {
strncpy(error_info, "rild_register_failed", sizeof(error_info));
goto out_assert;
}
g_hawk.rild_init_done = 0;
imei_str = ml_get_imei(&imei);
cpver_str = ml_get_cp_ver(&cpver);
mcc = hawk_get_current_mcc();
retries = 16;
while (!imei_str || !cpver_str || !mcc) {
if (!imei_str)
hawk_rild_get_imei();
if (!cpver_str)
ml_extract_cp_from_full_vers();
if (!mcc)
hawk_rild_get_imsi();
imei_str = ml_get_imei(&imei);
cpver_str = ml_get_cp_ver(&cpver);
mcc = hawk_get_current_mcc();
if (imei_str && cpver_str && mcc)
break;
sleep(5);
if (retries--)
continue;
ml_log_error("Unable to get IMEI, IMSI or CP version\n");
strncpy(error_info, "imei_cp_imsi_failed", sizeof(error_info));
goto out_assert;
}
ml_log_debug("IMEI = %s, MCC = %d, CP_VER = %s", imei_str, mcc, cpver_str);
if(!hawk_rild_ena_indications()) {
strncpy(error_info, "rild_indications_failed", sizeof(error_info));
goto out_assert;
}
hawk_rild_get_rat();
g_hawk.rild_init_done = 1;
return rc;
out_assert:
{
char assert_cmd[64];
sprintf(assert_cmd, "eeh -T panic hawk_%s", error_info);
ml_system_retry(1, 30, assert_cmd);
}
exit(1);
}
/**
* hawk_set_host_connection_type
*
* @param new_type the new connection mode to be used;
*/
void hawk_set_host_connection_type(enum hawk_host_connection_type new_type)
{
char type[32];
switch (new_type) {
#ifdef HAWK_ADB
case HAWK_HOST_CONNECTION_TYPE_ADB:
hawk_adb_get_host_api(&g_host_apis);
snprintf(type, sizeof(type), "ADB");
break;
#endif
case HAWK_HOST_CONNECTION_TYPE_STANDALONE:
hawk_sender_get_host_api(&g_host_apis);
snprintf(type, sizeof(type), "STANDALONE");
break;
default:
ml_log_error("hawk: error unknown connection type %d\n", new_type);
ml_system_retry(1, 30, "eeh -T panic hawk_unknown_connection_type");
exit(1);
}
ml_log_debug("hawk: host connection type was changed to %s\n", type);
}
/**
* void hawk_send_event_report(struct hawk_event_report)
* This function is used to send report in an hawk event
* to the host application (hawk sender). this indecates that an event
* is in progress and/or that new logs are ready to be sent.
* the function send based on host_connection type
*
* @param report_info a structure containing the report information
*/
void hawk_send_event_report(struct hawk_event_report *report_info)
{
if (g_host_apis.send_event_report_fptr)
g_host_apis.send_event_report_fptr(report_info);
else
ml_log_error("hawk: can't sent event to host, no function defined\n");
}
/**
* hawk_send_keep_alive_event
*
* this function create a new event (keep alive)
* and adds it to the hawk message Q
*/
void hawk_send_keep_alive_event(void)
{
struct hawk_msg *msg = NULL;
msg = hawk_alloc_msg();
def_vtype_upstr(msg->msg_info.event_type, HAWK_EVENT_KEEP_ALIVE);
def_vtype_upstr(msg->msg_info.status, HAWK_STATUS_LOGS_READY);
snprintf(msg->msg_info.description, sizeof(msg->msg_info.description), "Keep alive event");
def_vtype_upstr(msg->msg_info.initiator, HAWK_INITIATOR_AUX);
hawk_add_msg(msg);
}
/**
* hawk_str_to_msg
* This function does the translation of fifo message string and returns
* hawk message allocated and filled with the event info
* it is the responsibility of the caller to free the returned message.
*
* @param fifo_message a string of ASCII chars containing the message.
* @return hawk_msg allocated and valid hawk messege. NULL is returned in case of error.
*/
struct hawk_msg *hawk_str_to_msg(const char *fifo_message)
{
struct hawk_msg *msg = NULL;
int argc;
char sender[128], command[128], param3[128], param4[128];
memset(param3, 0, sizeof(param3));
memset(param4, 0, sizeof(param4));
ml_log_debug("Recieved msg: %s", fifo_message);
//try to read all 4 and check return value for real number of parameters
argc = sscanf(fifo_message, "%128s %128s %128s %128[^\n]", sender, command, param3, param4);
if (argc < 2 || argc > 4) { /*error in input string */
ml_log_error("failed to parse fifo message '%s' error %d\n", fifo_message, argc);
return NULL;
}
msg = hawk_alloc_msg();
if (strcmp("eeh", sender) == 0) { /*this is a message from EEH */
def_vtype_upstr(msg->msg_info.initiator, HAWK_INITIATOR_AUX);
if (strcmp("boot", command) == 0) {
snprintf(msg->msg_info.description, sizeof(msg->msg_info.description), "Boot message");
def_vtype_upstr(msg->msg_info.event_type, HAWK_EVENT_BOOT_DONE);
def_vtype_upstr(msg->msg_info.status, HAWK_STATUS_LOGS_READY);
} else if (strcmp("assert_notify", command) == 0) {
snprintf(msg->msg_info.description, sizeof(msg->msg_info.description), "Assert in progress");
def_vtype_upstr(msg->msg_info.event_type, HAWK_EVENT_ASSERT);
def_vtype_upstr(msg->msg_info.status, HAWK_STATUS_IN_PROGRESS);
} else if (strcmp("assert_ready", command) == 0) {
if (argc != 3)
goto exit_error;
snprintf(msg->msg_info.description, sizeof(msg->msg_info.description), "Assert done");
def_vtype_upstr(msg->msg_info.event_type, HAWK_EVENT_ASSERT);
def_vtype_upstr(msg->msg_info.status, HAWK_STATUS_LOGS_READY);
snprintf(msg->msg_info.event_id, sizeof(msg->msg_info.event_id), "%s", param3);
} else if (strcmp("assert_detected", command) == 0) {
if (argc != 3)
goto exit_error;
hawk_create_file(atoi(param3));
snprintf(msg->msg_info.description, sizeof(msg->msg_info.description), "Assert detected");
def_vtype_upstr(msg->msg_info.event_type, HAWK_EVENT_ASSERT_DETECTED);
def_vtype_upstr(msg->msg_info.status, HAWK_STATUS_IN_PROGRESS);
} else if (strcmp("sr_notify", command) == 0) {
snprintf(msg->msg_info.description, sizeof(msg->msg_info.description),
"silent reset in progress");
def_vtype_upstr(msg->msg_info.event_type, HAWK_EVENT_SILENT_RESET);
def_vtype_upstr(msg->msg_info.status, HAWK_STATUS_IN_PROGRESS);
} else if (strcmp("sr_ready", command) == 0) {
if (argc != 3)
goto exit_error;
snprintf(msg->msg_info.description, sizeof(msg->msg_info.description), "Silent reset done");
def_vtype_upstr(msg->msg_info.event_type, HAWK_EVENT_SILENT_RESET);
def_vtype_upstr(msg->msg_info.status, HAWK_STATUS_LOGS_READY);
msg->msg_info.include_sd_log = 1;
snprintf(msg->msg_info.event_id, sizeof(msg->msg_info.event_id), "%s", param3);
} else if (strcmp("mtsd_ok", command) == 0) {
snprintf(msg->msg_info.description, sizeof(msg->msg_info.description), "MTSD online");
def_vtype_upstr(msg->msg_info.event_type, HAWK_EVENT_MTSD_ONLINE);
def_vtype_upstr(msg->msg_info.status, HAWK_STATUS_LOGS_READY);
msg->msg_info.include_sd_log = 1;
} else {
ml_log_error("unknown command error\n");
goto exit_error;
}
} else if (strcmp("host", sender) == 0) { /*tablet application message */
def_vtype_upstr(msg->msg_info.initiator, HAWK_INITIATOR_HOST);
def_vtype_upstr(msg->msg_info.status, HAWK_STATUS_HOST_COMMAND);
if (strcmp("do_keep_alive", command) == 0) {
def_vtype_upstr(msg->msg_info.event_type, HAWK_EVENT_KEEP_ALIVE);
} else if (strcmp("do_assert", command) == 0) {
if (argc != 3)
goto exit_error;
if (strcmp("comm", param3) == 0)
def_vtype_upstr(msg->msg_info.event_type, HAWK_EVENT_FORCE_CP_ASSERT);
else
def_vtype_upstr(msg->msg_info.event_type, HAWK_EVENT_FORCE_AP_ASSERT);
} else if (strcmp("do_reset", command) == 0) {
def_vtype_upstr(msg->msg_info.event_type, HAWK_EVENT_FORCE_RESET);
} else if (strcmp("do_fota", command) == 0) {
def_vtype_upstr(msg->msg_info.event_type, HAWK_EVENT_FOTA);
} else { /*unknown command */
ml_log_error("unknown command error\n");
goto exit_error;
}
} else { /*unknown sender */
ml_log_error("unknown sender error\n");
goto exit_error;
}
return msg;
exit_error:
ml_log_error("%s error: argc = %d, sender %s, command %s, param3 %s, param4 %s\n",
__func__, argc, sender, command, param3, param4);
free(msg);
return NULL;
}
/**
* hawk_exceute_host_command
* This function excecutes commands recived from HOST application
*
* @param report_info the message from host to exceute.
*/
void hawk_execute_host_command(struct hawk_event_report *report_info)
{
switch (report_info->event_type) {
case HAWK_EVENT_FORCE_CP_ASSERT:
ml_system_retry(1, 30, "eeh -T CPASSERT");
break;
case HAWK_EVENT_FORCE_AP_ASSERT:
ml_system_retry(1, 30, "eeh -T panic");
break;
case HAWK_EVENT_FORCE_RESET:
ml_reboot_service(0, 0, NULL);
break;
case HAWK_EVENT_KEEP_ALIVE:
hawk_send_keep_alive_event();
break;
case HAWK_EVENT_FOTA:
ml_system_retry(1, ML_SYSTEM_MAX_TIMEOUT, HAWK_SENDER_FOTA_COMMAND);
break;
default:
ml_log_error("hawk: host command type unknown\n");
}
}
/*hawk message Q functions*/
/**
* hawk_add_msg
* This function adds a new message to the end of the messages q of hawk
* message should be allocated using hawk_alloc_msg function.
*
* @see hawk_alloc_msg
* @param msg the message to add to the message Q
*/
void hawk_add_msg(struct hawk_msg *msg)
{
/* Add message to queue */
pthread_mutex_lock(&g_hawk.cond_mutex);
list_add_tail(&msg->list, &g_hawk.msgq_head);
g_hawk.q_item_count++;
pthread_cond_signal(&g_hawk.cond);
pthread_mutex_unlock(&g_hawk.cond_mutex);
}
/**
* hawk_alloc_msg
* This function allocates a new message buffer, and inits the message list
*
* @return msg the message to add to the message Q
*/
struct hawk_msg *hawk_alloc_msg(void)
{
struct hawk_msg *msg;
msg = calloc(1, sizeof(*msg));
if (!msg) {
ml_log_error("OOM '%s'\n", strerror(errno));
ml_system_retry(1, 30, "eeh -T panic hawk_alloc_msg_OOM");
exit(1);
}
INIT_LIST_HEAD(&msg->list);
return msg;
}
/**
* get_logs_free_space
* This function checks free space in hawk logs dedicated directory,
* considering max size limit
*
* @return free size number
*/
unsigned long long get_logs_free_space(void)
{
unsigned long long logs_size_limit;
unsigned long long logs_dir_size;
if (!ml_dir_update(hawk_dir))
return 0;
//Check if logs free space is limited
logs_size_limit = ml_get_ulong_long_property("ro.mrvl.hawk.logs.max_size");
if (logs_size_limit == 0) //if limit is set to 0 -> no limit, return MAX size
return ULLONG_MAX;
logs_dir_size = ml_get_dir_size(hawk_dir);
ml_log_debug("%s size=%llu\n", hawk_dir, logs_dir_size);
if (logs_size_limit < logs_dir_size)
return 0; //no space left
return (logs_size_limit - logs_dir_size); //calculate size
}
/**
* hawk_set_log_dir
* This function sets full path to hawk logs dedicated directory
* to a global variable hawk_dir
*/
int hawk_set_log_dir(void)
{
char local_path[256];
property_get("ro.hawk.logs.mount_point", hawk_mount, SDCARD_MOUNT);
property_get("ro.hawk.logs.local_path", local_path, "ut_logs");
snprintf(hawk_dir, sizeof(hawk_dir) - 1, "%s%s", hawk_mount, local_path);
if (!ml_check_path(hawk_mount, 1, 1))
return 0;
return ml_dir_update(hawk_dir);
}
#ifdef HAWK_ADB
/*Hezi - hawkHostIP is defined in hawk_adb.c*/
extern char hawkHostIP[32];
static int hawk_get_battery_info(struct hawk_battery_info *info)
{
char tbuf[64] = { 0 };
struct stat buf;
static int battery_supported = 1;
int vbut_reads = 0;
if (!battery_supported)
return -1;
if (battery_supported == 1 && stat("/sys/class/power_supply/battery/capacity", &buf) != 0) {
ml_log_error("Battery capacity & status unsupported\n");
battery_supported = 0;
return -1;
}
battery_supported = 2;
memset(info->status, 0, sizeof(info->status));
if (ml_file2str("/sys/class/power_supply/battery/capacity", tbuf, sizeof(tbuf), 0) == NULL)
return -1;
info->capacity = atoi(tbuf);
if (ml_file2str("/sys/class/power_supply/battery/status", info->status, sizeof(info->status), 0) == NULL)
return -1;
ml_chomp(info->status);
do {
if (vbut_reads++) {
ml_log_error("Retry VBUT read - seems we have low voltage. (%d / %d)", info->voltage_now,
HAWK_HALT_ON_LOW_VOLTAGE);
sleep(1);
}
if (ml_file2str("/sys/class/power_supply/battery/voltage_now", tbuf, sizeof(tbuf), 0) == NULL)
return -1;
info->voltage_now = atoi(tbuf);
} while (info->voltage_now < HAWK_HALT_ON_LOW_VOLTAGE && vbut_reads < 4);
return 0;
}
/**
* hawk_send_battery_status
* this function sends battery indication to the host
*
* @author Chen Reichbach
* @version 1.1
*/
static void hawk_send_battery_status(void)
{
struct hawk_battery_info info;
if (hawk_get_battery_info(&info) != 0)
return;
ml_log_error("battery capacity %d voltage %d\n", info.capacity, info.voltage_now);
if (info.voltage_now < HAWK_HALT_ON_LOW_VOLTAGE) {
char command[1024];
ml_log_error("battery voltage too low (capacity %d, voltage %d < %d). halt.\n",
info.capacity, info.voltage_now, HAWK_HALT_ON_LOW_VOLTAGE);
memset(command, 0, sizeof(command));
snprintf(command, sizeof(command), "ping -c 1 -W 1 -w 1 %s", hawkHostIP);
if (0 != ml_system_retry(2, 30, command)) {
ml_log_error("HALT due to low voltage and peer that not responding.\n");
sleep(1);
ml_reboot_service(0, 0, NULL);
} else {
ml_log_error("voltage low but TABLET still responding. continue.\n");
}
}
if (g_host_apis.send_battery_status_fptr)
g_host_apis.send_battery_status_fptr(&info);
else
ml_log_error("hawk: failed to sent battery update, no fuction defined\n");
}
/**
* Prepare and send event with battery status/ capacity info
* If battery not supported - it does nothing.
*/
void hawk_send_battery_event(void)
{
struct hawk_msg *msg = hawk_alloc_msg();
def_vtype_upstr(msg->msg_info.initiator, HAWK_INITIATOR_AUX);
def_vtype_upstr(msg->msg_info.event_type, HAWK_EVENT_BATTERY_STATUS);
def_vtype_upstr(msg->msg_info.status, HAWK_STATUS_HOST_COMMAND);
snprintf(msg->msg_info.description, sizeof(msg->msg_info.description), "Battery status");
hawk_add_msg(msg);
}
#endif