1 /* Copyright (c) 2009-2014, The Linux Foundation. All rights reserved. 2 * 3 * Redistribution and use in source and binary forms, with or without 4 * modification, are permitted provided that the following conditions are 5 * met: 6 * * Redistributions of source code must retain the above copyright 7 * notice, this list of conditions and the following disclaimer. 8 * * Redistributions in binary form must reproduce the above 9 * copyright notice, this list of conditions and the following 10 * disclaimer in the documentation and/or other materials provided 11 * with the distribution. 12 * * Neither the name of The Linux Foundation, nor the names of its 13 * contributors may be used to endorse or promote products derived 14 * from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 23 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 26 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 */ 29 30 #define LOG_NDDEBUG 0 31 #define LOG_TAG "LocSvc_eng" 32 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <sys/time.h> 36 #include <pthread.h> 37 #include <errno.h> 38 #include <string.h> 39 #include <ctype.h> 40 #include <unistd.h> 41 #include <time.h> 42 #include <MsgTask.h> 43 44 #include <loc_eng.h> 45 46 #include "log_util.h" 47 #include "platform_lib_includes.h" 48 49 using namespace loc_core; 50 51 /*============================================================================= 52 * 53 * DATA DECLARATION 54 * 55 *============================================================================*/ 56 57 /*============================================================================= 58 * 59 * FUNCTION DECLARATIONS 60 * 61 *============================================================================*/ 62 static void* ni_thread_proc(void *args); 63 64 struct LocEngInformNiResponse : public LocMsg { 65 LocEngAdapter* mAdapter; 66 const GpsUserResponseType mResponse; 67 const void *mPayload; 68 inline LocEngInformNiResponse(LocEngAdapter* adapter, 69 GpsUserResponseType resp, 70 const void* data) : 71 LocMsg(), mAdapter(adapter), 72 mResponse(resp), mPayload(data) 73 { 74 locallog(); 75 } 76 inline ~LocEngInformNiResponse() 77 { 78 // this is a bit weird since mPayload is not 79 // allocated by this class. But there is no better way. 80 // mPayload actually won't be NULL here. 81 free((void*)mPayload); 82 } 83 inline virtual void proc() const 84 { 85 mAdapter->informNiResponse(mResponse, mPayload); 86 } 87 inline void locallog() const 88 { 89 LOC_LOGV("LocEngInformNiResponse - " 90 "response: %s\n mPayload: %p", 91 loc_get_ni_response_name(mResponse), 92 mPayload); 93 } 94 inline virtual void log() const 95 { 96 locallog(); 97 } 98 }; 99 100 /*=========================================================================== 101 102 FUNCTION loc_eng_ni_request_handler 103 104 DESCRIPTION 105 Displays the NI request and awaits user input. If a previous request is 106 in session, it is ignored. 107 108 RETURN VALUE 109 none 110 111 ===========================================================================*/ 112 void loc_eng_ni_request_handler(loc_eng_data_s_type &loc_eng_data, 113 const GpsNiNotification *notif, 114 const void* passThrough) 115 { 116 ENTRY_LOG(); 117 char lcs_addr[32]; // Decoded LCS address for UMTS CP NI 118 loc_eng_ni_data_s_type* loc_eng_ni_data_p = &loc_eng_data.loc_eng_ni_data; 119 loc_eng_ni_session_s_type* pSession = NULL; 120 121 if (NULL == loc_eng_data.ni_notify_cb) { 122 EXIT_LOG(%s, "loc_eng_ni_init hasn't happened yet."); 123 return; 124 } 125 126 if (notif->ni_type == GPS_NI_TYPE_EMERGENCY_SUPL) { 127 if (NULL != loc_eng_ni_data_p->sessionEs.rawRequest) { 128 LOC_LOGW("loc_eng_ni_request_handler, supl es NI in progress, new supl es NI ignored, type: %d", 129 notif->ni_type); 130 if (NULL != passThrough) { 131 free((void*)passThrough); 132 } 133 } else { 134 pSession = &loc_eng_ni_data_p->sessionEs; 135 } 136 } else { 137 if (NULL != loc_eng_ni_data_p->session.rawRequest || 138 NULL != loc_eng_ni_data_p->sessionEs.rawRequest) { 139 LOC_LOGW("loc_eng_ni_request_handler, supl NI in progress, new supl NI ignored, type: %d", 140 notif->ni_type); 141 if (NULL != passThrough) { 142 free((void*)passThrough); 143 } 144 } else { 145 pSession = &loc_eng_ni_data_p->session; 146 } 147 } 148 149 150 if (pSession) { 151 /* Save request */ 152 pSession->rawRequest = (void*)passThrough; 153 pSession->reqID = ++loc_eng_ni_data_p->reqIDCounter; 154 pSession->adapter = loc_eng_data.adapter; 155 156 /* Fill in notification */ 157 ((GpsNiNotification*)notif)->notification_id = pSession->reqID; 158 159 if (notif->notify_flags == GPS_NI_PRIVACY_OVERRIDE) 160 { 161 loc_eng_mute_one_session(loc_eng_data); 162 } 163 164 /* Log requestor ID and text for debugging */ 165 LOC_LOGI("Notification: notif_type: %d, timeout: %d, default_resp: %d", notif->ni_type, notif->timeout, notif->default_response); 166 LOC_LOGI(" requestor_id: %s (encoding: %d)", notif->requestor_id, notif->requestor_id_encoding); 167 LOC_LOGI(" text: %s text (encoding: %d)", notif->text, notif->text_encoding); 168 if (notif->extras[0]) 169 { 170 LOC_LOGI(" extras: %s", notif->extras); 171 } 172 173 /* For robustness, spawn a thread at this point to timeout to clear up the notification status, even though 174 * the OEM layer in java does not do so. 175 **/ 176 pSession->respTimeLeft = 5 + (notif->timeout != 0 ? notif->timeout : LOC_NI_NO_RESPONSE_TIME); 177 LOC_LOGI("Automatically sends 'no response' in %d seconds (to clear status)\n", pSession->respTimeLeft); 178 179 int rc = 0; 180 rc = pthread_create(&pSession->thread, NULL, ni_thread_proc, pSession); 181 if (rc) 182 { 183 LOC_LOGE("Loc NI thread is not created.\n"); 184 } 185 rc = pthread_detach(pSession->thread); 186 if (rc) 187 { 188 LOC_LOGE("Loc NI thread is not detached.\n"); 189 } 190 191 CALLBACK_LOG_CALLFLOW("ni_notify_cb - id", %d, notif->notification_id); 192 loc_eng_data.ni_notify_cb((GpsNiNotification*)notif); 193 } 194 EXIT_LOG(%s, VOID_RET); 195 } 196 197 /*=========================================================================== 198 199 FUNCTION ni_thread_proc 200 201 ===========================================================================*/ 202 static void* ni_thread_proc(void *args) 203 { 204 ENTRY_LOG(); 205 206 loc_eng_ni_session_s_type* pSession = (loc_eng_ni_session_s_type*)args; 207 int rc = 0; /* return code from pthread calls */ 208 209 struct timeval present_time; 210 struct timespec expire_time; 211 212 LOC_LOGD("Starting Loc NI thread...\n"); 213 pthread_mutex_lock(&pSession->tLock); 214 /* Calculate absolute expire time */ 215 gettimeofday(&present_time, NULL); 216 expire_time.tv_sec = present_time.tv_sec + pSession->respTimeLeft; 217 expire_time.tv_nsec = present_time.tv_usec * 1000; 218 LOC_LOGD("ni_thread_proc-Time out set for abs time %ld with delay %d sec\n", 219 (long) expire_time.tv_sec, pSession->respTimeLeft ); 220 221 while (!pSession->respRecvd) 222 { 223 rc = pthread_cond_timedwait(&pSession->tCond, 224 &pSession->tLock, 225 &expire_time); 226 if (rc == ETIMEDOUT) 227 { 228 pSession->resp = GPS_NI_RESPONSE_NORESP; 229 LOC_LOGD("ni_thread_proc-Thread time out after valting for specified time. Ret Val %d\n",rc ); 230 break; 231 } 232 } 233 LOC_LOGD("ni_thread_proc-Java layer has sent us a user response and return value from " 234 "pthread_cond_timedwait = %d\n",rc ); 235 pSession->respRecvd = FALSE; /* Reset the user response flag for the next session*/ 236 237 LOC_LOGD("pSession->resp is %d\n",pSession->resp); 238 239 // adding this check to support modem restart, in which case, we need the thread 240 // to exit without calling sending data. We made sure that rawRequest is NULL in 241 // loc_eng_ni_reset_on_engine_restart() 242 LocEngAdapter* adapter = pSession->adapter; 243 LocEngInformNiResponse *msg = NULL; 244 245 if (NULL != pSession->rawRequest) { 246 if (pSession->resp != GPS_NI_RESPONSE_IGNORE) { 247 LOC_LOGD("pSession->resp != GPS_NI_RESPONSE_IGNORE \n"); 248 msg = new LocEngInformNiResponse(adapter, 249 pSession->resp, 250 pSession->rawRequest); 251 } else { 252 LOC_LOGD("this is the ignore reply for SUPL ES\n"); 253 free(pSession->rawRequest); 254 } 255 pSession->rawRequest = NULL; 256 } 257 pthread_mutex_unlock(&pSession->tLock); 258 259 pSession->respTimeLeft = 0; 260 pSession->reqID = 0; 261 262 if (NULL != msg) { 263 LOC_LOGD("ni_thread_proc: adapter->sendMsg(msg)\n"); 264 adapter->sendMsg(msg); 265 } 266 267 EXIT_LOG(%s, VOID_RET); 268 return NULL; 269 } 270 271 void loc_eng_ni_reset_on_engine_restart(loc_eng_data_s_type &loc_eng_data) 272 { 273 ENTRY_LOG(); 274 loc_eng_ni_data_s_type* loc_eng_ni_data_p = &loc_eng_data.loc_eng_ni_data; 275 276 if (NULL == loc_eng_data.ni_notify_cb) { 277 EXIT_LOG(%s, "loc_eng_ni_init hasn't happened yet."); 278 return; 279 } 280 281 // only if modem has requested but then died. 282 if (NULL != loc_eng_ni_data_p->sessionEs.rawRequest) { 283 free(loc_eng_ni_data_p->sessionEs.rawRequest); 284 loc_eng_ni_data_p->sessionEs.rawRequest = NULL; 285 286 pthread_mutex_lock(&loc_eng_ni_data_p->sessionEs.tLock); 287 // the goal is to wake up ni_thread_proc 288 // and let it exit. 289 loc_eng_ni_data_p->sessionEs.respRecvd = TRUE; 290 pthread_cond_signal(&loc_eng_ni_data_p->sessionEs.tCond); 291 pthread_mutex_unlock(&loc_eng_ni_data_p->sessionEs.tLock); 292 } 293 294 if (NULL != loc_eng_ni_data_p->session.rawRequest) { 295 free(loc_eng_ni_data_p->session.rawRequest); 296 loc_eng_ni_data_p->session.rawRequest = NULL; 297 298 pthread_mutex_lock(&loc_eng_ni_data_p->session.tLock); 299 // the goal is to wake up ni_thread_proc 300 // and let it exit. 301 loc_eng_ni_data_p->session.respRecvd = TRUE; 302 pthread_cond_signal(&loc_eng_ni_data_p->session.tCond); 303 pthread_mutex_unlock(&loc_eng_ni_data_p->session.tLock); 304 } 305 306 EXIT_LOG(%s, VOID_RET); 307 } 308 309 /*=========================================================================== 310 FUNCTION loc_eng_ni_init 311 312 DESCRIPTION 313 This function initializes the NI interface 314 315 DEPENDENCIES 316 NONE 317 318 RETURN VALUE 319 None 320 321 SIDE EFFECTS 322 N/A 323 324 ===========================================================================*/ 325 void loc_eng_ni_init(loc_eng_data_s_type &loc_eng_data, GpsNiExtCallbacks *callbacks) 326 { 327 ENTRY_LOG_CALLFLOW(); 328 329 if(callbacks == NULL) 330 EXIT_LOG(%s, "loc_eng_ni_init: failed, cb is NULL"); 331 else if (NULL == callbacks->notify_cb) { 332 EXIT_LOG(%s, "loc_eng_ni_init: failed, no cb."); 333 } else if (NULL != loc_eng_data.ni_notify_cb) { 334 EXIT_LOG(%s, "loc_eng_ni_init: already inited."); 335 } else { 336 loc_eng_ni_data_s_type* loc_eng_ni_data_p = &loc_eng_data.loc_eng_ni_data; 337 loc_eng_ni_data_p->sessionEs.respTimeLeft = 0; 338 loc_eng_ni_data_p->sessionEs.respRecvd = FALSE; 339 loc_eng_ni_data_p->sessionEs.rawRequest = NULL; 340 loc_eng_ni_data_p->sessionEs.reqID = 0; 341 pthread_cond_init(&loc_eng_ni_data_p->sessionEs.tCond, NULL); 342 pthread_mutex_init(&loc_eng_ni_data_p->sessionEs.tLock, NULL); 343 344 loc_eng_ni_data_p->session.respTimeLeft = 0; 345 loc_eng_ni_data_p->session.respRecvd = FALSE; 346 loc_eng_ni_data_p->session.rawRequest = NULL; 347 loc_eng_ni_data_p->session.reqID = 0; 348 pthread_cond_init(&loc_eng_ni_data_p->session.tCond, NULL); 349 pthread_mutex_init(&loc_eng_ni_data_p->session.tLock, NULL); 350 351 loc_eng_data.ni_notify_cb = callbacks->notify_cb; 352 EXIT_LOG(%s, VOID_RET); 353 } 354 } 355 356 /*=========================================================================== 357 FUNCTION loc_eng_ni_respond 358 359 DESCRIPTION 360 This function receives user response from upper layer framework 361 362 DEPENDENCIES 363 NONE 364 365 RETURN VALUE 366 None 367 368 SIDE EFFECTS 369 N/A 370 371 ===========================================================================*/ 372 void loc_eng_ni_respond(loc_eng_data_s_type &loc_eng_data, 373 int notif_id, GpsUserResponseType user_response) 374 { 375 ENTRY_LOG_CALLFLOW(); 376 loc_eng_ni_data_s_type* loc_eng_ni_data_p = &loc_eng_data.loc_eng_ni_data; 377 loc_eng_ni_session_s_type* pSession = NULL; 378 379 if (NULL == loc_eng_data.ni_notify_cb) { 380 EXIT_LOG(%s, "loc_eng_ni_init hasn't happened yet."); 381 return; 382 } 383 384 if (notif_id == loc_eng_ni_data_p->sessionEs.reqID && 385 NULL != loc_eng_ni_data_p->sessionEs.rawRequest) { 386 pSession = &loc_eng_ni_data_p->sessionEs; 387 // ignore any SUPL NI non-Es session if a SUPL NI ES is accepted 388 if (user_response == GPS_NI_RESPONSE_ACCEPT && 389 NULL != loc_eng_ni_data_p->session.rawRequest) { 390 pthread_mutex_lock(&loc_eng_ni_data_p->session.tLock); 391 loc_eng_ni_data_p->session.resp = GPS_NI_RESPONSE_IGNORE; 392 loc_eng_ni_data_p->session.respRecvd = TRUE; 393 pthread_cond_signal(&loc_eng_ni_data_p->session.tCond); 394 pthread_mutex_unlock(&loc_eng_ni_data_p->session.tLock); 395 } 396 } else if (notif_id == loc_eng_ni_data_p->session.reqID && 397 NULL != loc_eng_ni_data_p->session.rawRequest) { 398 pSession = &loc_eng_ni_data_p->session; 399 } 400 401 if (pSession) { 402 LOC_LOGI("loc_eng_ni_respond: send user response %d for notif %d", user_response, notif_id); 403 pthread_mutex_lock(&pSession->tLock); 404 pSession->resp = user_response; 405 pSession->respRecvd = TRUE; 406 pthread_cond_signal(&pSession->tCond); 407 pthread_mutex_unlock(&pSession->tLock); 408 } 409 else { 410 LOC_LOGE("loc_eng_ni_respond: notif_id %d not an active session", notif_id); 411 } 412 413 EXIT_LOG(%s, VOID_RET); 414 } 415