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