/* 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 #include #include #include #include #include #include #include #include #include #include #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, gps_conf.SUPL_ES != 0); } 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); }