作为开放而非开源的Android系统,由于其基于Linux内核实现,在不违背Linux基于GPL许可前提下,为了隐藏各厂家自身特定硬件驱动实现细节,在用户空间定义了一套硬件抽象层,对硬件的操作细节从内核空间转移到用户空间。各厂商在Android的硬件抽象层实现特定硬件的操作细节,并编译成动态库,以库的形式提供给用户使用。Linux内核是基于GPL许可,即对源码的修改都必须开源,而Android是基于ASL许可,即可以随意使用源码,无需开源,因此将原本应该位于Linux内核的硬件驱动逻辑转移到Android平台来,就可以不必开源,从而保护了厂家的利益。Android的硬件抽象层就是为了保障Android平台基于Linux开发的硬件驱动和应用程序不必遵循GPL许可而保持封闭。因此Android就提供了一套访问硬件抽象层动态库的接口,各厂商只需要为他们的硬件实现软件操作细节。Android系统编译的硬件抽象库存放于/system/lib/hw目录下,如下图所示:
本文简单介绍Android系统提供的硬件抽象库访问接口。
hardware\libhardware\hardware.c
int hw_get_module(const char *id, const struct hw_module_t **module) { return hw_get_module_by_class(id, NULL, module); }参数id为硬件抽象层模块ID,hw_module_t是硬件抽象层模块描述结构。
static const char *variant_keys[] = { "ro.hardware", /* This goes first so that it can pick up a different file on the emulator. */ "ro.product.board", "ro.board.platform", "ro.arch" }; static const int HAL_VARIANT_KEYS_COUNT = (sizeof(variant_keys)/sizeof(variant_keys[0])); 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); //遍历数组variant_keys //HAL_VARIANT_KEYS_COUNT =(sizeof(variant_keys)/sizeof(variant_keys[0])); 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) { //加载动态库 status = load(class_id, path, module); } return status; }函数首先遍历数组variant_keys,读取以数组元素为属性的值,即循环读取
static int load(const char *id,const char *path,const struct hw_module_t **pHmi) { int status; void *handle; struct hw_module_t *hmi; //打开so动态库 handle = dlopen(path, RTLD_NOW); if (handle == NULL) { char const *err_str = dlerror(); ALOGE("load: module=%s\n%s", path, err_str?err_str:"unknown"); status = -EINVAL; goto done; } //得到模块描述符的首地址,在模块注册时,将模块描述符命名为HAL_MODULE_INFO_SYM,其真实名称为HMI, //这里就是取得结构体HMI的首地址,#define HAL_MODULE_INFO_SYM_AS_STR "HMI" const char *sym = HAL_MODULE_INFO_SYM_AS_STR; hmi = (struct hw_module_t *)dlsym(handle, sym); if (hmi == NULL) { ALOGE("load: couldn't find symbol %s", sym); status = -EINVAL; goto done; } /* 匹配模块ID */ if (strcmp(id, hmi->id) != 0) { ALOGE("load: id=%s != hmi->id=%s", id, hmi->id); status = -EINVAL; goto done; } //将模块句柄保存到hw_module_t的成员变量dso中 hmi->dso = handle; status = 0; done: if (status != 0) { hmi = NULL; if (handle != NULL) { dlclose(handle); handle = NULL; } } else { ALOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",id, path, *pHmi, handle); } *pHmi = hmi;//返回模块描述符hw_module_t的地址 return status; }
硬件抽象层模块编写规范规定每一个硬件抽象层模块都必须导出一个符号名称为HAL_MODULE_INFO_SYM_AS_STR的符号,而且这个符号必须是用来描述一个类型为hw_module_t的结构体的。