blob: 4597b983fec625aba2ac7deb57fff7da346a46fd [file] [log] [blame]
/* Copyright (c) 2009-2014, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation, nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#define LOG_NDDEBUG 0
#define LOG_TAG "LocSvc_eng"
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <time.h>
#include <MsgTask.h>
#include <loc_eng.h>
#include "log_util.h"
#include "platform_lib_includes.h"
using namespace loc_core;
/*=============================================================================
*
* DATA DECLARATION
*
*============================================================================*/
/*=============================================================================
*
* FUNCTION DECLARATIONS
*
*============================================================================*/
static void* ni_thread_proc(void *args);
struct LocEngInformNiResponse : public LocMsg {
LocEngAdapter* mAdapter;
const GpsUserResponseType mResponse;
const void *mPayload;
inline LocEngInformNiResponse(LocEngAdapter* adapter,
GpsUserResponseType resp,
const void* data) :
LocMsg(), mAdapter(adapter),
mResponse(resp), mPayload(data)
{
locallog();
}
inline ~LocEngInformNiResponse()
{
// this is a bit weird since mPayload is not
// allocated by this class. But there is no better way.
// mPayload actually won't be NULL here.
free((void*)mPayload);
}
inline virtual void proc() const
{
mAdapter->informNiResponse(mResponse, mPayload);
}
inline void locallog() const
{
LOC_LOGV("LocEngInformNiResponse - "
"response: %s\n mPayload: %p",
loc_get_ni_response_name(mResponse),
mPayload);
}
inline virtual void log() const
{
locallog();
}
};
/*===========================================================================
FUNCTION loc_eng_ni_request_handler
DESCRIPTION
Displays the NI request and awaits user input. If a previous request is
in session, it is ignored.
RETURN VALUE
none
===========================================================================*/
void loc_eng_ni_request_handler(loc_eng_data_s_type &loc_eng_data,
const GpsNiNotification *notif,
const void* passThrough)
{
ENTRY_LOG();
char lcs_addr[32]; // Decoded LCS address for UMTS CP NI
loc_eng_ni_data_s_type* loc_eng_ni_data_p = &loc_eng_data.loc_eng_ni_data;
loc_eng_ni_session_s_type* pSession = NULL;
if (NULL == loc_eng_data.ni_notify_cb) {
EXIT_LOG(%s, "loc_eng_ni_init hasn't happened yet.");
return;
}
if (notif->ni_type == GPS_NI_TYPE_EMERGENCY_SUPL) {
if (NULL != loc_eng_ni_data_p->sessionEs.rawRequest) {
LOC_LOGW("loc_eng_ni_request_handler, supl es NI in progress, new supl es NI ignored, type: %d",
notif->ni_type);
if (NULL != passThrough) {
free((void*)passThrough);
}
} else {
pSession = &loc_eng_ni_data_p->sessionEs;
}
} else {
if (NULL != loc_eng_ni_data_p->session.rawRequest ||
NULL != loc_eng_ni_data_p->sessionEs.rawRequest) {
LOC_LOGW("loc_eng_ni_request_handler, supl NI in progress, new supl NI ignored, type: %d",
notif->ni_type);
if (NULL != passThrough) {
free((void*)passThrough);
}
} else {
pSession = &loc_eng_ni_data_p->session;
}
}
if (pSession) {
/* Save request */
pSession->rawRequest = (void*)passThrough;
pSession->reqID = ++loc_eng_ni_data_p->reqIDCounter;
pSession->adapter = loc_eng_data.adapter;
/* Fill in notification */
((GpsNiNotification*)notif)->notification_id = pSession->reqID;
if (notif->notify_flags == GPS_NI_PRIVACY_OVERRIDE)
{
loc_eng_mute_one_session(loc_eng_data);
}
/* Log requestor ID and text for debugging */
LOC_LOGI("Notification: notif_type: %d, timeout: %d, default_resp: %d", notif->ni_type, notif->timeout, notif->default_response);
LOC_LOGI(" requestor_id: %s (encoding: %d)", notif->requestor_id, notif->requestor_id_encoding);
LOC_LOGI(" text: %s text (encoding: %d)", notif->text, notif->text_encoding);
if (notif->extras[0])
{
LOC_LOGI(" extras: %s", notif->extras);
}
/* For robustness, spawn a thread at this point to timeout to clear up the notification status, even though
* the OEM layer in java does not do so.
**/
pSession->respTimeLeft = 5 + (notif->timeout != 0 ? notif->timeout : LOC_NI_NO_RESPONSE_TIME);
LOC_LOGI("Automatically sends 'no response' in %d seconds (to clear status)\n", pSession->respTimeLeft);
int rc = 0;
rc = pthread_create(&pSession->thread, NULL, ni_thread_proc, pSession);
if (rc)
{
LOC_LOGE("Loc NI thread is not created.\n");
}
rc = pthread_detach(pSession->thread);
if (rc)
{
LOC_LOGE("Loc NI thread is not detached.\n");
}
CALLBACK_LOG_CALLFLOW("ni_notify_cb - id", %d, notif->notification_id);
loc_eng_data.ni_notify_cb((GpsNiNotification*)notif);
}
EXIT_LOG(%s, VOID_RET);
}
/*===========================================================================
FUNCTION ni_thread_proc
===========================================================================*/
static void* ni_thread_proc(void *args)
{
ENTRY_LOG();
loc_eng_ni_session_s_type* pSession = (loc_eng_ni_session_s_type*)args;
int rc = 0; /* return code from pthread calls */
struct timeval present_time;
struct timespec expire_time;
LOC_LOGD("Starting Loc NI thread...\n");
pthread_mutex_lock(&pSession->tLock);
/* Calculate absolute expire time */
gettimeofday(&present_time, NULL);
expire_time.tv_sec = present_time.tv_sec + pSession->respTimeLeft;
expire_time.tv_nsec = present_time.tv_usec * 1000;
LOC_LOGD("ni_thread_proc-Time out set for abs time %ld with delay %d sec\n",
(long) expire_time.tv_sec, pSession->respTimeLeft );
while (!pSession->respRecvd)
{
rc = pthread_cond_timedwait(&pSession->tCond,
&pSession->tLock,
&expire_time);
if (rc == ETIMEDOUT)
{
pSession->resp = GPS_NI_RESPONSE_NORESP;
LOC_LOGD("ni_thread_proc-Thread time out after valting for specified time. Ret Val %d\n",rc );
break;
}
}
LOC_LOGD("ni_thread_proc-Java layer has sent us a user response and return value from "
"pthread_cond_timedwait = %d\n",rc );
pSession->respRecvd = FALSE; /* Reset the user response flag for the next session*/
LOC_LOGD("pSession->resp is %d\n",pSession->resp);
// adding this check to support modem restart, in which case, we need the thread
// to exit without calling sending data. We made sure that rawRequest is NULL in
// loc_eng_ni_reset_on_engine_restart()
LocEngAdapter* adapter = pSession->adapter;
LocEngInformNiResponse *msg = NULL;
if (NULL != pSession->rawRequest) {
if (pSession->resp != GPS_NI_RESPONSE_IGNORE) {
LOC_LOGD("pSession->resp != GPS_NI_RESPONSE_IGNORE \n");
msg = new LocEngInformNiResponse(adapter,
pSession->resp,
pSession->rawRequest);
} else {
LOC_LOGD("this is the ignore reply for SUPL ES\n");
free(pSession->rawRequest);
}
pSession->rawRequest = NULL;
}
pthread_mutex_unlock(&pSession->tLock);
pSession->respTimeLeft = 0;
pSession->reqID = 0;
if (NULL != msg) {
LOC_LOGD("ni_thread_proc: adapter->sendMsg(msg)\n");
adapter->sendMsg(msg);
}
EXIT_LOG(%s, VOID_RET);
return NULL;
}
void loc_eng_ni_reset_on_engine_restart(loc_eng_data_s_type &loc_eng_data)
{
ENTRY_LOG();
loc_eng_ni_data_s_type* loc_eng_ni_data_p = &loc_eng_data.loc_eng_ni_data;
if (NULL == loc_eng_data.ni_notify_cb) {
EXIT_LOG(%s, "loc_eng_ni_init hasn't happened yet.");
return;
}
// only if modem has requested but then died.
if (NULL != loc_eng_ni_data_p->sessionEs.rawRequest) {
free(loc_eng_ni_data_p->sessionEs.rawRequest);
loc_eng_ni_data_p->sessionEs.rawRequest = NULL;
pthread_mutex_lock(&loc_eng_ni_data_p->sessionEs.tLock);
// the goal is to wake up ni_thread_proc
// and let it exit.
loc_eng_ni_data_p->sessionEs.respRecvd = TRUE;
pthread_cond_signal(&loc_eng_ni_data_p->sessionEs.tCond);
pthread_mutex_unlock(&loc_eng_ni_data_p->sessionEs.tLock);
}
if (NULL != loc_eng_ni_data_p->session.rawRequest) {
free(loc_eng_ni_data_p->session.rawRequest);
loc_eng_ni_data_p->session.rawRequest = NULL;
pthread_mutex_lock(&loc_eng_ni_data_p->session.tLock);
// the goal is to wake up ni_thread_proc
// and let it exit.
loc_eng_ni_data_p->session.respRecvd = TRUE;
pthread_cond_signal(&loc_eng_ni_data_p->session.tCond);
pthread_mutex_unlock(&loc_eng_ni_data_p->session.tLock);
}
EXIT_LOG(%s, VOID_RET);
}
/*===========================================================================
FUNCTION loc_eng_ni_init
DESCRIPTION
This function initializes the NI interface
DEPENDENCIES
NONE
RETURN VALUE
None
SIDE EFFECTS
N/A
===========================================================================*/
void loc_eng_ni_init(loc_eng_data_s_type &loc_eng_data, GpsNiExtCallbacks *callbacks)
{
ENTRY_LOG_CALLFLOW();
if(callbacks == NULL)
EXIT_LOG(%s, "loc_eng_ni_init: failed, cb is NULL");
else if (NULL == callbacks->notify_cb) {
EXIT_LOG(%s, "loc_eng_ni_init: failed, no cb.");
} else if (NULL != loc_eng_data.ni_notify_cb) {
EXIT_LOG(%s, "loc_eng_ni_init: already inited.");
} else {
loc_eng_ni_data_s_type* loc_eng_ni_data_p = &loc_eng_data.loc_eng_ni_data;
loc_eng_ni_data_p->sessionEs.respTimeLeft = 0;
loc_eng_ni_data_p->sessionEs.respRecvd = FALSE;
loc_eng_ni_data_p->sessionEs.rawRequest = NULL;
loc_eng_ni_data_p->sessionEs.reqID = 0;
pthread_cond_init(&loc_eng_ni_data_p->sessionEs.tCond, NULL);
pthread_mutex_init(&loc_eng_ni_data_p->sessionEs.tLock, NULL);
loc_eng_ni_data_p->session.respTimeLeft = 0;
loc_eng_ni_data_p->session.respRecvd = FALSE;
loc_eng_ni_data_p->session.rawRequest = NULL;
loc_eng_ni_data_p->session.reqID = 0;
pthread_cond_init(&loc_eng_ni_data_p->session.tCond, NULL);
pthread_mutex_init(&loc_eng_ni_data_p->session.tLock, NULL);
loc_eng_data.ni_notify_cb = callbacks->notify_cb;
EXIT_LOG(%s, VOID_RET);
}
}
/*===========================================================================
FUNCTION loc_eng_ni_respond
DESCRIPTION
This function receives user response from upper layer framework
DEPENDENCIES
NONE
RETURN VALUE
None
SIDE EFFECTS
N/A
===========================================================================*/
void loc_eng_ni_respond(loc_eng_data_s_type &loc_eng_data,
int notif_id, GpsUserResponseType user_response)
{
ENTRY_LOG_CALLFLOW();
loc_eng_ni_data_s_type* loc_eng_ni_data_p = &loc_eng_data.loc_eng_ni_data;
loc_eng_ni_session_s_type* pSession = NULL;
if (NULL == loc_eng_data.ni_notify_cb) {
EXIT_LOG(%s, "loc_eng_ni_init hasn't happened yet.");
return;
}
if (notif_id == loc_eng_ni_data_p->sessionEs.reqID &&
NULL != loc_eng_ni_data_p->sessionEs.rawRequest) {
pSession = &loc_eng_ni_data_p->sessionEs;
// ignore any SUPL NI non-Es session if a SUPL NI ES is accepted
if (user_response == GPS_NI_RESPONSE_ACCEPT &&
NULL != loc_eng_ni_data_p->session.rawRequest) {
pthread_mutex_lock(&loc_eng_ni_data_p->session.tLock);
loc_eng_ni_data_p->session.resp = GPS_NI_RESPONSE_IGNORE;
loc_eng_ni_data_p->session.respRecvd = TRUE;
pthread_cond_signal(&loc_eng_ni_data_p->session.tCond);
pthread_mutex_unlock(&loc_eng_ni_data_p->session.tLock);
}
} else if (notif_id == loc_eng_ni_data_p->session.reqID &&
NULL != loc_eng_ni_data_p->session.rawRequest) {
pSession = &loc_eng_ni_data_p->session;
}
if (pSession) {
LOC_LOGI("loc_eng_ni_respond: send user response %d for notif %d", user_response, notif_id);
pthread_mutex_lock(&pSession->tLock);
pSession->resp = user_response;
pSession->respRecvd = TRUE;
pthread_cond_signal(&pSession->tCond);
pthread_mutex_unlock(&pSession->tLock);
}
else {
LOC_LOGE("loc_eng_ni_respond: notif_id %d not an active session", notif_id);
}
EXIT_LOG(%s, VOID_RET);
}