• 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();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    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"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    声明了这两个函数:

    • hw_get_module()
      • 作用是通过传入的 id 来获取模块相关的信息。
      • 成功则返回 0,出错则返回值小于 0*module == NULL
    • hw_get_module_by_class()
      • 作用是通过 class_id 获取与模块实例相关的信息。
      • 提供模块信息的库文件应该是带有这样命名规范的:
    • audio.primary..so
    • audio.a2dp..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..so and
     * audio.a2dp..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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    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"
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
        /* 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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    至此,我们就获得了最终的 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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    那么这些函数指针被映射到什么地方了?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},
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    这是通用的映射,我们手机的芯片一般会重写这个 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}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

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

    4 流程简图

    流程简图

    5 总结

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

  • 相关阅读:
    Flask框架:运用Ajax轮询动态绘图
    Kubernetes学习笔记-kubernetes API服务器的安全防护(1)了解认证机制20220813
    【VSCode】Windows环境下,VSCode 搭建 cmake 编译环境(VSCode 插件配置)
    反向代理
    【计算机视觉】3D视觉
    source insight 使用过程中问题点总结
    如何免费创建三级域名?
    Java——》synchronized锁粗化&锁消除
    2022下半年(软考高级)信息系统项目师备考开班啦!
    路漫漫远修兮-GeoServer2.16.0版本跨域解决
  • 原文地址:https://blog.csdn.net/hello_1995/article/details/126039355