前面分析了 android HAL 层是如何搜索硬件模块的动态共享库的,其实就是在 "/system/lib/hw/" 或者 "/vnedor/lib/hw" 这两个路径下找到共享库 moduleid.variant.so 后,通过调用 load 函数加载库。
下面我们进入 load 函数,看看具体是如何实现加载共享库的。
以下为 load 函数定义,同样在 /hardware/libhardware/hardware.c 中实现的:
/**
* Load the file defined by the variant and if successful
* return the dlopen handle and the hmi.
* @return 0 = success, !0 = failure.
*/
static int load(const char *id,
const char *path,
const struct hw_module_t **pHmi)
{//传入硬件模块id和库所在路径,获取到硬件模块结构体
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) {//匹配解析出硬件模块的id和传入我们实际想要得到的模块id是否一致
LOGE("load: id=%s != hmi->id=%s", id, hmi->id);
status = -EINVAL;
goto done;
}
hmi->dso = handle; //将打开库得到句柄传给硬件模块的dso
/* 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;//将得到的module的结果通过第三个参数传给hw_module_t
return status;
}
可以看到 load 函数传入的几个参数:
第一个参数就是需要加载的硬件模块对应动态库的硬件模块的 id;
第二个参数就是动态库存放的路径,就是在 hw_get_module 函数前部分搜索库得到的 path;
第三个参数就是我们需要得到的硬件模块结构体,通过它传给 hw_get_module,hw_get_module 函数再通过参数传给 jni。
第 19 行,首先调用 dlopen 打开共享库,该函数通过传入的库的路径找到库,并且打开它,传回一个操作句柄 handle,然后再调用 dlsym 函数解析这个打开的库,下面第 29 行,得到库中包含的硬件模块结构体,并将它返回回来。所以硬件厂商或者硬件移植者都必须根据 HAL 的这个架构去实现填充这个和自己硬件相关的硬件模块结构体 hw_module_t,供使用。
通过 dlsym 解析之后就得到了 hw_module_t,随后第 37 行,将从库中解析得到的结构体中的 id 和传入的 id 作比较,看是否一致。
如果一致则证明就是得到正确的硬件模块了。
最后第 60 行,将 hw_module_t 结构体指针传给第三个参数,传给 hw_get_module 函数。
到此,hw_get_module 函数就得到了硬件模块结构体 hw_module_t。
有了 hw_module_t,那么通过其内部的 method open 就能打开硬件模块对应的设备了,通过结构体中的一些方法就能够操作硬件设备了。