Android-GnssHal层gps.xxx.so查找与加载过程分析
gps.xxx.so不是在系统编译的时候直接prelink的而是在运行阶段由gnss hal service动态查找并加载的;
android.hardware.gnss@1.0-service.rc 会在系统启动时 启动 android.hardware.gnss@1.0-service 然后通过 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<gps_device_t*>(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 "<MODULE_ID>.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<HAL_VARIANT_KEYS_COUNT; i++) { if (property_get(variant_keys[i], prop, NULL) == 0) { continue; } if (hw_module_exists(path, sizeof(path), name, prop) == 0) { goto found; } } /* Nothing found, try the default */ if (hw_module_exists(path, sizeof(path), name, "default") == 0) { goto found; } return -ENOENT; found: /* load the module, if this fails, we're doomed, and we should not try * to load a different variant. */ return load(class_id, path, module); } 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(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.