好久没有写技术博客了,恰逢今天还感冒了,这破天气,晚上凉风一吹,就感冒了,要加强锻炼呀。

好了,废话不多说,由于工作需要,我要移植一个虚拟的gps模块,于是乎,我就参考了Android模拟器的gps模块的实现方法,只需稍微改动就完成了我的工作了,随后我也会附上我做的模块的代码,这里主要还是来解析下模拟器上的gps模块代码吧。

相信做过android location方面应用的同志都知道,android 模拟器虽然没有真正的GPS功能,但是DDMS可以模拟GPS,通过telnet连接到adb,然后发送GPS数据,再转化成NMEA格式的信号给android系统,就可以模拟出location功能了,相信用过的童鞋都知道,没用过的同志去搜索一下就知道了,这里我就不多说了,我主要还是来分析一下这个模拟的功能是如何实现的,这里还是膜拜一下写android源码的大神们,多看看源码,学到的东西很多呢。

首先,我们直入主题,对于移植系统的人来说(比如说我),关注的是中间部分的代码,android的framework层我们需要改动的很少,最多就是加点log来调试,驱动层呢,因为模拟器没有真实的设备,也不可能利用PC上的资源区模拟,因为PC是没有GPS模块的(除非你的电脑很高级),但是我想还是可以通过网络来得到地理位置的,虽然不是非常的准确,希望google的工程师可以去完善,呵呵,题外话了。说了这么多,我就是想说,android 模拟器中gps模块的功能主要依赖于2个东西,一个是ddms中的geo fix命令,还有一个是hal层中的gps_qemu.c中作为硬件抽象层的处理,把虚拟的数据上报给framework层。

主要层次如下图

好了,思路清晰了,咱就看代码,位于源码目录下/sdk/emulator/gps/gps_qemu.c

首先我们要搞清楚,在andrroid中HAL 的一个位置问题,HAL是为了更好的封装好硬件驱动存在的,主要是一些接口,编译成库文件,给framework中国的jni来调用,我们这里的GPS模块会被编译成gps.goldfish.so文件,在同目录下的Android.mk中有写到

[cpp] view plain copy print ?
  1. LOCAL_CFLAGS += -DQEMU_HARDWARE
  2. LOCAL_SHARED_LIBRARIES := liblog libcutils libhardware
  3. LOCAL_SRC_FILES := gps_qemu.c
  4. LOCAL_MODULE := gps.goldfish
  5. LOCAL_MODULE_TAGS := debug
LOCAL_CFLAGS += -DQEMU_HARDWARE
LOCAL_SHARED_LIBRARIES := liblog libcutils libhardware
LOCAL_SRC_FILES := gps_qemu.c
LOCAL_MODULE := gps.goldfish
LOCAL_MODULE_TAGS := debug

然后呢,在jni中会这样调用

[cpp] view plain copy print ?
  1. static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, jclass clazz) {
  2. int err;
  3. hw_module_t* module;
  4. method_reportLocation = env->GetMethodID(clazz, "reportLocation", "(IDDDFFFJ)V");
  5. method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V");
  6. method_reportSvStatus = env->GetMethodID(clazz, "reportSvStatus", "()V");
  7. method_reportAGpsStatus = env->GetMethodID(clazz, "reportAGpsStatus", "(III)V");
  8. method_reportNmea = env->GetMethodID(clazz, "reportNmea", "(J)V");
  9. method_setEngineCapabilities = env->GetMethodID(clazz, "setEngineCapabilities", "(I)V");
  10. method_xtraDownloadRequest = env->GetMethodID(clazz, "xtraDownloadRequest", "()V");
  11. method_reportNiNotification = env->GetMethodID(clazz, "reportNiNotification",
  12. "(IIIIILjava/lang/String;Ljava/lang/String;IILjava/lang/String;)V");
  13. method_requestRefLocation = env->GetMethodID(clazz,"requestRefLocation","(I)V");
  14. method_requestSetID = env->GetMethodID(clazz,"requestSetID","(I)V");
  15. method_requestUtcTime = env->GetMethodID(clazz,"requestUtcTime","()V");
  16. err = hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
  17. if (err == 0) {
  18. hw_device_t* device;
  19. err = module->methods->open(module, GPS_HARDWARE_MODULE_ID, &device);
  20. if (err == 0) {
  21. gps_device_t* gps_device = (gps_device_t *)device;
  22. sGpsInterface = gps_device->get_gps_interface(gps_device);
  23. }
  24. }
  25. if (sGpsInterface) {
  26. sGpsXtraInterface =
  27. (const GpsXtraInterface*)sGpsInterface->get_extension(GPS_XTRA_INTERFACE);
  28. sAGpsInterface =
  29. (const AGpsInterface*)sGpsInterface->get_extension(AGPS_INTERFACE);
  30. sGpsNiInterface =
  31. (const GpsNiInterface*)sGpsInterface->get_extension(GPS_NI_INTERFACE);
  32. sGpsDebugInterface =
  33. (const GpsDebugInterface*)sGpsInterface->get_extension(GPS_DEBUG_INTERFACE);
  34. sAGpsRilInterface =
  35. (const AGpsRilInterface*)sGpsInterface->get_extension(AGPS_RIL_INTERFACE);
  36. }
  37. }
static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, jclass clazz) {int err;hw_module_t* module;method_reportLocation = env->GetMethodID(clazz, "reportLocation", "(IDDDFFFJ)V");method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V");method_reportSvStatus = env->GetMethodID(clazz, "reportSvStatus", "()V");method_reportAGpsStatus = env->GetMethodID(clazz, "reportAGpsStatus", "(III)V");method_reportNmea = env->GetMethodID(clazz, "reportNmea", "(J)V");method_setEngineCapabilities = env->GetMethodID(clazz, "setEngineCapabilities", "(I)V");method_xtraDownloadRequest = env->GetMethodID(clazz, "xtraDownloadRequest", "()V");method_reportNiNotification = env->GetMethodID(clazz, "reportNiNotification","(IIIIILjava/lang/String;Ljava/lang/String;IILjava/lang/String;)V");method_requestRefLocation = env->GetMethodID(clazz,"requestRefLocation","(I)V");method_requestSetID = env->GetMethodID(clazz,"requestSetID","(I)V");method_requestUtcTime = env->GetMethodID(clazz,"requestUtcTime","()V");err = hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);if (err == 0) {hw_device_t* device;err = module->methods->open(module, GPS_HARDWARE_MODULE_ID, &device);if (err == 0) {gps_device_t* gps_device = (gps_device_t *)device;sGpsInterface = gps_device->get_gps_interface(gps_device);}}if (sGpsInterface) {sGpsXtraInterface =(const GpsXtraInterface*)sGpsInterface->get_extension(GPS_XTRA_INTERFACE);sAGpsInterface =(const AGpsInterface*)sGpsInterface->get_extension(AGPS_INTERFACE);sGpsNiInterface =(const GpsNiInterface*)sGpsInterface->get_extension(GPS_NI_INTERFACE);sGpsDebugInterface =(const GpsDebugInterface*)sGpsInterface->get_extension(GPS_DEBUG_INTERFACE);sAGpsRilInterface =(const AGpsRilInterface*)sGpsInterface->get_extension(AGPS_RIL_INTERFACE);}
}

这个函数在android设备启动的时候会被调用来初始化GPS模块的一些东西,主要是来的到GPS模块的一些接口函数,重点看这个函数

[cpp] view plain copy print ?
  1. err = hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
    err = hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);

这个函数原型在HAL中的hardware.c中

[cpp] view plain copy print ?
  1. int hw_get_module_by_class(const char *class_id, const char *inst,
  2. const struct hw_module_t **module)
  3. {
  4. int status;
  5. int i;
  6. const struct hw_module_t *hmi = NULL;
  7. char prop[PATH_MAX];
  8. char path[PATH_MAX];
  9. char name[PATH_MAX];
  10. if (inst)
  11. snprintf(name, PATH_MAX, "%s.%s", class_id, inst);
  12. else
  13. strlcpy(name, class_id, PATH_MAX);
  14. /*
  15. * Here we rely on the fact that calling dlopen multiple times on
  16. * the same .so will simply increment a refcount (and not load
  17. * a new copy of the library).
  18. * We also assume that dlopen() is thread-safe.
  19. */
  20. /* Loop through the configuration variants looking for a module */
  21. for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) {
  22. if (i < HAL_VARIANT_KEYS_COUNT) {
  23. if (property_get(variant_keys[i], prop, NULL) == 0) {
  24. continue;
  25. }
  26. snprintf(path, sizeof(path), "%s/%s.%s.so",
  27. HAL_LIBRARY_PATH2, name, prop);
  28. if (access(path, R_OK) == 0) break;
  29. snprintf(path, sizeof(path), "%s/%s.%s.so",
  30. HAL_LIBRARY_PATH1, name, prop);
  31. if (access(path, R_OK) == 0) break;
  32. } else {
  33. snprintf(path, sizeof(path), "%s/%s.default.so",
  34. HAL_LIBRARY_PATH1, name);
  35. if (access(path, R_OK) == 0) break;
  36. }
  37. }
  38. status = -ENOENT;
  39. if (i < HAL_VARIANT_KEYS_COUNT+1) {
  40. /* load the module, if this fails, we're doomed, and we should not try
  41. * to load a different variant. */
  42. status = load(class_id, path, module);
  43. }
  44. return status;
  45. }
int hw_get_module_by_class(const char *class_id, const char *inst,const struct hw_module_t **module)
{int status;int i;const struct hw_module_t *hmi = NULL;char prop[PATH_MAX];char path[PATH_MAX];char name[PATH_MAX];if (inst)snprintf(name, PATH_MAX, "%s.%s", class_id, inst);elsestrlcpy(name, class_id, PATH_MAX);/** Here we rely on the fact that calling dlopen multiple times on* the same .so will simply increment a refcount (and not load* a new copy of the library).* We also assume that dlopen() is thread-safe.*//* Loop through the configuration variants looking for a module */for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) {if (i < HAL_VARIANT_KEYS_COUNT) {if (property_get(variant_keys[i], prop, NULL) == 0) {continue;}snprintf(path, sizeof(path), "%s/%s.%s.so",HAL_LIBRARY_PATH2, name, prop);if (access(path, R_OK) == 0) break;snprintf(path, sizeof(path), "%s/%s.%s.so",HAL_LIBRARY_PATH1, name, prop);if (access(path, R_OK) == 0) break;} else {snprintf(path, sizeof(path), "%s/%s.default.so",HAL_LIBRARY_PATH1, name);if (access(path, R_OK) == 0) break;}}status = -ENOENT;if (i < HAL_VARIANT_KEYS_COUNT+1) {/* load the module, if this fails, we're doomed, and we should not try* to load a different variant. */status = load(class_id, path, module);}return status;
}

当我们编译gps模块之后会在/system/lib/hw/下生成一个gps.goldfish.so文件,这个函数就是去寻找这个库文件,然后调用load函数去打开这个库文件,来得到库中的函数接口

[cpp] view plain copy print ?
  1. static int load(const char *id,
  2. const char *path,
  3. const struct hw_module_t **pHmi)
  4. {
  5. int status;
  6. void *handle;
  7. struct hw_module_t *hmi;
  8. /*
  9. * load the symbols resolving undefined symbols before
  10. * dlopen returns. Since RTLD_GLOBAL is not or'd in with
  11. * RTLD_NOW the external symbols will not be global
  12. */
  13. handle = dlopen(path, RTLD_NOW);
  14. if (handle == NULL) {
  15. char const *err_str = dlerror();
  16. LOGE("load: module=%s\n%s", path, err_str?err_str:"unknown");
  17. status = -EINVAL;
  18. goto done;
  19. }
  20. /* Get the address of the struct hal_module_info. */
  21. const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
  22. hmi = (struct hw_module_t *)dlsym(handle, sym);
  23. if (hmi == NULL) {
  24. LOGE("load: couldn't find symbol %s", sym);
  25. status = -EINVAL;
  26. goto done;
  27. }
  28. /* Check that the id matches */
  29. if (strcmp(id, hmi->id) != 0) {
  30. LOGE("load: id=%s != hmi->id=%s", id, hmi->id);
  31. status = -EINVAL;
  32. goto done;
  33. }
  34. hmi->dso = handle;
  35. /* success */
  36. status = 0;
  37. done:
  38. if (status != 0) {
  39. hmi = NULL;
  40. if (handle != NULL) {
  41. dlclose(handle);
  42. handle = NULL;
  43. }
  44. } else {
  45. LOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",
  46. id, path, *pHmi, handle);
  47. }
  48. *pHmi = hmi;
  49. return status;
  50. }
static int load(const char *id,const char *path,const struct hw_module_t **pHmi)
{int status;void *handle;struct hw_module_t *hmi;/** load the symbols resolving undefined symbols before* dlopen returns. Since RTLD_GLOBAL is not or'd in with* RTLD_NOW the external symbols will not be global*/handle = dlopen(path, RTLD_NOW);if (handle == NULL) {char const *err_str = dlerror();LOGE("load: module=%s\n%s", path, err_str?err_str:"unknown");status = -EINVAL;goto done;}/* Get the address of the struct hal_module_info. */const char *sym = HAL_MODULE_INFO_SYM_AS_STR;hmi = (struct hw_module_t *)dlsym(handle, sym);if (hmi == NULL) {LOGE("load: couldn't find symbol %s", sym);status = -EINVAL;goto done;}/* Check that the id matches */if (strcmp(id, hmi->id) != 0) {LOGE("load: id=%s != hmi->id=%s", id, hmi->id);status = -EINVAL;goto done;}hmi->dso = handle;/* success */status = 0;done:if (status != 0) {hmi = NULL;if (handle != NULL) {dlclose(handle);handle = NULL;}} else {LOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",id, path, *pHmi, handle);}*pHmi = hmi;return status;
}

这里我介绍的比较简洁,因为在我之前的博客中已经介绍过这部分的内容了,可以参考这里: http://blog.csdn.net/zhangjie201412/article/details/7225617

好了,回到我们GPS模块的代码上来

之后就会调用

[cpp] view plain copy print ?
  1. err = module->methods->open(module, GPS_HARDWARE_MODULE_ID, &device);
        err = module->methods->open(module, GPS_HARDWARE_MODULE_ID, &device);

来打开设备,来看下HAL中的代码

[cpp] view plain copy print ?
  1. static int open_gps(const struct hw_module_t* module, char const* name,
  2. struct hw_device_t** device)
  3. {
  4. struct gps_device_t *dev = malloc(sizeof(struct gps_device_t));
  5. memset(dev, 0, sizeof(*dev));
  6. dev->common.tag = HARDWARE_DEVICE_TAG;
  7. dev->common.version = 0;
  8. dev->common.module = (struct hw_module_t*)module;
  9. //    dev->common.close = (int (*)(struct hw_device_t*))close_lights;
  10. dev->get_gps_interface = gps__get_gps_interface;
  11. *device = (struct hw_device_t*)dev;
  12. return 0;
  13. }
static int open_gps(const struct hw_module_t* module, char const* name,struct hw_device_t** device)
{struct gps_device_t *dev = malloc(sizeof(struct gps_device_t));memset(dev, 0, sizeof(*dev));dev->common.tag = HARDWARE_DEVICE_TAG;dev->common.version = 0;dev->common.module = (struct hw_module_t*)module;
//    dev->common.close = (int (*)(struct hw_device_t*))close_lights;dev->get_gps_interface = gps__get_gps_interface;*device = (struct hw_device_t*)dev;return 0;
}

这里只是做了一些初始化,然后把接口函数挂钩一下

[cpp] view plain copy print ?
  1. dev->get_gps_interface = gps__get_gps_interface;
    dev->get_gps_interface = gps__get_gps_interface;

这个回调函数很简单

[cpp] view plain copy print ?
  1. static const GpsInterface  qemuGpsInterface = {
  2. sizeof(GpsInterface),
  3. qemu_gps_init,
  4. qemu_gps_start,
  5. qemu_gps_stop,
  6. qemu_gps_cleanup,
  7. qemu_gps_inject_time,
  8. qemu_gps_inject_location,
  9. qemu_gps_delete_aiding_data,
  10. qemu_gps_set_position_mode,
  11. qemu_gps_get_extension,
  12. };
  13. const GpsInterface* gps__get_gps_interface(struct gps_device_t* dev)
  14. {
  15. return &qemuGpsInterface;
  16. }
static const GpsInterface  qemuGpsInterface = {sizeof(GpsInterface),qemu_gps_init,qemu_gps_start,qemu_gps_stop,qemu_gps_cleanup,qemu_gps_inject_time,qemu_gps_inject_location,qemu_gps_delete_aiding_data,qemu_gps_set_position_mode,qemu_gps_get_extension,
};const GpsInterface* gps__get_gps_interface(struct gps_device_t* dev)
{return &qemuGpsInterface;
}

返回qemuGpsInterface结构体,这个机构提中就是一大堆的回调函数。

下面我们按照调用顺序来一个一个介绍这些回调函数。

首先就是qume_gps_init函数

[cpp] view plain copy print ?
  1. static int
  2. qemu_gps_init(GpsCallbacks* callbacks)
  3. {
  4. GpsState*  s = _gps_state;
  5. if (!s->init)
  6. gps_state_init(s, callbacks);
  7. if (s->fd < 0)
  8. return -1;
  9. return 0;
  10. }
static int
qemu_gps_init(GpsCallbacks* callbacks)
{GpsState*  s = _gps_state;if (!s->init)gps_state_init(s, callbacks);if (s->fd < 0)return -1;return 0;
}

这里我发现了一个很好玩的东西,这里这个GpsState* s是如何得到全局的实例的呢,是通过_gps_state,而_gps_state的定义是这样的

[cpp] view plain copy print ?
  1. typedef struct {
  2. int                     init;
  3. int                     fd;
  4. GpsCallbacks            callbacks;
  5. pthread_t               thread;
  6. int                     control[2];
  7. } GpsState;
  8. static GpsState  _gps_state[1];
typedef struct {int                     init;int                     fd;GpsCallbacks            callbacks;pthread_t               thread;int                     control[2];
} GpsState;static GpsState  _gps_state[1];

这里我的理解是在全局静态的定义了一个结构体指针,并分配了内存。

为何不在init函数中使用malloc来分配内存,然后使用呢,有点意思,现在还不知道有什么好处,难道只是卖弄吗?

好了,不多说了,接下去看调用的gps_state_init函数

在这之前,我来介绍下GpsState结构体中成员的作用吧

int init:

一个初始化的标志,为1表示初始化了,为0表示未初始化

int fd:

socket读写的文件描述符,如果是真实的硬件的话,应该是串口读写的描述符

callbacks:

这个是从jni传下来的回调函数,得到数据之后就回调

thread:

这个没什么好说的,就是一个线程

int control[2]:

本地使用的socket来进程间通信,会面会讲到。

继续init函数

[cpp] view plain copy print ?
  1. static void
  2. gps_state_init( GpsState*  state, GpsCallbacks* callbacks )
  3. {
  4. state->init       = 1;
  5. state->control[0] = -1;
  6. state->control[1] = -1;
  7. state->fd         = -1;
  8. state->fd = qemud_channel_open(QEMU_CHANNEL_NAME);
  9. if (state->fd < 0) {
  10. D("no gps emulation detected");
  11. return;
  12. }
  13. D("gps emulation will read from '%s' qemud channel", QEMU_CHANNEL_NAME );
  14. if ( socketpair( AF_LOCAL, SOCK_STREAM, 0, state->control ) < 0 ) {
  15. LOGE("could not create thread control socket pair: %s", strerror(errno));
  16. goto Fail;
  17. }
  18. state->thread = callbacks->create_thread_cb( "gps_state_thread", gps_state_thread, state );
  19. if ( !state->thread ) {
  20. LOGE("could not create gps thread: %s", strerror(errno));
  21. goto Fail;
  22. }
  23. state->callbacks = *callbacks;
  24. D("gps state initialized");
  25. return;
  26. Fail:
  27. gps_state_done( state );
  28. }
static void
gps_state_init( GpsState*  state, GpsCallbacks* callbacks )
{state->init       = 1;state->control[0] = -1;state->control[1] = -1;state->fd         = -1;state->fd = qemud_channel_open(QEMU_CHANNEL_NAME);if (state->fd < 0) {D("no gps emulation detected");return;}D("gps emulation will read from '%s' qemud channel", QEMU_CHANNEL_NAME );if ( socketpair( AF_LOCAL, SOCK_STREAM, 0, state->control ) < 0 ) {LOGE("could not create thread control socket pair: %s", strerror(errno));goto Fail;}state->thread = callbacks->create_thread_cb( "gps_state_thread", gps_state_thread, state );if ( !state->thread ) {LOGE("could not create gps thread: %s", strerror(errno));goto Fail;}state->callbacks = *callbacks;D("gps state initialized");return;Fail:gps_state_done( state );
}

首先书初始化赋值工作,看到没,把init变量赋值为1了。然后调用了qemud_channel_open函数来得到了adb tcp的socket文件描述符。然后调用socketpair创建本地的socket通信对来实现进程间通信,然后创建了线程,赋值回调函数,下图描述了代码执行的流程。

这图有点丑,不过大体思路还是清楚的,可以对照着代码看,这里使用的是event poll技术进行事件的处理,在线程中,把fd和control[1]加入了epoll中,设置为POLLIIN模式,当有事件发生是,就会调用相应的代码,这里的control[1],在这里做控制作用,只要是控制gps的开始和停止的,所以在线程外面对control[0]进行写操作的话,对应的control[1]就会收到相应的指令,然后采取措施。具体代码如下

[cpp] view plain copy print ?
  1. static void
  2. gps_state_thread( void*  arg )
  3. {
  4. GpsState*   state = (GpsState*) arg;
  5. NmeaReader  reader[1];
  6. int         epoll_fd   = epoll_create(2);
  7. int         started    = 0;
  8. int         gps_fd     = state->fd;
  9. int         control_fd = state->control[1];
  10. nmea_reader_init( reader );
  11. // register control file descriptors for polling
  12. epoll_register( epoll_fd, control_fd );
  13. epoll_register( epoll_fd, gps_fd );
  14. D("gps thread running");
  15. // now loop
  16. for (;;) {
  17. struct epoll_event   events[2];
  18. int                  ne, nevents;
  19. nevents = epoll_wait( epoll_fd, events, 2, -1 );
  20. if (nevents < 0) {
  21. if (errno != EINTR)
  22. LOGE("epoll_wait() unexpected error: %s", strerror(errno));
  23. continue;
  24. }
  25. D("gps thread received %d events", nevents);
  26. for (ne = 0; ne < nevents; ne++) {
  27. if ((events[ne].events & (EPOLLERR|EPOLLHUP)) != 0) {
  28. LOGE("EPOLLERR or EPOLLHUP after epoll_wait() !?");
  29. return;
  30. }
  31. if ((events[ne].events & EPOLLIN) != 0) {
  32. int  fd = events[ne].data.fd;
  33. if (fd == control_fd)
  34. {
  35. char  cmd = 255;
  36. int   ret;
  37. D("gps control fd event");
  38. do {
  39. ret = read( fd, &cmd, 1 );
  40. } while (ret < 0 && errno == EINTR);
  41. if (cmd == CMD_QUIT) {
  42. D("gps thread quitting on demand");
  43. return;
  44. }
  45. else if (cmd == CMD_START) {
  46. if (!started) {
  47. D("gps thread starting  location_cb=%p", state->callbacks.location_cb);
  48. started = 1;
  49. nmea_reader_set_callback( reader, state->callbacks.location_cb );
  50. }
  51. }
  52. else if (cmd == CMD_STOP) {
  53. if (started) {
  54. D("gps thread stopping");
  55. started = 0;
  56. nmea_reader_set_callback( reader, NULL );
  57. }
  58. }
  59. }
  60. else if (fd == gps_fd)
  61. {
  62. char  buff[32];
  63. D("gps fd event");
  64. for (;;) {
  65. int  nn, ret;
  66. ret = read( fd, buff, sizeof(buff) );
  67. if (ret < 0) {
  68. if (errno == EINTR)
  69. continue;
  70. if (errno != EWOULDBLOCK)
  71. LOGE("error while reading from gps daemon socket: %s:", strerror(errno));
  72. break;
  73. }
  74. D("received %d bytes: %.*s", ret, ret, buff);
  75. for (nn = 0; nn < ret; nn++)
  76. nmea_reader_addc( reader, buff[nn] );
  77. }
  78. D("gps fd event end");
  79. }
  80. else
  81. {
  82. LOGE("epoll_wait() returned unkown fd %d ?", fd);
  83. }
  84. }
  85. }
  86. }
  87. }
static void
gps_state_thread( void*  arg )
{GpsState*   state = (GpsState*) arg;NmeaReader  reader[1];int         epoll_fd   = epoll_create(2);int         started    = 0;int         gps_fd     = state->fd;int         control_fd = state->control[1];nmea_reader_init( reader );// register control file descriptors for pollingepoll_register( epoll_fd, control_fd );epoll_register( epoll_fd, gps_fd );D("gps thread running");// now loopfor (;;) {struct epoll_event   events[2];int                  ne, nevents;nevents = epoll_wait( epoll_fd, events, 2, -1 );if (nevents < 0) {if (errno != EINTR)LOGE("epoll_wait() unexpected error: %s", strerror(errno));continue;}D("gps thread received %d events", nevents);for (ne = 0; ne < nevents; ne++) {if ((events[ne].events & (EPOLLERR|EPOLLHUP)) != 0) {LOGE("EPOLLERR or EPOLLHUP after epoll_wait() !?");return;}if ((events[ne].events & EPOLLIN) != 0) {int  fd = events[ne].data.fd;if (fd == control_fd){char  cmd = 255;int   ret;D("gps control fd event");do {ret = read( fd, &cmd, 1 );} while (ret < 0 && errno == EINTR);if (cmd == CMD_QUIT) {D("gps thread quitting on demand");return;}else if (cmd == CMD_START) {if (!started) {D("gps thread starting  location_cb=%p", state->callbacks.location_cb);started = 1;nmea_reader_set_callback( reader, state->callbacks.location_cb );}}else if (cmd == CMD_STOP) {if (started) {D("gps thread stopping");started = 0;nmea_reader_set_callback( reader, NULL );}}}else if (fd == gps_fd){char  buff[32];D("gps fd event");for (;;) {int  nn, ret;ret = read( fd, buff, sizeof(buff) );if (ret < 0) {if (errno == EINTR)continue;if (errno != EWOULDBLOCK)LOGE("error while reading from gps daemon socket: %s:", strerror(errno));break;}D("received %d bytes: %.*s", ret, ret, buff);for (nn = 0; nn < ret; nn++)nmea_reader_addc( reader, buff[nn] );}D("gps fd event end");}else{LOGE("epoll_wait() returned unkown fd %d ?", fd);}}}}
}

好了,android 模拟器的虚拟hal层就介绍到这边,下面来看一下geo fix命令的实现源码,我也是找了好久才找到的,在external/qemu/android/console.c中

[cpp] view plain copy print ?
  1. static int
  2. do_geo_fix( ControlClient  client, char*  args )
  3. {
  4. // GEO_SAT2 provides bug backwards compatibility.
  5. enum { GEO_LONG = 0, GEO_LAT, GEO_ALT, GEO_SAT, GEO_SAT2, NUM_GEO_PARAMS };
  6. char*   p = args;
  7. int     top_param = -1;
  8. double  params[ NUM_GEO_PARAMS ];
  9. int     n_satellites = 1;
  10. static  int     last_time = 0;
  11. static  double  last_altitude = 0.;
  12. if (!p)
  13. p = "";
  14. /* tokenize */
  15. while (*p) {
  16. char*   end;
  17. double  val = strtod( p, &end );
  18. if (end == p) {
  19. control_write( client, "KO: argument '%s' is not a number\n", p );
  20. return -1;
  21. }
  22. params[++top_param] = val;
  23. if (top_param + 1 == NUM_GEO_PARAMS)
  24. break;
  25. p = end;
  26. while (*p && (p[0] == ' ' || p[0] == '\t'))
  27. p += 1;
  28. }
  29. /* sanity check */
  30. if (top_param < GEO_LAT) {
  31. control_write( client, "KO: not enough arguments: see 'help geo fix' for details\r\n" );
  32. return -1;
  33. }
  34. /* check number of satellites, must be integer between 1 and 12 */
  35. if (top_param >= GEO_SAT) {
  36. int sat_index = (top_param >= GEO_SAT2) ? GEO_SAT2 : GEO_SAT;
  37. n_satellites = (int) params[sat_index];
  38. if (n_satellites != params[sat_index]
  39. || n_satellites < 1 || n_satellites > 12) {
  40. control_write( client, "KO: invalid number of satellites. Must be an integer between 1 and 12\r\n");
  41. return -1;
  42. }
  43. }
  44. /* generate an NMEA sentence for this fix */
  45. {
  46. STRALLOC_DEFINE(s);
  47. double   val;
  48. int      deg, min;
  49. char     hemi;
  50. /* format overview:
  51. *    time of fix      123519     12:35:19 UTC
  52. *    latitude         4807.038   48 degrees, 07.038 minutes
  53. *    north/south      N or S
  54. *    longitude        01131.000  11 degrees, 31. minutes
  55. *    east/west        E or W
  56. *    fix quality      1          standard GPS fix
  57. *    satellites       1 to 12    number of satellites being tracked
  58. *    HDOP             <dontcare> horizontal dilution
  59. *    altitude         546.       altitude above sea-level
  60. *    altitude units   M          to indicate meters
  61. *    diff             <dontcare> height of sea-level above ellipsoid
  62. *    diff units       M          to indicate meters (should be <dontcare>)
  63. *    dgps age         <dontcare> time in seconds since last DGPS fix
  64. *    dgps sid         <dontcare> DGPS station id
  65. */
  66. /* first, the time */
  67. stralloc_add_format( s, "$GPGGA,%06d", last_time );
  68. last_time ++;
  69. /* then the latitude */
  70. hemi = 'N';
  71. val  = params[GEO_LAT];
  72. if (val < 0) {
  73. hemi = 'S';
  74. val  = -val;
  75. }
  76. deg = (int) val;
  77. val = 60*(val - deg);
  78. min = (int) val;
  79. val = 10000*(val - min);
  80. stralloc_add_format( s, ",%02d%02d.%04d,%c", deg, min, (int)val, hemi );
  81. /* the longitude */
  82. hemi = 'E';
  83. val  = params[GEO_LONG];
  84. if (val < 0) {
  85. hemi = 'W';
  86. val  = -val;
  87. }
  88. deg = (int) val;
  89. val = 60*(val - deg);
  90. min = (int) val;
  91. val = 10000*(val - min);
  92. stralloc_add_format( s, ",%02d%02d.%04d,%c", deg, min, (int)val, hemi );
  93. /* bogus fix quality, satellite count and dilution */
  94. stralloc_add_format( s, ",1,%02d,", n_satellites );
  95. /* optional altitude + bogus diff */
  96. if (top_param >= GEO_ALT) {
  97. stralloc_add_format( s, ",%.1g,M,0.,M", params[GEO_ALT] );
  98. last_altitude = params[GEO_ALT];
  99. } else {
  100. stralloc_add_str( s, ",,,," );
  101. }
  102. /* bogus rest and checksum */
  103. stralloc_add_str( s, ",,,*47" );
  104. /* send it, then free */
  105. android_gps_send_nmea( stralloc_cstr(s) );
  106. stralloc_reset( s );
  107. }
  108. return 0;
  109. }
static int
do_geo_fix( ControlClient  client, char*  args )
{// GEO_SAT2 provides bug backwards compatibility.enum { GEO_LONG = 0, GEO_LAT, GEO_ALT, GEO_SAT, GEO_SAT2, NUM_GEO_PARAMS };char*   p = args;int     top_param = -1;double  params[ NUM_GEO_PARAMS ];int     n_satellites = 1;static  int     last_time = 0;static  double  last_altitude = 0.;if (!p)p = "";/* tokenize */while (*p) {char*   end;double  val = strtod( p, &end );if (end == p) {control_write( client, "KO: argument '%s' is not a number\n", p );return -1;}params[++top_param] = val;if (top_param + 1 == NUM_GEO_PARAMS)break;p = end;while (*p && (p[0] == ' ' || p[0] == '\t'))p += 1;}/* sanity check */if (top_param < GEO_LAT) {control_write( client, "KO: not enough arguments: see 'help geo fix' for details\r\n" );return -1;}/* check number of satellites, must be integer between 1 and 12 */if (top_param >= GEO_SAT) {int sat_index = (top_param >= GEO_SAT2) ? GEO_SAT2 : GEO_SAT;n_satellites = (int) params[sat_index];if (n_satellites != params[sat_index]|| n_satellites < 1 || n_satellites > 12) {control_write( client, "KO: invalid number of satellites. Must be an integer between 1 and 12\r\n");return -1;}}/* generate an NMEA sentence for this fix */{STRALLOC_DEFINE(s);double   val;int      deg, min;char     hemi;/* format overview:*    time of fix      123519     12:35:19 UTC*    latitude         4807.038   48 degrees, 07.038 minutes*    north/south      N or S*    longitude        01131.000  11 degrees, 31. minutes*    east/west        E or W*    fix quality      1          standard GPS fix*    satellites       1 to 12    number of satellites being tracked*    HDOP             <dontcare> horizontal dilution*    altitude         546.       altitude above sea-level*    altitude units   M          to indicate meters*    diff             <dontcare> height of sea-level above ellipsoid*    diff units       M          to indicate meters (should be <dontcare>)*    dgps age         <dontcare> time in seconds since last DGPS fix*    dgps sid         <dontcare> DGPS station id*//* first, the time */stralloc_add_format( s, "$GPGGA,%06d", last_time );last_time ++;/* then the latitude */hemi = 'N';val  = params[GEO_LAT];if (val < 0) {hemi = 'S';val  = -val;}deg = (int) val;val = 60*(val - deg);min = (int) val;val = 10000*(val - min);stralloc_add_format( s, ",%02d%02d.%04d,%c", deg, min, (int)val, hemi );/* the longitude */hemi = 'E';val  = params[GEO_LONG];if (val < 0) {hemi = 'W';val  = -val;}deg = (int) val;val = 60*(val - deg);min = (int) val;val = 10000*(val - min);stralloc_add_format( s, ",%02d%02d.%04d,%c", deg, min, (int)val, hemi );/* bogus fix quality, satellite count and dilution */stralloc_add_format( s, ",1,%02d,", n_satellites );/* optional altitude + bogus diff */if (top_param >= GEO_ALT) {stralloc_add_format( s, ",%.1g,M,0.,M", params[GEO_ALT] );last_altitude = params[GEO_ALT];} else {stralloc_add_str( s, ",,,," );}/* bogus rest and checksum */stralloc_add_str( s, ",,,*47" );/* send it, then free */android_gps_send_nmea( stralloc_cstr(s) );stralloc_reset( s );}return 0;
}

通过穿进去的经纬度,海拔等信息转化成NMEA格式的gps数据,然后通过socket发出去。

这部分就介绍到这里,之后会更精彩,哈哈。

希望这篇文章对读者有帮助,完全是参考android源码的,对我来说源码是最好的学习途径。

Android 驱动和系统开发 2. 解析模拟器GPS模块 (原创)相关推荐

  1. Android 驱动和系统开发 2. 解析模拟器GPS模块

    好久没有写技术博客了,恰逢今天还感冒了,这破天气,晚上凉风一吹,就感冒了,要加强锻炼呀. 好了,废话不多说,由于工作需要,我要移植一个虚拟的gps模块,于是乎,我就参考了android模拟器的gps模 ...

  2. Android 驱动和系统开发 1. 一个简单的例子(原创)

    首先,边学习边记录点自己的代码,希望看了我写的代码觉得不怎么样的,多多提出来,让我也学习学习,我一定会虚心接受大家的指导. 这里我们是来学习android 驱动和android系统框架的,这里我只针对 ...

  3. 全志t3linux驱动_全志A20GPIO驱动分析|Android驱动及系统开发交流区|研发交流|雨滴科技技术论坛 - Powered by Discuz!...

    虽然成功控制了PH17脚输出高低电平,但是我感觉什么都没有获取到(关于Linux的字符设备驱动). 打算把驱动直接编译进内核,首先看看怎样把自己的驱动添加到内核配置界面上.打开内核配置界面,在lich ...

  4. 深入解析android 5.0系统 pdf,深入解析Android 5.0系统 刘超 中文pdf_源雷技术空间

    资源名称:深入解析Android 5.0系统 刘超 中文pdf <深入解析Android 5.0系统>详细剖析了最新Android 5.0 系统主要框架的原理和具体实现.本书共24章,覆盖 ...

  5. 如何使用Arduino开发板连接NEO-6M GPS模块

    本文主要介绍如何使用Arduino开发板连接NEO-6M GPS模块,然后获取GPS数据. GPS全称全球定位系统,当你旅行时,它可以用来确定位置.时间和速度. NEO-6M GPS模块简介 NEO- ...

  6. Android震动vibrator系统开发全过程

    一.前言 本人刚学习安卓驱动开发,水平不能说菜,是根本没有水平,在这里把学习过程贴出来,跟大家一起学习交流,还望大家多多指正,转载的请标明出处. 二.android驱动介绍 安卓总体架构是在 Linu ...

  7. Android实例RSS客户端开发(2)--解析XML文件

    一 介绍完RSS之后,下面开始讲解如何解析RSS文件.因为RSS是基于XML的,所以我们就直接介绍如何解析XML文件. 解析XML的方式有很多种,大家比较熟悉的可能就是DOM解析. DOM(文件对象模 ...

  8. 知海系统开发程序解析

    知海系统开发找l88Z.6ZZ685l.知海app开发.知海商城开发.知海软件开发,知海模式开发,知海源码开发.知海开发. 第二类引用类型,就是没有重写过hashcode()和equals()方法的类 ...

  9. 学习笔记 02:关于在某宝上面购买的stm32f103c8t6最小系统开发板如何使用USBtoTTL模块烧录程序的记录

    前言 最近在学习用stm32制作,于是乎在某宝上面买了一个最小系统.我身边因为没有STLink.JLink等烧录器.无法烧录,痛苦面具的我差点就要去买一个烧录器,好在突然想起我之前搞蓝牙调试的时候有一 ...

最新文章

  1. springboot 项目将本地引用打进jar包
  2. 放眼2018年,我们对虚拟现实产品有五大疑问
  3. 5、在secureCRT中vi显色
  4. tp6中使用微信支付sdk
  5. 安徽省公务员计算机专业知识,安徽省公务员考试计算机专业知识编程题
  6. hdu 4277 USACO ORZ
  7. Linux 命令之 uname -- 显示系统/主机的相关信息
  8. 美团数据仓库-数据脱敏
  9. JAVA 基础之容器集合(Collection和Map)
  10. Spring Cloud Alibaba迁移指南(一):一行代码从 Hystrix 迁移到 Sentinel 1
  11. phpstudy 升级mysql 及MySQL服务等问题
  12. Linux常用命令及配置--简单
  13. 怎样有效降低论文的重复率?
  14. python 百度翻译破解版,亲证可行
  15. SQL Server活动监视器
  16. 解析IEC 61850通信规约
  17. 全球最大照片网站 Unsplash 开放图片检索数据集
  18. TypeError: decoding str is not supported
  19. 景深决定照相机什么特性_照相机光圈与景深的关系
  20. gird布局之容器属性justify-items与align-items

热门文章

  1. PB的KeyDown在调试或者程序触发时检测不到的问题
  2. linuxprobe第一弹
  3. 全国大学生网络安全精英赛练习题
  4. 行测-判断推理-图形推理-样式规律-数量规律-素数量-小元素特征
  5. 晓羊教育获新东方、华创资本领投近1.5亿元人民币B轮融资 打造基础教育信息化大数据新生态...
  6. dpkg安装软件流程_dpkg软件包管理
  7. 《Linux驱动:设备节点文件的创建过程》
  8. 利用python分析电商_某电商平台数据分析报告(1)
  9. 操作系统-轮转法(时间片轮转法)
  10. .NET Core是什么