Android Camera 流程學習記錄(三)—— Camera hw_get_module() 相關邏輯

簡介
這一篇筆記,咱們將從 hw_get_module() 函數入手,去探究 Libraries 層是如何調用 HAL 層的庫中的函數的。
CameraService 是在開機時就會啓動的,而當它第一次啓動時,就會調用一個名爲 onFirstRef() 的成員函數,咱們所要探究的內容就是從這裏開始的。
NOTE: 
這一部分主要參考: 
Android–hw_get_module解析
原本想一天內應該能搞定這篇記錄,結果公司這邊正好有個項目要作,因而只能在閒暇時間慢慢寫出來了。
hw_get_module()
1. CameraService
1.1 CameraService.cpp
位置:framework/av/services/camera/libcameraservice/CameraService.cpp
CameraService::onFirstRef(): 
首先調用其基類的 onFirstRef 函數。
更新 notifier (這個 BatteryNotifier 好像是個單例,看類名好像和電池有關)。
經過 hw_get_module 函數獲取 rawModule。
注意 rawModule 是 camera_module_t 類型。
利用 rawModule 建立 mModule 的實例,mModule 是 CameraModule 類。
    BnCameraService::onFirstRef();api

    // 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;
    }ide

    /*** 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);oop

/**
 * 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;
        }
    }this

    /* 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;
        }
    }.net

    /* Nothing found, try the default */
    if (hw_module_exists(path, sizeof(path), name, "default") == 0) {
        goto found;
    }指針

    return -ENOENT;rest

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 文件。
ELF:Executable and Linkable Format,可執行連接格式。
ELF 文件頭保存了一個路線圖,用於描述文件的組織結構。
經過 readelf -s 命令,咱們能夠查看對應的 .so 文件描述,能夠看到其中有一個 Name 屬性爲 HMI ,其對應的位置就是咱們所須要的結構體 hw_module_t。
因而咱們經過 HMI 字段,就能夠從動態連接庫中讀取出相應的結構體,從而得以在 Libraries 層中調用 HAL 層的庫函數。
至此,咱們就得到了最終的 rawModule,而後咱們回到 onFirstRef() 中繼續分析。
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;orm

    /*
     * 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;
}

3. CameraModule
3.1 CameraModule.cpp
位置:frameworks/av/services/camera/libcameraservice/common/CameraModule.cpp
構造函數: 
注意,這裏的 mModule 是 camera_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(): 
調用 mModule 的 init() 函數。
若是沒有指定的 init() 函數,則 init 流程到這裏就能夠結束了。
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 函數指針。
這個指針指向的具體函數,是根據具體的 Camera 設備肯定的,其中我查看了 QCamera2Factory.cpp ,這裏就實現了對應的函數。
到這裏暫時就不必再深刻下去了,由於業務關係如今優先把 Framework 到 Libraries 的部分搞清楚。
    /**
     * init:
     *
     * This method is called by the camera service before any other methods
     * are invoked, right after the camera HAL library has been successfully
     * loaded. It may be left as NULL by the HAL module, if no initialization
     * in needed.
     *
     * It can be used by HAL implementations to perform initialization and
     * other one-time operations.
     *
     * Version information (based on camera_module_t.common.module_api_version):
     *
     * CAMERA_MODULE_API_VERSION_1_x/2_0/2_1/2_2/2_3:
     *   Not provided by HAL module. Framework will not call this function.
     *
     * CAMERA_MODULE_API_VERSION_2_4:
     *   If not NULL, will always be called by the framework once after the HAL
     *   module is loaded, before any other HAL module method is called.
     *
     * Return values:
     *
     * 0:           On a successful operation.
     *
     * -ENODEV:     Initialization cannot be completed due to an internal
     *              error. The HAL must be assumed to be in a nonfunctional
     *              state.
     *
     */
    int (*init)();

3.3* QCamera2Factory
看路徑,這幾個文件應該是和高通平臺有關的。
3.3.1* QCamera2Factory.h
位置:hardware/qcom/camera/QCamera2/QCamera2Factory.h
3.3.2* QCamera2Factory.cpp
位置:hardware/qcom/camera/QCamera2/QCamera2Factory.cpp
3.3.3* QCamera2Hal.cpp
位置:hardware/qcom/camera/QCamera2/QCamera2Hal.cpp
這個文件中有以下定義: 
詳細描述了 camera_common。
肯定了函數指針的指向。
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}
};

4. 流程簡圖

小結 這篇筆記中,咱們從 CameraService::onFirstRef() 入手,逐漸理順了以 hw_get_module() 爲中心的一個調用邏輯。 實際上,Android HAL 層有一個通用的入口,即宏 HAL_MODULE_INFO_SYM,經過它,咱們獲取 HAL 層中的模塊實例,從而使得咱們能夠調用 HAL 層所提供的函數。 理解了 HAL 層的入口,接下來咱們能夠去對 Camera.startPreview() 的控制流程進行分析,從而再次加深咱們對 Camera 控制流的理解。

相關文章
相關標籤/搜索