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 <cutils/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.
31static 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().
73static 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.
88static 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.
109void 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
197Exit:
198  epoll_deregister(epoll_fd, control_fd);
199  epoll_deregister(epoll_fd, gps_fd);
200}
201