1/* Copyright (c) 2009-2013, 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
120    if (NULL == loc_eng_data.ni_notify_cb) {
121        EXIT_LOG(%s, "loc_eng_ni_init hasn't happened yet.");
122        return;
123    }
124
125    /* If busy, use default or deny */
126    if (NULL != loc_eng_ni_data_p->rawRequest)
127    {
128        /* XXX Consider sending a NO RESPONSE reply or queue the request */
129        LOC_LOGW("loc_eng_ni_request_handler, notification in progress, new NI request ignored, type: %d",
130                 notif->ni_type);
131        if (NULL != passThrough) {
132            free((void*)passThrough);
133        }
134    }
135    else {
136        /* Save request */
137        loc_eng_ni_data_p->rawRequest = (void*)passThrough;
138
139        /* Fill in notification */
140        ((GpsNiNotification*)notif)->notification_id = loc_eng_ni_data_p->reqID;
141
142        if (notif->notify_flags == GPS_NI_PRIVACY_OVERRIDE)
143        {
144            loc_eng_mute_one_session(loc_eng_data);
145        }
146
147        /* Log requestor ID and text for debugging */
148        LOC_LOGI("Notification: notif_type: %d, timeout: %d, default_resp: %d", notif->ni_type, notif->timeout, notif->default_response);
149        LOC_LOGI("              requestor_id: %s (encoding: %d)", notif->requestor_id, notif->requestor_id_encoding);
150        LOC_LOGI("              text: %s text (encoding: %d)", notif->text, notif->text_encoding);
151        if (notif->extras[0])
152        {
153            LOC_LOGI("              extras: %s", notif->extras);
154        }
155
156        /* For robustness, spawn a thread at this point to timeout to clear up the notification status, even though
157         * the OEM layer in java does not do so.
158         **/
159        loc_eng_ni_data_p->respTimeLeft = 5 + (notif->timeout != 0 ? notif->timeout : LOC_NI_NO_RESPONSE_TIME);
160        LOC_LOGI("Automatically sends 'no response' in %d seconds (to clear status)\n", loc_eng_ni_data_p->respTimeLeft);
161
162        int rc = 0;
163        rc = pthread_create(&loc_eng_ni_data_p->thread, NULL, ni_thread_proc, &loc_eng_data);
164        if (rc)
165        {
166            LOC_LOGE("Loc NI thread is not created.\n");
167        }
168        rc = pthread_detach(loc_eng_ni_data_p->thread);
169        if (rc)
170        {
171            LOC_LOGE("Loc NI thread is not detached.\n");
172        }
173
174        CALLBACK_LOG_CALLFLOW("ni_notify_cb - id", %d, notif->notification_id);
175        loc_eng_data.ni_notify_cb((GpsNiNotification*)notif);
176    }
177    EXIT_LOG(%s, VOID_RET);
178}
179
180/*===========================================================================
181
182FUNCTION ni_thread_proc
183
184===========================================================================*/
185static void* ni_thread_proc(void *args)
186{
187    ENTRY_LOG();
188
189    loc_eng_data_s_type* loc_eng_data_p = (loc_eng_data_s_type*)args;
190    loc_eng_ni_data_s_type* loc_eng_ni_data_p = &loc_eng_data_p->loc_eng_ni_data;
191    int rc = 0;          /* return code from pthread calls */
192
193    struct timeval present_time;
194    struct timespec expire_time;
195
196    LOC_LOGD("Starting Loc NI thread...\n");
197    pthread_mutex_lock(&loc_eng_ni_data_p->tLock);
198    /* Calculate absolute expire time */
199    gettimeofday(&present_time, NULL);
200    expire_time.tv_sec  = present_time.tv_sec + loc_eng_ni_data_p->respTimeLeft;
201    expire_time.tv_nsec = present_time.tv_usec * 1000;
202    LOC_LOGD("ni_thread_proc-Time out set for abs time %ld with delay %d sec\n",
203             (long) expire_time.tv_sec, loc_eng_ni_data_p->respTimeLeft );
204
205    while (!loc_eng_ni_data_p->respRecvd)
206    {
207        rc = pthread_cond_timedwait(&loc_eng_ni_data_p->tCond,
208                                    &loc_eng_ni_data_p->tLock,
209                                    &expire_time);
210        if (rc == ETIMEDOUT)
211        {
212            loc_eng_ni_data_p->resp = GPS_NI_RESPONSE_NORESP;
213            LOC_LOGD("ni_thread_proc-Thread time out after valting for specified time. Ret Val %d\n",rc );
214            break;
215        }
216    }
217    LOC_LOGD("ni_thread_proc-Java layer has sent us a user response and return value from "
218             "pthread_cond_timedwait = %d\n",rc );
219    loc_eng_ni_data_p->respRecvd = FALSE; /* Reset the user response flag for the next session*/
220
221    // adding this check to support modem restart, in which case, we need the thread
222    // to exit without calling sending data. We made sure that rawRequest is NULL in
223    // loc_eng_ni_reset_on_engine_restart()
224    LocEngAdapter* adapter = loc_eng_data_p->adapter;
225    LocEngInformNiResponse *msg = NULL;
226
227    if (NULL != loc_eng_ni_data_p->rawRequest) {
228        msg = new LocEngInformNiResponse(adapter,
229                                         loc_eng_ni_data_p->resp,
230                                         loc_eng_ni_data_p->rawRequest);
231        loc_eng_ni_data_p->rawRequest = NULL;
232    }
233    pthread_mutex_unlock(&loc_eng_ni_data_p->tLock);
234
235    loc_eng_ni_data_p->respTimeLeft = 0;
236    loc_eng_ni_data_p->reqID++;
237
238    if (NULL != msg) {
239        adapter->sendMsg(msg);
240    }
241
242    EXIT_LOG(%s, VOID_RET);
243    return NULL;
244}
245
246void loc_eng_ni_reset_on_engine_restart(loc_eng_data_s_type &loc_eng_data)
247{
248    ENTRY_LOG();
249    loc_eng_ni_data_s_type* loc_eng_ni_data_p = &loc_eng_data.loc_eng_ni_data;
250
251    if (NULL == loc_eng_data.ni_notify_cb) {
252        EXIT_LOG(%s, "loc_eng_ni_init hasn't happened yet.");
253        return;
254    }
255
256    // only if modem has requested but then died.
257    if (NULL != loc_eng_ni_data_p->rawRequest) {
258        free(loc_eng_ni_data_p->rawRequest);
259        loc_eng_ni_data_p->rawRequest = NULL;
260
261        pthread_mutex_lock(&loc_eng_ni_data_p->tLock);
262        // the goal is to wake up ni_thread_proc
263        // and let it exit.
264        loc_eng_ni_data_p->respRecvd = TRUE;
265        pthread_cond_signal(&loc_eng_ni_data_p->tCond);
266        pthread_mutex_unlock(&loc_eng_ni_data_p->tLock);
267    }
268
269    EXIT_LOG(%s, VOID_RET);
270}
271
272/*===========================================================================
273FUNCTION    loc_eng_ni_init
274
275DESCRIPTION
276   This function initializes the NI interface
277
278DEPENDENCIES
279   NONE
280
281RETURN VALUE
282   None
283
284SIDE EFFECTS
285   N/A
286
287===========================================================================*/
288void loc_eng_ni_init(loc_eng_data_s_type &loc_eng_data, GpsNiExtCallbacks *callbacks)
289{
290    ENTRY_LOG_CALLFLOW();
291
292    if(callbacks == NULL)
293        EXIT_LOG(%s, "loc_eng_ni_init: failed, cb is NULL");
294    else if (NULL == callbacks->notify_cb) {
295        EXIT_LOG(%s, "loc_eng_ni_init: failed, no cb.");
296    } else if (NULL != loc_eng_data.ni_notify_cb) {
297        EXIT_LOG(%s, "loc_eng_ni_init: already inited.");
298    } else {
299        loc_eng_ni_data_s_type* loc_eng_ni_data_p = &loc_eng_data.loc_eng_ni_data;
300        loc_eng_ni_data_p->respTimeLeft = 0;
301        loc_eng_ni_data_p->respRecvd = FALSE;
302        loc_eng_ni_data_p->rawRequest = NULL;
303        loc_eng_ni_data_p->reqID = 0;
304        pthread_cond_init(&loc_eng_ni_data_p->tCond, NULL);
305        pthread_mutex_init(&loc_eng_ni_data_p->tLock, NULL);
306
307        loc_eng_data.ni_notify_cb = callbacks->notify_cb;
308        EXIT_LOG(%s, VOID_RET);
309    }
310}
311
312/*===========================================================================
313FUNCTION    loc_eng_ni_respond
314
315DESCRIPTION
316   This function receives user response from upper layer framework
317
318DEPENDENCIES
319   NONE
320
321RETURN VALUE
322   None
323
324SIDE EFFECTS
325   N/A
326
327===========================================================================*/
328void loc_eng_ni_respond(loc_eng_data_s_type &loc_eng_data,
329                        int notif_id, GpsUserResponseType user_response)
330{
331    ENTRY_LOG_CALLFLOW();
332    loc_eng_ni_data_s_type* loc_eng_ni_data_p = &loc_eng_data.loc_eng_ni_data;
333
334    if (NULL == loc_eng_data.ni_notify_cb) {
335        EXIT_LOG(%s, "loc_eng_ni_init hasn't happened yet.");
336        return;
337    }
338
339    if (notif_id == loc_eng_ni_data_p->reqID &&
340        NULL != loc_eng_ni_data_p->rawRequest)
341    {
342        LOC_LOGI("loc_eng_ni_respond: send user response %d for notif %d", user_response, notif_id);
343        pthread_mutex_lock(&loc_eng_ni_data_p->tLock);
344        loc_eng_ni_data_p->resp = user_response;
345        loc_eng_ni_data_p->respRecvd = TRUE;
346        pthread_cond_signal(&loc_eng_ni_data_p->tCond);
347        pthread_mutex_unlock(&loc_eng_ni_data_p->tLock);
348    }
349    else {
350        LOC_LOGE("loc_eng_ni_respond: reqID %d and notif_id %d mismatch or rawRequest %p, response: %d",
351                 loc_eng_ni_data_p->reqID, notif_id, loc_eng_ni_data_p->rawRequest, user_response);
352    }
353
354    EXIT_LOG(%s, VOID_RET);
355}
356