Android-GnssHal层gps.xxx.so查找与加载过程分析
gps.xxx.so不是在系统编译的时候直接prelink的而是在运行阶段由gnss hal service动态查找并加载的;
[email protected] 会在系统启动时 启动 [email protected]
然后通过 hw_get_module 获取到对应的module并open以及获取 interface ;
IGnss* HIDL_FETCH_IGnss(const char* /* hal */) {
hw_module_t* module;
IGnss* iface = nullptr;
int 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) {
iface = new Gnss(reinterpret_cast(device));
} else {
ALOGE("gnssDevice open %s failed: %d", GPS_HARDWARE_MODULE_ID, err);
}
} else {
ALOGE("gnss hw_get_module %s failed: %d", GPS_HARDWARE_MODULE_ID, err);
}
return iface;
}
hw_get_module 接口的实现在 hardware/libhardware/hardware.c
线面对几个关键步骤进行分析
/**
* There are a set of variant filename for modules. The form of the filename
* is ".variant.so" so for the led module the Dream variants
* of base "ro.product.board", "ro.board.platform" and "ro.arch" would be:
*
* led.trout.so
* led.msm7k.so
* led.ARMV6.so
* led.default.so
*/
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 int load(const char *id,
const char *path,
const struct hw_module_t **pHmi)
{
int status = -EINVAL;
void *handle = NULL;
struct hw_module_t *hmi = NULL;
#ifdef __ANDROID_VNDK__
const bool try_system = false;
#else
const bool try_system = true;
#endif
/*
* 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
*/
if (try_system &&
strncmp(path, HAL_LIBRARY_PATH1, strlen(HAL_LIBRARY_PATH1)) == 0) {
/* If the library is in system partition, no need to check
* sphal namespace. Open it with dlopen.
*/
handle = dlopen(path, RTLD_NOW);
} else {
handle = android_load_sphal_library(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;
}
/* 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) {
ALOGE("load: couldn't find symbol %s", sym);
status = -EINVAL;
goto done;
}
/* Check that the id matches */
if (strcmp(id, hmi->id) != 0) {
ALOGE("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 {
ALOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",
id, path, *pHmi, handle);
}
*pHmi = hmi;
return status;
}
/*
* Check if a HAL with given name and subname exists, if so return 0, otherwise
* otherwise return negative. On success path will contain the path to the HAL.
*/
static int hw_module_exists(char *path, size_t path_len, const char *name,
const char *subname)
{
snprintf(path, path_len, "%s/%s.%s.so",
HAL_LIBRARY_PATH3, name, subname);
if (access(path, R_OK) == 0)
return 0;
snprintf(path, path_len, "%s/%s.%s.so",
HAL_LIBRARY_PATH2, name, subname);
if (access(path, R_OK) == 0)
return 0;
#ifndef __ANDROID_VNDK__
snprintf(path, path_len, "%s/%s.%s.so",
HAL_LIBRARY_PATH1, name, subname);
if (access(path, R_OK) == 0)
return 0;
#endif
return -ENOENT;
}
int hw_get_module_by_class(const char *class_id, const char *inst,
const struct hw_module_t **module)
{
int i = 0;
char prop[PATH_MAX] = {0};
char path[PATH_MAX] = {0};
char name[PATH_MAX] = {0};
char prop_name[PATH_MAX] = {0};
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.
*/
/* First try a property specific to the class and possibly instance */
snprintf(prop_name, sizeof(prop_name), "ro.hardware.%s", name);
if (property_get(prop_name, prop, NULL) > 0) {
if (hw_module_exists(path, sizeof(path), name, prop) == 0) {
goto found;
}
}
/* Loop through the configuration variants looking for a module */
for (i=0 ; i
如 hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module); 调用中
GPS_HARDWARE_MODULE_ID 被定义为 "gps", module 为出参, 即找到的module结构体回传给调用者;
hw_get_module 实际是调用 hw_get_module_by_class 并将 id 作为入参传递;
在 hw_get_module_by_class 中会构建一个prop_name即 ro.hardware.%s 其中%s用参数id代替;
这里是 ro.hardware.gps;
如果系统能查找到 ro.hardware.gps property 并且属性值不为空,就会调用 hw_module_exists 查找so的path;
so的格式是 "path/gps.%s.so" 其中%s用查到的属性值代替;
这里的path按照如下定义的path中的3 2 1的顺序查找
#if defined(__LP64__)
#define HAL_LIBRARY_PATH1 "/system/lib64/hw"
#define HAL_LIBRARY_PATH2 "/vendor/lib64/hw"
#define HAL_LIBRARY_PATH3 "/odm/lib64/hw"
#else
#define HAL_LIBRARY_PATH1 "/system/lib/hw"
#define HAL_LIBRARY_PATH2 "/vendor/lib/hw"
#define HAL_LIBRARY_PATH3 "/odm/lib/hw"
#endif
调试时可以使用 getprop 命令获取系统所有的property并确认每个property的值;
如执行命令 # getprop | grep hardware
[ro.hardware]: [xxxv3]
则需要把 gps so 命名为 gps.xxxv3.so 并放到上述定义的 /system/ 或 /vendor 或 /odm 对应的目录下才会被正确加载;
如果 access 成功 即对应的文件存在, 则直接 goto 到标签 found: 即直接执行load加载对应的so;
如果库文件是在 /system 目录下则直接使用dlopen打开,否则使用 android_load_sphal_library 打开;
如果不能查找到 ro.hardware.%s 属性或属性值为空,则循环查找 variant_keys 中的 property;
variant_keys 被定义如下
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"
};
这里 会获取到诸如 版型,平台,架构等;
获取到对应的属性值后会尝试到上述目录下查找 gps.xxx.so 文件并加载;
如果上述尝试都没有成功找到一个对应的 gps so 文件则会尝试最后一个命名规则;
hw_module_exists(path, sizeof(path), name, "default");
即在上述路径查找命名如 gps.default.so 的文件并尝试加载;
否则返回值为2,即 ENOENT: no such file or directory.