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;
LocEngInformNiResponseLocEngInformNiResponse68 inline LocEngInformNiResponse(LocEngAdapter* adapter,
69 GpsUserResponseType resp,
70 const void* data) :
71 LocMsg(), mAdapter(adapter),
72 mResponse(resp), mPayload(data)
73 {
74 locallog();
75 }
~LocEngInformNiResponseLocEngInformNiResponse76 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 }
procLocEngInformNiResponse83 inline virtual void proc() const
84 {
85 mAdapter->informNiResponse(mResponse, mPayload);
86 }
locallogLocEngInformNiResponse87 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 }
logLocEngInformNiResponse94 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 ===========================================================================*/
loc_eng_ni_request_handler(loc_eng_data_s_type & loc_eng_data,const GpsNiNotification * notif,const void * passThrough)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 ===========================================================================*/
ni_thread_proc(void * args)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
loc_eng_ni_reset_on_engine_restart(loc_eng_data_s_type & loc_eng_data)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 ===========================================================================*/
loc_eng_ni_init(loc_eng_data_s_type & loc_eng_data,GpsNiExtCallbacks * callbacks)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 ===========================================================================*/
loc_eng_ni_respond(loc_eng_data_s_type & loc_eng_data,int notif_id,GpsUserResponseType user_response)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