2. HAL模块的动态加载与注册

任务目标

在Android HAL框架中,实现一个自定义硬件模块的 hw_module_t 结构体,通过标准宏完成模块注册,并利用 hw_get_module() 在运行时动态加载该模块,最终获取模块句柄并验证加载成功。

环境与前置依赖

  • Android源码环境(AOSP),已编译 libhardware 库。
  • 本案例代码放置于 hardware/libhardware/modules/demo/ 目录下。
  • 模块ID定义为 HARDWARE_MODULE_ID_DEMO,值为 "demo"。

核心实现

以下代码完整实现了HAL模块的定义、注册与动态加载验证。模块仅暴露一个返回版本信息的简单操作,用于确认加载链路正确。

// demo_hal.c
#include <hardware/hardware.h>
#include <stdint.h>

#define DEMO_MODULE_ID "demo"
#define DEMO_API_VERSION HARDWARE_MODULE_API_VERSION(1, 0)

// 模块自定义方法:返回模块版本字符串
static int demo_get_version(const struct hw_module_t* module, char* buf, int size) {
    if (buf == NULL || size < 8) return -1;
    snprintf(buf, size, "v1.0");
    return 0;
}

// 模块实例结构(可扩展自定义字段)
struct demo_module_t {
    struct hw_module_t common;
    int (*get_version)(const struct hw_module_t*, char*, int);
};

// 打开设备时回调(本案例仅验证加载,不实现具体设备)
static int demo_device_open(const struct hw_module_t* module, const char* name,
                            struct hw_device_t** device) {
    (void)module; (void)name; (void)device;
    return -ENOSYS; // 无设备操作,仅验证模块加载
}

// 模块方法表
static struct hw_module_methods_t demo_methods = {
    .open = demo_device_open,
};

// 模块实例定义
struct demo_module_t HAL_MODULE_INFO_SYM = {
    .common = {
        .tag = HARDWARE_MODULE_TAG,
        .module_api_version = DEMO_API_VERSION,
        .hal_api_version = HARDWARE_HAL_API_VERSION,
        .id = DEMO_MODULE_ID,
        .name = "Demo HAL Module",
        .author = "AOSP Course",
        .methods = &demo_methods,
        .dso = NULL, // 由hw_get_module()加载后填充
    },
    .get_version = demo_get_version,
};

// 使用标准宏注册模块(展开后声明HAL_MODULE_INFO_SYM)
HAL_MODULE_INFO_SYM = HAL_MODULE_INFO_SYM; // 确保符号不被优化掉
// Android.bp
cc_library_shared {
    name: "demo.default",
    relative_install_path: "hw",
    srcs: ["demo_hal.c"],
    shared_libs: ["libhardware"],
    vendor: true,
    proprietary: true,
}
// 验证程序 test_demo_load.c
#include <hardware/hardware.h>
#include <stdio.h>

#define DEMO_MODULE_ID "demo"

int main() {
    const struct hw_module_t* hw_module = NULL;
    int ret = hw_get_module(DEMO_MODULE_ID, &hw_module);
    if (ret != 0) {
        fprintf(stderr, "hw_get_module failed: %d\n", ret);
        return -1;
    }

    // 验证模块基本信息
    if (hw_module == NULL || hw_module->id == NULL ||
        strcmp(hw_module->id, DEMO_MODULE_ID) != 0) {
        fprintf(stderr, "Invalid module loaded\n");
        return -2;
    }

    // 调用自定义方法
    struct demo_module_t* demo_mod = (struct demo_module_t*)hw_module;
    char version[16] = {0};
    if (demo_mod->get_version(hw_module, version, sizeof(version)) != 0) {
        fprintf(stderr, "get_version failed\n");
        return -3;
    }

    printf("Module loaded: id=%s, version=%s\n", hw_module->id, version);
    return 0;
}

编译与运行

  1. 编译模块:在AOSP根目录执行 mmm hardware/libhardware/modules/demo/,生成 demo.default.so。
  2. 推送模块:adb push demo.default.so /vendor/lib64/hw/(64位系统)或 /vendor/lib/hw/(32位系统)。
  3. 编译验证程序:将 test_demo_load.c 加入Android.bp或使用NDK直接编译,推送至设备执行。

验证方法与判定标准

  • 运行 test_demo_load,观察输出。
  • 通过条件:程序返回0,且打印 Module loaded: id=demo, version=v1.0。
  • 若返回 -1,检查模块路径是否正确、模块ID是否匹配;若返回 -2,检查模块结构体初始化是否正确。
HAL模块动态加载流程 1. 模块定义 struct demo_module_t HAL_MODULE_INFO_SYM HAL_MODULE_INFO_SYM 符号 编译 2. 编译产物 demo.default.so 包含 HAL_MODULE_INFO_SYM 导出符号表 推送 3. 系统安装 /vendor/lib64/hw/ demo.default.so hw_get_module() 搜索路径 dlopen() 4. hw_get_module() 动态加载过程 按ID查找 "demo" dlopen() 加载.so文件 dlsym() HAL_MODULE_INFO_SYM 返回句柄 hw_module_t*

关键说明

  • HAL_MODULE_INFO_SYM 是Android HAL框架约定的模块入口符号,hw_get_module() 通过 dlsym() 查找该符号获取模块结构体。
  • 模块ID必须与 hw_get_module() 传入的ID严格一致,且 .so 文件名需以 .default 结尾(如 demo.default.so),系统才能正确识别。
  • 本案例仅验证模块加载链路,device_open 返回 -ENOSYS 表示未实现具体设备操作,不影响模块加载验证。