1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "guest/hals/gps/gps_thread.h"
17 
18 #include <errno.h>
19 #include <fcntl.h>
20 #include <math.h>
21 #include <pthread.h>
22 #include <sys/epoll.h>
23 #include <time.h>
24 #include <unistd.h>
25 
26 #include <log/log.h>
27 #include <cutils/sockets.h>
28 #include <hardware/gps.h>
29 
30 // Calls an callback function to pass received and parsed GPS data to Android.
reader_call_callback(GpsDataReader * r)31 static void reader_call_callback(GpsDataReader* r) {
32   if (!r) {
33     ALOGW("%s: called with r=NULL", __FUNCTION__);
34     return;
35   }
36   if (!r->callback) {
37     ALOGW("%s: no callback registered; keeping the data to send later",
38           __FUNCTION__);
39     return;
40   }
41   if (!r->fix.flags) {
42     ALOGW("%s: no GPS fix", __FUNCTION__);
43     return;
44   }
45   // Always uses current time converted to UTC time in milliseconds.
46   time_t secs = time(NULL);  // seconds from 01/01/1970.
47   r->fix.timestamp = (long long)secs * 1000;
48 
49 #if GPS_DEBUG
50   D("* Parsed GPS Data");
51   if (r->fix.flags & GPS_LOCATION_HAS_LAT_LONG) {
52     D(" - latitude = %g", r->fix.latitude);
53     D(" - longitude = %g", r->fix.longitude);
54   }
55   if (r->fix.flags & GPS_LOCATION_HAS_ALTITUDE)
56     D(" - altitude = %g", r->fix.altitude);
57   if (r->fix.flags & GPS_LOCATION_HAS_SPEED) D(" - speed = %g", r->fix.speed);
58   if (r->fix.flags & GPS_LOCATION_HAS_BEARING)
59     D(" - bearing = %g", r->fix.bearing);
60   if (r->fix.flags & GPS_LOCATION_HAS_ACCURACY)
61     D(" - accuracy = %g", r->fix.accuracy);
62   long long utc_secs = r->fix.timestamp / 1000;
63   struct tm utc;
64   gmtime_r((time_t*)&utc_secs, &utc);
65   D(" - time = %s", asctime(&utc));
66 #endif
67 
68   D("Sending fix to callback %p", r->callback);
69   r->callback(&r->fix);
70 }
71 
72 // Parses data received so far and calls reader_call_callback().
reader_parse_message(GpsDataReader * r)73 static void reader_parse_message(GpsDataReader* r) {
74   D("Received: '%s'", r->buffer);
75 
76   int num_read = sscanf(r->buffer, "%lf,%lf,%lf,%f,%f,%f", &r->fix.longitude,
77                         &r->fix.latitude, &r->fix.altitude, &r->fix.bearing,
78                         &r->fix.speed, &r->fix.accuracy);
79   if (num_read != 6) {
80     ALOGE("Couldn't find 6 values from the received message %s.", r->buffer);
81     return;
82   }
83   r->fix.flags = DEFAULT_GPS_LOCATION_FLAG;
84   reader_call_callback(r);
85 }
86 
87 // Accepts a newly received string & calls reader_parse_message if '\n' is seen.
reader_accept_string(GpsDataReader * r,char * const str,const int len)88 static void reader_accept_string(GpsDataReader* r, char* const str,
89                                  const int len) {
90   int index;
91   for (index = 0; index < len; index++) {
92     if (r->index >= (int)sizeof(r->buffer) - 1) {
93       if (str[index] == '\n') {
94         ALOGW("Message longer than buffer; new byte (%d) skipped.", str[index]);
95         r->index = 0;
96       }
97     } else {
98       r->buffer[r->index++] = str[index];
99       if (str[index] == '\n') {
100         r->buffer[r->index] = '\0';
101         reader_parse_message(r);
102         r->index = 0;
103       }
104     }
105   }
106 }
107 
108 // GPS state threads which communicates with control and data sockets.
gps_state_thread(void * arg)109 void gps_state_thread(void* arg) {
110   GpsState* state = (GpsState*)arg;
111   GpsDataReader reader;
112   int epoll_fd = epoll_create(2);
113   int started = -1;
114   int gps_fd = state->fd;
115   int control_fd = state->control[1];
116 
117   memset(&reader, 0, sizeof(reader));
118   reader.fix.size = sizeof(reader.fix);
119 
120   epoll_register(epoll_fd, control_fd);
121   epoll_register(epoll_fd, gps_fd);
122 
123   while (1) {
124     struct epoll_event events[2];
125     int nevents, event_index;
126 
127     nevents = epoll_wait(epoll_fd, events, 2, 500);
128     D("Thread received %d events", nevents);
129     if (nevents < 0) {
130       if (errno != EINTR)
131         ALOGE("epoll_wait() unexpected error: %s", strerror(errno));
132       continue;
133     } else if (nevents == 0) {
134       if (started == 1) {
135         reader_call_callback(&reader);
136       }
137       continue;
138     }
139 
140     for (event_index = 0; event_index < nevents; event_index++) {
141       if ((events[event_index].events & (EPOLLERR | EPOLLHUP)) != 0) {
142         ALOGE("EPOLLERR or EPOLLHUP after epoll_wait() !?");
143         goto Exit;
144       }
145 
146       if ((events[event_index].events & EPOLLIN) != 0) {
147         int fd = events[event_index].data.fd;
148         if (fd == control_fd) {
149           unsigned char cmd = 255;
150           int ret;
151           do {
152             ret = read(fd, &cmd, 1);
153           } while (ret < 0 && errno == EINTR);
154 
155           if (cmd == CMD_STOP || cmd == CMD_QUIT) {
156             if (started == 1) {
157               D("Thread stopping");
158               started = 0;
159               reader.callback = NULL;
160             }
161             if (cmd == CMD_QUIT) {
162               D("Thread quitting");
163               goto Exit;
164             }
165           } else if (cmd == CMD_START) {
166             if (started != 1) {
167               reader.callback = state->callbacks.location_cb;
168               D("Thread starting callback=%p", reader.callback);
169               reader_call_callback(&reader);
170               started = 1;
171             }
172           } else {
173             ALOGE("unknown control command %d", cmd);
174           }
175         } else if (fd == gps_fd) {
176           char buff[256];
177           int ret;
178           for (;;) {
179             ret = read(fd, buff, sizeof(buff));
180             if (ret < 0) {
181               if (errno == EINTR) continue;
182               if (errno != EWOULDBLOCK)
183                 ALOGE("error while reading from gps daemon socket: %s:",
184                       strerror(errno));
185               break;
186             }
187             D("Thread received %d bytes: %.*s", ret, ret, buff);
188             reader_accept_string(&reader, buff, ret);
189           }
190         } else {
191           ALOGE("epoll_wait() returned unknown fd %d.", fd);
192         }
193       }
194     }
195   }
196 
197 Exit:
198   epoll_deregister(epoll_fd, control_fd);
199   epoll_deregister(epoll_fd, gps_fd);
200 }
201