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

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

好了,废话不多说,由于工作需要,我要移植一个虚拟的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中有写到

  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中会这样调用

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

  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中

  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.   
  11.     if (inst)  
  12.         snprintf(name, PATH_MAX, "%s.%s", class_id, inst);  
  13.     else  
  14.         strlcpy(name, class_id, PATH_MAX);  
  15.   
  16.     /* 
  17.      * Here we rely on the fact that calling dlopen multiple times on 
  18.      * the same .so will simply increment a refcount (and not load 
  19.      * a new copy of the library). 
  20.      * We also assume that dlopen() is thread-safe. 
  21.      */  
  22.   
  23.     /* Loop through the configuration variants looking for a module */  
  24.     for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) {  
  25.         if (i < HAL_VARIANT_KEYS_COUNT) {  
  26.             if (property_get(variant_keys[i], prop, NULL) == 0) {  
  27.                 continue;  
  28.             }  
  29.             snprintf(path, sizeof(path), "%s/%s.%s.so",  
  30.                      HAL_LIBRARY_PATH2, name, prop);  
  31.             if (access(path, R_OK) == 0) break;  
  32.   
  33.             snprintf(path, sizeof(path), "%s/%s.%s.so",  
  34.                      HAL_LIBRARY_PATH1, name, prop);  
  35.             if (access(path, R_OK) == 0) break;  
  36.         } else {  
  37.             snprintf(path, sizeof(path), "%s/%s.default.so",  
  38.                      HAL_LIBRARY_PATH1, name);  
  39.             if (access(path, R_OK) == 0) break;  
  40.         }  
  41.     }  
  42.   
  43.     status = -ENOENT;  
  44.     if (i < HAL_VARIANT_KEYS_COUNT+1) {  
  45.         /* load the module, if this fails, we're doomed, and we should not try 
  46.          * to load a different variant. */  
  47.         status = load(class_id, path, module);  
  48.     }  
  49.   
  50.     return status;  
  51. }  
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);
    else
        strlcpy(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函数去打开这个库文件,来得到库中的函数接口

  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.     /* 
  10.      * load the symbols resolving undefined symbols before 
  11.      * dlopen returns. Since RTLD_GLOBAL is not or'd in with 
  12.      * RTLD_NOW the external symbols will not be global 
  13.      */  
  14.     handle = dlopen(path, RTLD_NOW);  
  15.     if (handle == NULL) {  
  16.         char const *err_str = dlerror();  
  17.         LOGE("load: module=%s\n%s", path, err_str?err_str:"unknown");  
  18.         status = -EINVAL;  
  19.         goto done;  
  20.     }  
  21.   
  22.     /* Get the address of the struct hal_module_info. */  
  23.     const char *sym = HAL_MODULE_INFO_SYM_AS_STR;  
  24.     hmi = (struct hw_module_t *)dlsym(handle, sym);  
  25.     if (hmi == NULL) {  
  26.         LOGE("load: couldn't find symbol %s", sym);  
  27.         status = -EINVAL;  
  28.         goto done;  
  29.     }  
  30.   
  31.     /* Check that the id matches */  
  32.     if (strcmp(id, hmi->id) != 0) {  
  33.         LOGE("load: id=%s != hmi->id=%s", id, hmi->id);  
  34.         status = -EINVAL;  
  35.         goto done;  
  36.     }  
  37.   
  38.     hmi->dso = handle;  
  39.   
  40.     /* success */  
  41.     status = 0;  
  42.   
  43.     done:  
  44.     if (status != 0) {  
  45.         hmi = NULL;  
  46.         if (handle != NULL) {  
  47.             dlclose(handle);  
  48.             handle = NULL;  
  49.         }  
  50.     } else {  
  51.         LOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",  
  52.                 id, path, *pHmi, handle);  
  53.     }  
  54.   
  55.     *pHmi = hmi;  
  56.   
  57.     return status;  
  58. }  
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模块的代码上来

之后就会调用

  1. err = module->methods->open(module, GPS_HARDWARE_MODULE_ID, &device);  
        err = module->methods->open(module, GPS_HARDWARE_MODULE_ID, &device);

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

  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.   
  7.     dev->common.tag = HARDWARE_DEVICE_TAG;  
  8.     dev->common.version = 0;  
  9.     dev->common.module = (struct hw_module_t*)module;  
  10. //    dev->common.close = (int (*)(struct hw_device_t*))close_lights;  
  11.     dev->get_gps_interface = gps__get_gps_interface;  
  12.   
  13.     *device = (struct hw_device_t*)dev;  
  14.     return 0;  
  15. }  
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;
}

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

  1. dev->get_gps_interface = gps__get_gps_interface;  
    dev->get_gps_interface = gps__get_gps_interface;

这个回调函数很简单

  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.   
  14. const GpsInterface* gps__get_gps_interface(struct gps_device_t* dev)  
  15. {  
  16.     return &qemuGpsInterface;  
  17. }  
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函数

  1. static int  
  2. qemu_gps_init(GpsCallbacks* callbacks)  
  3. {  
  4.     GpsState*  s = _gps_state;  
  5.   
  6.     if (!s->init)  
  7.         gps_state_init(s, callbacks);  
  8.   
  9.     if (s->fd < 0)  
  10.         return -1;  
  11.   
  12.     return 0;  
  13. }  
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的定义是这样的

  1. typedef struct {  
  2.     int                     init;  
  3.     int                     fd;  
  4.     GpsCallbacks            callbacks;  
  5.     pthread_t               thread;  
  6.     int                     control[2];  
  7. } GpsState;  
  8.   
  9. 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函数

  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.   
  9.     state->fd = qemud_channel_open(QEMU_CHANNEL_NAME);  
  10.   
  11.     if (state->fd < 0) {  
  12.         D("no gps emulation detected");  
  13.         return;  
  14.     }  
  15.   
  16.     D("gps emulation will read from '%s' qemud channel", QEMU_CHANNEL_NAME );  
  17.   
  18.     if ( socketpair( AF_LOCAL, SOCK_STREAM, 0, state->control ) < 0 ) {  
  19.         LOGE("could not create thread control socket pair: %s", strerror(errno));  
  20.         goto Fail;  
  21.     }  
  22.   
  23.     state->thread = callbacks->create_thread_cb( "gps_state_thread", gps_state_thread, state );  
  24.   
  25.     if ( !state->thread ) {  
  26.         LOGE("could not create gps thread: %s", strerror(errno));  
  27.         goto Fail;  
  28.     }  
  29.   
  30.     state->callbacks = *callbacks;  
  31.   
  32.     D("gps state initialized");  
  33.     return;  
  34.   
  35. Fail:  
  36.     gps_state_done( state );  
  37. }  
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]就会收到相应的指令,然后采取措施。具体代码如下

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

    D("gps thread running");

    // now loop
    for (;;) {
        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中

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值