Camera1 源码解析系列—— Camera1 hw_get_module() 解析

Posted ByteSaid

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Camera1 源码解析系列—— Camera1 hw_get_module() 解析相关的知识,希望对你有一定的参考价值。

前言

这一章,我们将从 hw_get_module() 函数入手,去探究 Libraries 层是如何调用 HAL 层的库中的函数的。
CameraService 是在开机时就会启动的,而当它第一次启动时,就会调用一个名为 onFirstRef() 的成员函数,我们所要探究的内容就是从这里开始的。

1 CameraService

  • 路径:framework/av/services/camera/libcameraservice/CameraService.cpp
  • CameraService::onFirstRef()
    • 首先调用其基类的 onFirstRef 函数。
    • 更新 notifier (这个 BatteryNotifier 好像是个单例,看类名好像和电池有关)。
    • 通过 hw_get_module 函数获取 rawModule
    • 注意 rawModulecamera_module_t 类型。
    • 利用 rawModule 创建 mModule 的实例,mModuleCameraModule 类。
    BnCameraService::onFirstRef();

    // Update battery life tracking if service is restarting
    BatteryNotifier& notifier(BatteryNotifier::getInstance());
    notifier.noteResetCamera();
    notifier.noteResetFlashlight();

    camera_module_t *rawModule;
    /*** NOTE THIS ***/
    int err = hw_get_module(CAMERA_HARDWARE_MODULE_ID,
            (const hw_module_t **)&rawModule);
    if (err < 0) 
        ALOGE("Could not load camera HAL module: %d (%s)", err, strerror(-err));
        logServiceError("Could not load camera HAL module", err);
        return;
    

    /*** NOTE THIS ***/
    mModule = new CameraModule(rawModule);
    err = mModule->init();

2 hardware

2.1 hardware.h

  • 路径:hardware/libhardware/include/hardware/hardware.h
  • 注意两个宏定义:
/**
 * Name of the hal_module_info
 */
#define HAL_MODULE_INFO_SYM         HMI

/**
 * Name of the hal_module_info as a string
 */
#define HAL_MODULE_INFO_SYM_AS_STR  "HMI"

声明了这两个函数:

  • hw_get_module()
    • 作用是通过传入的 id 来获取模块相关的信息。
    • 成功则返回 0,出错则返回值小于 0*module == NULL
  • hw_get_module_by_class()
    • 作用是通过 class_id 获取与模块实例相关的信息。
    • 提供模块信息的库文件应该是带有这样命名规范的:
  • audio.primary.<variant>.so
  • audio.a2dp.<variant>.so
/**
 * Get the module info associated with a module by id.
 *
 * @return: 0 == success, <0 == error and *module == NULL
 */
int hw_get_module(const char *id, const struct hw_module_t **module);

/**
 * Get the module info associated with a module instance by class 'class_id'
 * and instance 'inst'.
 *
 * Some modules types necessitate multiple instances. For example audio supports
 * multiple concurrent interfaces and thus 'audio' is the module class
 * and 'primary' or 'a2dp' are module interfaces. This implies that the files
 * providing these modules would be named audio.primary.<variant>.so and
 * audio.a2dp.<variant>.so
 *
 * @return: 0 == success, <0 == error and *module == NULL
 */
int hw_get_module_by_class(const char *class_id, const char *inst,
                           const struct hw_module_t **module);

2.2 hardware.c

  • 路径:hardware/libhardware/hardware.c
  • 注意这个数组:
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"
;
  • hw_get_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_by_class()
    • 读取库文件,尝试的顺序是:
      • ro.hardware
      • ro.product.board
      • ro.board.platform
      • ro.arch
      • default
    • 通过 load 函数加载模块。
    /* 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. */
    /*** NOTE THIS ***/
    return load(class_id, path, module);
    /* 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. */
    /*** NOTE THIS ***/
    return load(class_id, path, module);
  • load()
    • 调用 dlopen() 函数获取一个 handle
    • 调用 dlsym() 函数从动态链接库中获取 hw_module_t 类型的 hmi
    • NOTE:
      • 为了获取动态链接库中的结构体,我们需要用到一个字符串 sym
      • sym 对应宏 HAL_MODULE_INFO_SYM_AS_STR,即 “HMI”。
      • 我们的动态链接库 .so 文件,是一个 ELF 文件。
      • ELFExecutable and Linkable Format,可执行链接格式。
      • ELF 文件头保存了一个路线图,用于描述文件的组织结构。
      • 通过 readelf -s 命令,我们可以查看对应的 .so 文件描述,可以看到其中有一个 Name 属性为 HMI ,其对应的位置就是我们所需要的结构体 hw_module_t
      • 于是我们通过 HMI 字段,就可以从动态链接库中读取出相应的结构体,从而得以在 Libraries 层中调用 HAL 层的库函数。
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;

    /*
     * 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
     */
    /*** NOTE THIS ***/
    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;
    

    /* Get the address of the struct hal_module_info. */
    /*** NOTE THIS ***/
    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;

/*** NOTE THIS ***/
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;

至此,我们就获得了最终的 rawModule,然后我们回到 onFirstRef() 中继续分析 CameraModule

3 CameraModule

3.1 CameraModule.cpp

  • 路径:frameworks/av/services/camera/libcameraservice/common/CameraModule.cpp
  • 构造函数:
    • 注意,这里的 mModulecamera_module_t 类型。
CameraModule::CameraModule(camera_module_t *module) 
    if (module == NULL) 
        ALOGE("%s: camera hardware module must not be null", 
                __FUNCTION__);
        assert(0);
    
    mModule = module;

  • init()
    • 调用 mModuleinit() 函数,这个 mModule 就是 camera_module_t 结构体对象。
int CameraModule::init() 
    ATRACE_CALL();
    int res = OK;
    if (getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_4 &&
            mModule->init != NULL) 
        ATRACE_BEGIN("camera_module->init");
        res = mModule->init();
        ATRACE_END();
    
    mCameraInfoMap.setCapacity(getNumberOfCameras());
    return res;

3.2 camera_common.h

  • 路径:hardware/libhardware/include/hardware/camera_common.h
  • 声明了 camera_module_t
    • 结构体中声明了许多函数指针,其中就有 init 函数指针。
typedef struct camera_module 
    hw_module_t common;
    int (*get_number_of_cameras)(void);
    int (*get_camera_info)(int camera_id, struct camera_info *info);
    int (*set_callbacks)(const camera_module_callbacks_t *callbacks);
    void (*get_vendor_tag_ops)(vendor_tag_ops_t* ops);
    int (*open_legacy)(const struct hw_module_t* module, const char* id,
            uint32_t halVersion, struct hw_device_t** device);
    int (*set_torch_mode)(const char* camera_id, bool enabled);
    int (*init)();
    void* reserved[5];
 camera_module_t;

那么这些函数指针被映射到什么地方了?hardwareCameraHAL.cpp 中提供了原生的映射方法。

3.3 CameraHAL.cpp

  • 路径:hardware/libhardware/modules/camera/3_0/CameraHAL.cpp
camera_module_t HAL_MODULE_INFO_SYM __attribute__ ((visibility("default"))) = 
    .common = 
        .tag                = HARDWARE_MODULE_TAG,
        .module_api_version = CAMERA_MODULE_API_VERSION_2_2,
        .hal_api_version    = HARDWARE_HAL_API_VERSION,
        .id                 = CAMERA_HARDWARE_MODULE_ID,
        .name               = "Default Camera HAL",
        .author             = "The android Open Source Project",
        .methods            = &gCameraModuleMethods,
        .dso                = NULL,
        .reserved           = 0,
    ,
    .get_number_of_cameras = get_number_of_cameras,
    .get_camera_info       = get_camera_info,
    .set_callbacks         = set_callbacks,
    .get_vendor_tag_ops    = get_vendor_tag_ops,
    .open_legacy           = NULL,
    .set_torch_mode        = NULL,
    .init                  = NULL,
    .reserved              = 0,
;

这是通用的映射,我们手机的芯片一般会重写这个 HAL 层接口,例如使用高通芯片的话,会在 QCamera2Hal.cpp 中重写这个函数映射。不同的芯片会在不同的地方,但是不会相差太大,况且这些函数指针都是一样的,这是 Android HAL 层提供的通用调用方法。

3.4 QCamera2Hal.cpp

  • 路径:hardware/qcom/camera/QCamera2/QCamera2Hal.cpp
  • 这个文件中有如下定义:
    • 详细描述了 camera_common
    • 确定了函数指针的指向,QCamera2Factory::mModuleMethods
static hw_module_t camera_common = 
    .tag                    = HARDWARE_MODULE_TAG,
    .module_api_version     = CAMERA_MODULE_API_VERSION_2_4,
    .hal_api_version        = HARDWARE_HAL_API_VERSION,
    .id                     = CAMERA_HARDWARE_MODULE_ID,
    .name                   = "QCamera Module",
    .author                 = "Qualcomm Innovation Center Inc",
    .methods                = &qcamera::QCamera2Factory::mModuleMethods,
    .dso                    = NULL,
    .reserved               = 0
;

camera_module_t HAL_MODULE_INFO_SYM = 
    .common                 = camera_common,
    .get_number_of_cameras  = qcamera::QCamera2Factory::get_number_of_cameras,
    .get_camera_info        = qcamera::QCamera2Factory::get_camera_info,
    .set_callbacks          = qcamera::QCamera2Factory::set_callbacks,
    .get_vendor_tag_ops     = qcamera::QCamera3VendorTags::get_vendor_tag_ops,
    .open_legacy            = qcamera::QCamera2Factory::open_legacy,
    .set_torch_mode         = qcamera::QCamera2Factory::set_torch_mode,
    .init                   = NULL,
    .reserved               = 0
;

3.5 QCamera2Factory.cpp

  • 路径:hardware/qcom/camera/QCamera2/QCamera2Factory.cpp
struct hw_module_methods_t QCamera2Factory::mModuleMethods = 
    .open = QCamera2Factory::camera_device_open,
;

int QCamera2Factory::camera_device_open(
    const struct hw_module_t *module, const char *id,
    struct hw_device_t **hw_device)

    int rc = NO_ERROR;
    if (module != &HAL_MODULE_INFO_SYM.common) 
        LOGE("Invalid module. Trying to open %p, expect %p",
            module, &HAL_MODULE_INFO_SYM.common);
        return INVALID_OPERATION;
    
    if (!id) 
        LOGE("Invalid camera id");
        return BAD_VALUE;
    
#ifdef QCAMERA_HAL1_SUPPORT
    if(gQCameraMuxer)
        rc =  gQCameraMuxer->camera_device_open(module, id, hw_device);
    else
#endif
        rc = gQCamera2Factory->cameraDeviceOpen(atoi(id), hw_device);
    return rc;

再往下追踪就是芯片中重写的核心方法了,暂时不作深入分析。

4 流程简图

5 总结

本篇我们从 CameraService::onFirstRef() 入手,逐渐理顺了以 hw_get_module() 为中心的一个调用逻辑。实际上,Android HAL 层有一个通用的入口,即宏 HAL_MODULE_INFO_SYM,通过它,我们获取 HAL 层中的模块实例,从而使得我们可以调用 HAL 层所提供的函数。理解了 HAL 层的入口,接下来我们可以去对 Camera.startPreview() 的控制流程进行分析了。

以上是关于Camera1 源码解析系列—— Camera1 hw_get_module() 解析的主要内容,如果未能解决你的问题,请参考以下文章

Camera1 源码解析系列—— Camera1 startPreview() 流程解析

Camera1 源码解析系列—— Camera1 Open() 流程解析

Camera1 源码解析系列—— Camera1 takePicture() 流程解析

Camera1 源码解析系列—— Camera1 takePicture() 流程解析

Camera1 源码解析系列—— Camera1 基本架构

Camera1 源码解析系列—— Camera1 基本架构