Android HAL是google应厂商不希望公开源码的要求所推出的新概念。它能以封闭源码的形式提供硬件驱动模块,其目的是把Android Framework 和linux kernel隔开,让Android不至于过度以来linux kernel。HAL提供了简单的设备驱动程序接口,应用程序使用设备驱动程序与底层硬件通信
从上图看出,HAL位于linux Kernel与libraries和Android Runtime之间,也就是说HAL是底层硬件设备驱动程序提供给Framework的一个接口层,它将直接和底层的设备驱动程序挂接。因此当我们需要将Android移植到其他硬件上时,或者给Android系统添加新的硬件支持时,需要对Android的HAL层进行移植或者实现。Android HAL层实现位于源码中的路径如下:
n hardware/libhardware_legacy
n hardware/libhardware/
n hardware/ril/
HAL的实现是一个硬件抽象层的框架,其硬件设备的具体操作由对应的stub进行间接的回调。HAL框架位于如下两个文件:
n hardware/libhardware/include/hardware/hardware.h
n hardware/libhardware/hardware.c
hardware.h中定义了三个重要的结构体:
n struct hw_device_t
n struct hw_module_t
n struct hw_module_methods_t
下面分别介绍这三个重要的结构体。
结构体hw_device_t表示硬件设备,存储了各种硬件设备的公共属性和方法,其定义如下:
typedef struct hw_device_t { // 标记 HARDWARE_DEVICE_TAG */ uint32_t tag; // 版本号 for hw_device_t */ uint32_t version; // 该硬件属于哪一个module*/ struct hw_module_t* module; //padding reserved for future use */ uint32_t reserved[12]; // 关闭设备操作 int (*close)(struct hw_device_t* device); } hw_device_t;
|
如果要移植或者添加新的硬件,那么都需要实用该结构体进行注册,其中tag必须初始化,结构体hw_module_t在进行加载的时候用于判断属于哪一个module,其定义代码如下:
typedef struct hw_module_t { //tag must be initialized to HARDWARE_MODULE_TAG */ uint32_t tag; //major version number for the module */ uint16_t version_major; //minor version number of the module */ uint16_t version_minor; //module 的id,通过这个id找到相应的so文件和module*/ const char *id; // Name of this module */ const char *name; // Author/owner/implementor of the module */ const char *author; // Modules methods */ struct hw_module_methods_t* methods; // module's dso */ void* dso; // padding to 128 bytes, reserved for future use */ uint32_t reserved[32-7]; } hw_module_t; |
结构体hw_module_methods_t用于定义操作设备的方法,这里只是定义了打开设备的方法open,其定义如下:
typedef struct hw_module_methods_t { // 打开设备 */ int (*open)(const struct hw_module_t* module, const char* id, struct hw_device_t** device); } hw_module_methods_t; |
如果要执行打开设备的操作,可以使用
module->methods->open(module,GPS_HARDWARE_MUDOLE_ID,(struct hw_device_t**)device);
当需要加载module时,调用hardware.c中的hw_get_module函数获取HAL,起代码如下:
int hw_get_module(const char *id, const struct hw_module_t **module) { return hw_get_module_by_class(id, NULL, module); } |
hw_get_module_by_class():
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. //
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; } |
在上面函数中,Android系统首先在系统属性中查找硬件定义,然后通过该函数的参数id和查找到的模块路径(path)加载相应的HAL的特定模块so库文件。如果在系统属性中未定义硬件属性,则需要使用默认硬件HAL对应模块so库文件,其中property_get函数将根据定义的硬件属性配置查找对应的模块及路径,调用load函数加载。
加载了so库文件之后,就可以操作具体的硬件设备,对与不同的硬件设备,Android提供了一些接口,他们位于“hardware/libhareware/include/hardware”中。如果要自定义HAL,那么也要遵守这些已经提供的接口。
在Android系统中,关于GPS的硬件抽象层实现:
n hardware/libhardware_legacy/include/hardware_legacy/gps.h
gps.h定义了各种常量和信息,包括定位模式、状态等,并同时给JNI层调用的接口。对于GPS的具体实现,需要跟具体的GPS驱动来定,因为某些GPS设备能直接输出所需要的NMEA数据,有的则需要自己实现解析才能得到。
n GpsLocation:
typedef struct { // set to sizeof(GpsLocation) */ size_t size; //标志位. */ uint16_t flags; //维度 double latitude; //经度 double longitude; //以WSG 84坐标系统表示高度信息 double altitude; /速度 float speed; //方向 float bearing; //精确度 float accuracy; //时间戳 GpsUtcTime timestamp; } GpsLocation; |
n GpsStatus:
typedef struct {
size_t size; GpsStatusValue status; } GpsStatus; |
GpsLocation用于表示GPS的定位信息,GpsStatus表示状态信息,这些数据将和java层的GpsLocationProvider.java统一,状态信息如下:
typedef uint16_t GpsStatusValue; // IMPORTANT: Note that the following values must match // constants in GpsLocationProvider.java. //未知 #define GPS_STATUS_NONE 0 //已经开始导航 #define GPS_STATUS_SESSION_BEGIN 1 //已经停止导航 #define GPS_STATUS_SESSION_END 2 //已经通电但是设备没有导航 #define GPS_STATUS_ENGINE_ON 3 //没有通电状态 #define GPS_STATUS_ENGINE_OFF 4 |
除了以上所述还包含了一些其他的数据结构体,比如GpsSvInfo(卫星信息),GpsSvStatus(卫星状态)、GpsAidingData(帮助数据)等。下面看最重要的结构体,GpsInterface定义如下:
typedef struct { // size_t size; //初始化GPS时设置回调函数的结构体GpsCallbacks int (*init)( GpsCallbacks* callbacks ); //开始导航 int (*start)( void ); /停止导航 int (*stop)( void ); //关闭接口 void (*cleanup)( void ); //置入当前时间 int (*inject_time)(GpsUtcTime time, int64_t timeReference, int uncertainty); //置入位置信息 int (*inject_location)(double latitude, double longitude, float accuracy); //删除帮助数据 void (*delete_aiding_data)(GpsAidingData flags); //设置位置模式 int (*set_position_mode)(GpsPositionMode mode, GpsPositionRecurrence recurrence, uint32_t min_interval, uint32_t preferred_accuracy, uint32_t preferred_time);
//得到扩展信息指针 const void* (*get_extension)(const char* name); } GpsInterface; |
初始化函数init被调用的时候,会传入一个GpsCallbacks指针,它是一个包含多个回调函数的结构体,定义如下:
typedef void (* gps_location_callback)(GpsLocation* location); typedef void (* gps_status_callback)(GpsStatus* status); typedef void (* gps_sv_status_callback)(GpsSvStatus* sv_info); typedef void (* gps_nmea_callback)(GpsUtcTime timestamp, const char* nmea, int length); typedef void (* gps_set_capabilities)(uint32_t capabilities); typedef void (* gps_acquire_wakelock)(); typedef void (* gps_release_wakelock)(); typedef void (* gps_request_utc_time)(); typedef pthread_t (* gps_create_thread)(const char* name, void (*start)(void *), void* arg); typedef struct {
size_t size; gps_location_callback location_cb; gps_status_callback status_cb; gps_sv_status_callback sv_status_cb; gps_nmea_callback nmea_cb; gps_set_capabilities set_capabilities_cb; gps_acquire_wakelock acquire_wakelock_cb; gps_release_wakelock release_wakelock_cb; gps_create_thread create_thread_cb; gps_request_utc_time request_utc_time_cb; } GpsCallbacks; |
Init函数会注册这些回调函数,当触发某个状态时可以通过该回调函数像JNI层相应函数,JNI在回调到java层,完成数据的回调通知。另外,还定义了GpsXtraInterface结构体,它的作用是GPS定义的一个增强,当GPS没有搜索到卫星时候,通过网络下载数据,可以让GPS快速找到当前可以使用的卫星信息,定义如下:
typedef void (* gps_xtra_download_request)(); typedef pthread_t (* gps_create_thread)(const char* name, void (*start)(void *), void* arg); typedef struct { gps_xtra_download_request download_request_cb; gps_create_thread create_thread_cb; } GpsXtraCallbacks;
typedef struct {
size_t size; int (*init)( GpsXtraCallbacks* callbacks ); //植入XTRA数据到GPS int (*inject_xtra_data)( char* data, int length ); } GpsXtraInterface; |
其中包含了inject_xtra_data函数,它通过网络下载的Xtra数据植入GPS中,同样在init函数中设置回调结构体GpsXtraCallbacks,作为下载Xtra数据的会的回调函数。
另外,对基于CellID的基站定位进行了扩展,定义了AGPS接口结构体AGpsInterface和其回调结构其AGpsCallbacks,实现方式和Gps类似。
Gps设备结构体gps_device_t:
struct gps_device_t { struct hw_device_t common; //获取Gps硬件接口 const GpsInterface* (*get_gps_interface)(struct gps_device_t* dev); }; |
实现get_gps_interface方法在gps.c中,去实现查找具体的硬件接口,如果设备没有GPS硬件,可以模拟GPS接口,这样应用程序就不会因为没有硬件支持而挂掉。
一下代码是模拟Gps接口的一部分代码:
// GpsInterface 结构体中函数初始化 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, }; //获取Gps接口具体实现 const GpsInterface* gps__get_gps_interface(struct gps_device_t* dev) { return &qemuGpsInterface; } //打开模拟gps设备 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->get_gps_interface = gps__get_gps_interface;
*device = (struct hw_device_t*)dev; return 0; } //打开设备open函数的具体实现到open_gps static struct hw_module_methods_t gps_module_methods = { //设置open函数实现 .open = open_gps }; //初始化hw_module_t结构体 const struct hw_module_t HAL_MODULE_INFO_SYM = { .tag = HARDWARE_MODULE_TAG, .version_major = 1, .version_minor = 0, .id = GPS_HARDWARE_MODULE_ID, .name = "Goldfish GPS Module", .author = "The Android Open Source Project", .methods = &gps_module_methods, }; |
在初始化module的结构体HAL_MODULE_INFO_SYM中,设置methods为gps_module_methods,而上面又对gps_module_methods进行了初始化,里面设置了open函数了open_gps, open_gps真正实现了打开gps设备。在open_pgs函数中,设置了gps_device_t结构体的get_gps_interface方法,获取接口。
经过上面的介绍,Gps HAL层已经实现了对JNI的接口,那么JNI是如何调用的呢?具体实现在:com_android_server_location_GpsLocationProvider.cpp。
static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, jclass clazz) { int err; hw_module_t* module; 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); } } …… } |
在这个方法中,实际完成了硬件so库的加载,并获取得HAL的GPS接口。
n hw_get_module()函数是hardware.c中实现的方法,具体完成module的获取并加载so库文件。
n module->methods->open()实际上是调用到了gps.c文件中的open_gps()方法。
n 做了一个结构体的强制转换,讲hw_device_t转换为gps_device
n 调用get_gps_interface,实际上是调用gps.c中的gps__get_gps_interface,获取gps接口
接下来设置回调函数:
GpsCallbacks sGpsCallbacks = { sizeof(GpsCallbacks), location_callback, status_callback, sv_status_callback, nmea_callback, set_capabilities_callback, acquire_wakelock_callback, release_wakelock_callback, create_thread_callback, request_utc_time_callback, }; static jboolean android_location_GpsLocationProvider_init(JNIEnv* env, jobject obj) { // this must be set before calling into the HAL library if (!mCallbacksObj) mCallbacksObj = env->NewGlobalRef(obj);
// fail if the main interface fails to initialize if (!sGpsInterface || sGpsInterface->init(&sGpsCallbacks) != 0) return false; ...... return true; } |
这里先初始化了回调结构体GpsCallbacks,并且实现了回调函数,回调函数中,又将会回调到java层。
到了这里gps初始化完成,等待这java发送的调用命令。