C++程序  |  201行  |  6.1 KB

/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "guest/hals/gps/gps_thread.h"

#include <errno.h>
#include <fcntl.h>
#include <math.h>
#include <pthread.h>
#include <sys/epoll.h>
#include <time.h>
#include <unistd.h>

#include <cutils/log.h>
#include <cutils/sockets.h>
#include <hardware/gps.h>

// Calls an callback function to pass received and parsed GPS data to Android.
static void reader_call_callback(GpsDataReader* r) {
  if (!r) {
    ALOGW("%s: called with r=NULL", __FUNCTION__);
    return;
  }
  if (!r->callback) {
    ALOGW("%s: no callback registered; keeping the data to send later",
          __FUNCTION__);
    return;
  }
  if (!r->fix.flags) {
    ALOGW("%s: no GPS fix", __FUNCTION__);
    return;
  }
  // Always uses current time converted to UTC time in milliseconds.
  time_t secs = time(NULL);  // seconds from 01/01/1970.
  r->fix.timestamp = (long long)secs * 1000;

#if GPS_DEBUG
  D("* Parsed GPS Data");
  if (r->fix.flags & GPS_LOCATION_HAS_LAT_LONG) {
    D(" - latitude = %g", r->fix.latitude);
    D(" - longitude = %g", r->fix.longitude);
  }
  if (r->fix.flags & GPS_LOCATION_HAS_ALTITUDE)
    D(" - altitude = %g", r->fix.altitude);
  if (r->fix.flags & GPS_LOCATION_HAS_SPEED) D(" - speed = %g", r->fix.speed);
  if (r->fix.flags & GPS_LOCATION_HAS_BEARING)
    D(" - bearing = %g", r->fix.bearing);
  if (r->fix.flags & GPS_LOCATION_HAS_ACCURACY)
    D(" - accuracy = %g", r->fix.accuracy);
  long long utc_secs = r->fix.timestamp / 1000;
  struct tm utc;
  gmtime_r((time_t*)&utc_secs, &utc);
  D(" - time = %s", asctime(&utc));
#endif

  D("Sending fix to callback %p", r->callback);
  r->callback(&r->fix);
}

// Parses data received so far and calls reader_call_callback().
static void reader_parse_message(GpsDataReader* r) {
  D("Received: '%s'", r->buffer);

  int num_read = sscanf(r->buffer, "%lf,%lf,%lf,%f,%f,%f", &r->fix.longitude,
                        &r->fix.latitude, &r->fix.altitude, &r->fix.bearing,
                        &r->fix.speed, &r->fix.accuracy);
  if (num_read != 6) {
    ALOGE("Couldn't find 6 values from the received message %s.", r->buffer);
    return;
  }
  r->fix.flags = DEFAULT_GPS_LOCATION_FLAG;
  reader_call_callback(r);
}

// Accepts a newly received string & calls reader_parse_message if '\n' is seen.
static void reader_accept_string(GpsDataReader* r, char* const str,
                                 const int len) {
  int index;
  for (index = 0; index < len; index++) {
    if (r->index >= (int)sizeof(r->buffer) - 1) {
      if (str[index] == '\n') {
        ALOGW("Message longer than buffer; new byte (%d) skipped.", str[index]);
        r->index = 0;
      }
    } else {
      r->buffer[r->index++] = str[index];
      if (str[index] == '\n') {
        r->buffer[r->index] = '\0';
        reader_parse_message(r);
        r->index = 0;
      }
    }
  }
}

// GPS state threads which communicates with control and data sockets.
void gps_state_thread(void* arg) {
  GpsState* state = (GpsState*)arg;
  GpsDataReader reader;
  int epoll_fd = epoll_create(2);
  int started = -1;
  int gps_fd = state->fd;
  int control_fd = state->control[1];

  memset(&reader, 0, sizeof(reader));
  reader.fix.size = sizeof(reader.fix);

  epoll_register(epoll_fd, control_fd);
  epoll_register(epoll_fd, gps_fd);

  while (1) {
    struct epoll_event events[2];
    int nevents, event_index;

    nevents = epoll_wait(epoll_fd, events, 2, 500);
    D("Thread received %d events", nevents);
    if (nevents < 0) {
      if (errno != EINTR)
        ALOGE("epoll_wait() unexpected error: %s", strerror(errno));
      continue;
    } else if (nevents == 0) {
      if (started == 1) {
        reader_call_callback(&reader);
      }
      continue;
    }

    for (event_index = 0; event_index < nevents; event_index++) {
      if ((events[event_index].events & (EPOLLERR | EPOLLHUP)) != 0) {
        ALOGE("EPOLLERR or EPOLLHUP after epoll_wait() !?");
        goto Exit;
      }

      if ((events[event_index].events & EPOLLIN) != 0) {
        int fd = events[event_index].data.fd;
        if (fd == control_fd) {
          unsigned char cmd = 255;
          int ret;
          do {
            ret = read(fd, &cmd, 1);
          } while (ret < 0 && errno == EINTR);

          if (cmd == CMD_STOP || cmd == CMD_QUIT) {
            if (started == 1) {
              D("Thread stopping");
              started = 0;
              reader.callback = NULL;
            }
            if (cmd == CMD_QUIT) {
              D("Thread quitting");
              goto Exit;
            }
          } else if (cmd == CMD_START) {
            if (started != 1) {
              reader.callback = state->callbacks.location_cb;
              D("Thread starting callback=%p", reader.callback);
              reader_call_callback(&reader);
              started = 1;
            }
          } else {
            ALOGE("unknown control command %d", cmd);
          }
        } else if (fd == gps_fd) {
          char buff[256];
          int ret;
          for (;;) {
            ret = read(fd, buff, sizeof(buff));
            if (ret < 0) {
              if (errno == EINTR) continue;
              if (errno != EWOULDBLOCK)
                ALOGE("error while reading from gps daemon socket: %s:",
                      strerror(errno));
              break;
            }
            D("Thread received %d bytes: %.*s", ret, ret, buff);
            reader_accept_string(&reader, buff, ret);
          }
        } else {
          ALOGE("epoll_wait() returned unknown fd %d.", fd);
        }
      }
    }
  }

Exit:
  epoll_deregister(epoll_fd, control_fd);
  epoll_deregister(epoll_fd, gps_fd);
}