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
49using namespace loc_core;
50
51/*=============================================================================
52 *
53 *                             DATA DECLARATION
54 *
55 *============================================================================*/
56
57/*=============================================================================
58 *
59 *                             FUNCTION DECLARATIONS
60 *
61 *============================================================================*/
62static void* ni_thread_proc(void *args);
63
64struct 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
102FUNCTION loc_eng_ni_request_handler
103
104DESCRIPTION
105   Displays the NI request and awaits user input. If a previous request is
106   in session, it is ignored.
107
108RETURN VALUE
109   none
110
111===========================================================================*/
112void 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
199FUNCTION ni_thread_proc
200
201===========================================================================*/
202static 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
271void 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/*===========================================================================
310FUNCTION    loc_eng_ni_init
311
312DESCRIPTION
313   This function initializes the NI interface
314
315DEPENDENCIES
316   NONE
317
318RETURN VALUE
319   None
320
321SIDE EFFECTS
322   N/A
323
324===========================================================================*/
325void 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/*===========================================================================
357FUNCTION    loc_eng_ni_respond
358
359DESCRIPTION
360   This function receives user response from upper layer framework
361
362DEPENDENCIES
363   NONE
364
365RETURN VALUE
366   None
367
368SIDE EFFECTS
369   N/A
370
371===========================================================================*/
372void 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