android camera动态库加载过程

一.android camera库的加载过程

      下面代码都是android 4.4的代码,上层的代码基本上变化不大。我们知道android调用hal的接口的时候,都要将对应的动态库加载进内存才可以使用,这个加载过程大家也许有些陌生,现在就已camera动态库的加载来探索一下动态库的加载过程。

      camera库是由camera service负责加载的,它的加载过程是在onFirstRef()中实现的。在CameraService服务类中定义了一个camera_module_t *mModule;它的结构体如下所示,具体google已经在代码中添加了大量注释,大家一看就懂。^_^  ^_^  ^_^  ^_^

typedef struct camera_module {
    hw_module_t common;

    /**
     * get_number_of_cameras:
     *
     * Returns the number of camera devices accessible through the camera
     * module.  The camera devices are numbered 0 through N-1, where N is the
     * value returned by this call. The name of the camera device for open() is
     * simply the number converted to a string. That is, "0" for camera ID 0,
     * "1" for camera ID 1.
     *
     * The value here must be static, and cannot change after the first call to
     * this method
     */
    int (*get_number_of_cameras)(void);

    /**
     * get_camera_info:
     *
     * Return the static camera information for a given camera device. This
     * information may not change for a camera device.
     *
     */
    int (*get_camera_info)(int camera_id, struct camera_info *info);

    /**
     * set_callbacks:
     *
     * Provide callback function pointers to the HAL module to inform framework
     * of asynchronous camera module events. The framework will call this
     * function once after initial camera HAL module load, after the
     * get_number_of_cameras() method is called for the first time, and before
     * any other calls to the module.
     *
     * Version information (based on camera_module_t.common.module_api_version):
     *
     *  CAMERA_MODULE_API_VERSION_1_0, CAMERA_MODULE_API_VERSION_2_0:
     *
     *    Not provided by HAL module. Framework may not call this function.
     *
     *  CAMERA_MODULE_API_VERSION_2_1:
     *
     *    Valid to be called by the framework.
     *
     */
    int (*set_callbacks)(const camera_module_callbacks_t *callbacks);

    /**
     * get_vendor_tag_ops:
     *
     * Get methods to query for vendor extension metadata tag information. The
     * HAL should fill in all the vendor tag operation methods, or leave ops
     * unchanged if no vendor tags are defined.
     *
     * Version information (based on camera_module_t.common.module_api_version):
     *
     *  CAMERA_MODULE_API_VERSION_1_x/2_0/2_1:
     *    Not provided by HAL module. Framework may not call this function.
     *
     *  CAMERA_MODULE_API_VERSION_2_2:
     *    Valid to be called by the framework.
     */
    void (*get_vendor_tag_ops)(vendor_tag_ops_t* ops);

    /* reserved for future use */
    void* reserved[8];
} camera_module_t;

      在寻找Camera module的时候,传下去一个camera_module_t对象指针,那么这样的话我们就需要在hal层去填充这样一个结构体,或者我们本来就在hal实现了一个对应的结构,简单的拷贝就行了。这里本来就在hal实现了这样的一个结构了,只要去链接就行了。

void CameraService::onFirstRef()
{
    LOG1("CameraService::onFirstRef");

    BnCameraService::onFirstRef();

    if (hw_get_module(CAMERA_HARDWARE_MODULE_ID, //就是根据这个camera module id来判断问题点的。
                (const hw_module_t **)&mModule) < 0) { //传下去的是&mModule,我们看看hw_get_module()实现。
        ALOGE("Could not load camera HAL module");
        mNumberOfCameras = 0;
    }
    else {
        ALOGI("Loaded \"%s\" camera module", mModule->common.name);
        mNumberOfCameras = mModule->get_number_of_cameras();
        if (mNumberOfCameras > MAX_CAMERAS) {
            ALOGE("Number of cameras(%d) > MAX_CAMERAS(%d).",
                    mNumberOfCameras, MAX_CAMERAS);
            mNumberOfCameras = MAX_CAMERAS;
        }
        for (int i = 0; i < mNumberOfCameras; i++) {
            setCameraFree(i);
        }

        if (mModule->common.module_api_version >=
                CAMERA_MODULE_API_VERSION_2_1) {
            mModule->set_callbacks(this);
        }

        CameraDeviceFactory::registerService(this);
    }
}

      上面的代码中调用到了hw_get_module(),在源码中不知大家有没有注意到第二个参数是hw_module_t **module,这里是指针的指针,而我们刚才传的是camera_module_t**指针。大家可以看到camera_module_t 结构第一个域就是hw_module_t 所以这里就不难理解了。

源码路径:hardware/libhardware/hardware.c

/** Base path of the hal modules */
#define HAL_LIBRARY_PATH1 "/system/lib/hw"  //这里看来hal硬件模块的存放地址是一定的。
#define HAL_LIBRARY_PATH2 "/vendor/lib/hw"


int hw_get_module(const char *id, const struct hw_module_t **module)
{
    return hw_get_module_by_class(id, NULL, module); //这里的id就是camera模块的id,每一个hal module都有对应的id,区分他们就通过这个id来
                                                     //区分了。
 }
int hw_get_module_by_class(const char *class_id, const char *inst,
                           const struct hw_module_t **module)
{
    int status;
    int i;
    const struct hw_module_t *hmi = NULL;
    char prop[PATH_MAX];
    char path[PATH_MAX];
    char name[PATH_MAX];

    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.
     */

    /* Loop through the configuration variants looking for a module */
    for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) {
        if (i < HAL_VARIANT_KEYS_COUNT) {
            if (property_get(variant_keys[i], prop, NULL) == 0) { //关键字数组,上面有宏代码。
                continue;
            }
            snprintf(path, sizeof(path), "%s/%s.%s.so",
                     HAL_LIBRARY_PATH2, name, prop);
            if (access(path, R_OK) == 0) break;

            snprintf(path, sizeof(path), "%s/%s.%s.so", //拼接完整的camera库。
                     HAL_LIBRARY_PATH1, name, prop);
            if (access(path, R_OK) == 0) break;
        } else {
            snprintf(path, sizeof(path), "%s/%s.default.so",
                     HAL_LIBRARY_PATH2, name);
            if (access(path, R_OK) == 0) break;

            snprintf(path, sizeof(path), "%s/%s.default.so",
                     HAL_LIBRARY_PATH1, name);
            if (access(path, R_OK) == 0) break;
        }
    }

    status = -ENOENT;
    if (i < HAL_VARIANT_KEYS_COUNT+1) {
        /* load the module, if this fails, we're doomed, and we should not try
         * to load a different variant. */
        status = load(class_id, path, module); //如果上面都进行完毕,走到这里,说明已经找到库了,这里就去加载。
    }

    return status;
}
//根据id来加载hal的module
static int load(const char *id,
        const char *path,
        const struct hw_module_t **pHmi)
{
    int status;
    void *handle;
    struct hw_module_t *hmi;

    /*
     * 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
     */
    handle = dlopen(path, RTLD_NOW); //动态加载内存的api,这里的path=/system/lib/hw/camera.so
    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;   //别的地方定义#define HAL_MODULE_INFO_SYM_AS_STR  "HMI"
    hmi = (struct hw_module_t *)dlsym(handle, sym); //我们动态链接的是"HMI"这个符号。
    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) { //确定id是否是CAMERA_HARDWARE_MODULE_ID.
        ALOGE("load: id=%s != hmi->id=%s", id, hmi->id);
        status = -EINVAL;
        goto done;
    }

    // psw0523 fix for lights module loading segfault
    if (strcmp(id, "lights"))
        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; //最后将这个指针,赋给我们之前定义的 struct camera_module变量。这里模块就加载进来了。

    return status;
}

//hal代码
extern "C" {
    struct camera_module HAL_MODULE_INFO_SYM = { //这里的HAL_MODULE_INFO_SYM ="HMI",链接的就是这个结构体
      common : {
          tag           : HARDWARE_MODULE_TAG,
          version_major : 1,
          version_minor : 0,
          id            : CAMERA_HARDWARE_MODULE_ID, //就是通过id来匹配的。在上面匹配时下发的就是这个id
          name          : "orion camera HAL",
          author        : "Samsung Corporation",
          methods       : &camera_module_methods,
      },
      get_number_of_cameras : HAL_getNumberOfCameras,
      get_camera_info       : HAL_getCameraInfo
    };
}

上面camera module我们已经加载过了,这时候我们需要构造一个camera_device的结构。这个结构通过camera module里面的open方法来获取,请看下面代码。

小料:这里以实例来详细说明一下如何查找到我们需要的库的。

int hw_get_module_by_class(const char *class_id, const char *inst,
                           const struct hw_module_t **module)
{
1    int status;
2    int i;
3    const struct hw_module_t *hmi = NULL;
4    char prop[PATH_MAX];
5    char path[PATH_MAX];
6    char name[PATH_MAX];

7    if (inst)
8        snprintf(name, PATH_MAX, "%s.%s", class_id, inst);
9    else
10        strlcpy(name, class_id, PATH_MAX);

11    snprintf(prop_name, sizeof(prop_name), "ro.hardware.%s", name);
12    if (property_get(prop_name, prop, NULL) > 0) {
13        if (hw_module_exists(path, sizeof(path), name, prop) == 0) {
14            goto found;
        }
    }

15    for (i=0 ; i<HAL_VARIANT_KEYS_COUNT; i++) {
16        if (property_get(variant_keys[i], prop, NULL) == 0) {
            continue;
        }
17        if (hw_module_exists(path, sizeof(path), name, prop) == 0) {
            goto found;
        }
    }


    /* Nothing found, try the default */
18    if (hw_module_exists(path, sizeof(path), name, "default") == 0) {
        goto found;
    }
class_id:这个就是指向“camera"字符串的指针,在调用处用一个宏来表示该字符串( hardware / libhardware / include / hardware / camera_common.h),如下所示:

#define CAMERA_HARDWARE_MODULE_ID "camera"
 上面的代码中,由于inst传进来时就为NULL,所以第7行开始的if语句只会走else语句后面第10句。我们可以看到第10句的作用就是把“camera"字符串拷贝到name局部变量中的,这样的话name = ”camea" 

紧接着第11标号处,会去读“ro.hardware.camera",这个property变量,但是一般情况下我们读过来的都是空。例如下面实例。

adb shell getprop ro.hardwaresc8830

紧接着就来到15标号处,这里它会循环读取下面的property变量,并将读取过来的变量保存到prop局部变量中。

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"
};

#:adb shell getprop ro.product.board
     sp8730seea_jig

#:adb shell getprop ro.board.platform
     sc8830

然后标号17处会重新将上面的name和prop重新组装成最后确定的那个库,注意prop=sc8830,name = camera

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_PATH2, name, subname); //PATH2=/vendor/lib/hw,那么这里path = /vendor/lib/hw/camera.sc8830.so,就这样我们找到了camera库。

    if (access(path, R_OK) == 0)
        return 0;

    snprintf(path, path_len, "%s/%s.%s.so", //PATH1=/system/lib/hw,那么这里path = /system/lib/hw/camera.sc8830.so,就这样我们找到了camera库。

             HAL_LIBRARY_PATH1, name, subname);
    if (access(path, R_OK) == 0)
        return 0;

    return -ENOENT;
}
现在我们知道,camera库是通过读取系统property变量来确定的库的名字,那么这些变量都是什么时候写入到property数据库中的呢。我们来看看。经过搜索发现这些property都是编译系统导出到build.prop文件,由kernel一次性写入的。build/tools/buildinfo.sh
#!/bin/bash

echo "# begin build properties"
echo "# autogenerated by buildinfo.sh"

echo "ro.build.id=$BUILD_ID"
echo "ro.build.display.id=$BUILD_DISPLAY_ID"
echo "ro.build.version.incremental=$BUILD_NUMBER"

buildinfo.sh包含在/build/core/Makefile,这里我们来看看Makefile中是如何来实现的。

INSTALLED_BUILD_PROP_TARGET := $(TARGET_OUT)/build.prop
ALL_DEFAULT_INSTALLED_MODULES += $(INSTALLED_BUILD_PROP_TARGET) 
这里注意:INSTALLED_BUILD_PROP_TARGET 定义的就是我们在out目录下对应工程的目录。

BUILDINFO_SH := build/tools/buildinfo.sh 就在这里
# build.prop

ifdef TARGET_SYSTEM_PROP
system_prop_file := $(TARGET_SYSTEM_PROP)
else
system_prop_file := $(wildcard $(TARGET_DEVICE_DIR)/system.prop)
endif
$(INSTALLED_BUILD_PROP_TARGET): $(BUILDINFO_SH) $(INTERNAL_BUILD_ID_MAKEFILE) $(BUILD_SYSTEM)/version_defaults.mk $(system_prop_file)
	@echo Target buildinfo: $@
	@mkdir -p $(dir $@)
	$(hide) echo > $@
ifneq ($(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_OEM_PROPERTIES),)
	$(hide) echo "#" >> $@; \
	        echo "# PRODUCT_OEM_PROPERTIES" >> $@; \
	        echo "#" >> $@;
	$(hide) $(foreach prop,$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_OEM_PROPERTIES), \
		echo "import /oem/oem.prop $(prop)" >> $@;)
endif
	$(hide) TARGET_BUILD_TYPE="$(TARGET_BUILD_VARIANT)" \
			TARGET_BUILD_FLAVOR="$(TARGET_PRODUCT)-$(TARGET_BUILD_VARIANT)" \
			TARGET_DEVICE="$(TARGET_DEVICE)" \
			PRODUCT_NAME="$(TARGET_PRODUCT)" \
			PRODUCT_BRAND="$(PRODUCT_BRAND)" \
			PRODUCT_DEFAULT_LANGUAGE="$(call default-locale-language,$(PRODUCT_LOCALES))" \
			PRODUCT_DEFAULT_REGION="$(call default-locale-region,$(PRODUCT_LOCALES))" \
			PRODUCT_DEFAULT_WIFI_CHANNELS="$(PRODUCT_DEFAULT_WIFI_CHANNELS)" \
			PRODUCT_MODEL="$(PRODUCT_MODEL)" \
			PRODUCT_MANUFACTURER="$(PRODUCT_MANUFACTURER)" \
			PRIVATE_BUILD_DESC="$(PRIVATE_BUILD_DESC)" \
			BUILD_ID="$(BUILD_ID)" \
			BUILD_DISPLAY_ID="$(BUILD_DISPLAY_ID)" \
			BUILD_NUMBER="$(BUILD_NUMBER)" \
			PLATFORM_VERSION="$(PLATFORM_VERSION)" \
			PLATFORM_SDK_VERSION="$(PLATFORM_SDK_VERSION)" \
			PLATFORM_VERSION_CODENAME="$(PLATFORM_VERSION_CODENAME)" \
			PLATFORM_VERSION_ALL_CODENAMES="$(PLATFORM_VERSION_ALL_CODENAMES)" \
			BUILD_VERSION_TAGS="$(BUILD_VERSION_TAGS)" \
			TARGET_BOOTLOADER_BOARD_NAME="$(TARGET_BOOTLOADER_BOARD_NAME)" \
			BUILD_FINGERPRINT="$(BUILD_FINGERPRINT)" \
			$(if $(OEM_THUMBPRINT_PROPERTIES),BUILD_THUMBPRINT="$(BUILD_THUMBPRINT)") \
			TARGET_BOARD_PLATFORM="$(TARGET_BOARD_PLATFORM)" \
			TARGET_CPU_ABI_LIST="$(TARGET_CPU_ABI_LIST)" \
			TARGET_CPU_ABI_LIST_32_BIT="$(TARGET_CPU_ABI_LIST_32_BIT)" \
			TARGET_CPU_ABI_LIST_64_BIT="$(TARGET_CPU_ABI_LIST_64_BIT)" \
			TARGET_CPU_ABI="$(TARGET_CPU_ABI)" \
			TARGET_CPU_ABI2="$(TARGET_CPU_ABI2)" \
			TARGET_AAPT_CHARACTERISTICS="$(TARGET_AAPT_CHARACTERISTICS)" \
	        bash $(BUILDINFO_SH) >> $@ //这里发现执行build/tools/buildinfo.sh脚本,写道目标文件build.prop中。
	$(hide) $(foreach file,$(system_prop_file), \
		if [ -f "$(file)" ]; then \
			echo "#" >> $@; \
			echo Target buildinfo from: "$(file)"; \
			echo "# from $(file)" >> $@; \
			echo "#" >> $@; \
			cat $(file) >> $@; \
		fi;)
	$(if $(ADDITIONAL_BUILD_PROPERTIES), \
		$(hide) echo >> $@; \
		        echo "#" >> $@; \
		        echo "# ADDITIONAL_BUILD_PROPERTIES" >> $@; \
		        echo "#" >> $@; )
	$(hide) $(foreach line,$(ADDITIONAL_BUILD_PROPERTIES), \
		echo "$(line)" >> $@;)
	$(hide) build/tools/post_process_props.py $@ $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SYSTEM_PROPERTY_BLACKLIST)
看到这了这里我们来到out目录下看看build.prop文件。


typedef struct camera_device {
    /**
     * camera_device.common.version must be in the range
     * HARDWARE_DEVICE_API_VERSION(0,0)-(1,FF). CAMERA_DEVICE_API_VERSION_1_0 is
     * recommended.
     */
    hw_device_t common;
    camera_device_ops_t *ops;
    void *priv;
} camera_device_t;
//hal的open方法
static int HAL_camera_device_open(const struct hw_module_t* module, //我们刚才在之前加载的camera_module指针
                                  const char *id,  //camera id
                                  struct hw_device_t** device) //上层传下来的camera_device_t结构,其实hw_device_t包含在开始处。
{
    ALOGV("DEBUG(%s):", __func__);

    int cameraId = atoi(id);
    if (cameraId < 0 || cameraId >= HAL_getNumberOfCameras()) {
        ALOGE("ERR(%s):Invalid camera ID %s", __func__, id);
        return -EINVAL;
    }

    if (g_cam_device) { //
        if (obj(g_cam_device)->getCameraId() == cameraId) {
            ALOGV("DEBUG(%s):returning existing camera ID %s", __func__, id);
            goto done;
        } else {
            ALOGE("ERR(%s):Cannot open camera %d. camera %d is already running!",
                    __func__, cameraId, obj(g_cam_device)->getCameraId());
            return -ENOSYS;
        }
    }

    g_cam_device = (camera_device_t *)malloc(sizeof(camera_device_t)); //底层已经准备好camera_device_t结构了
    if (!g_cam_device)
        return -ENOMEM;

    g_cam_device->common.tag     = HARDWARE_DEVICE_TAG; //对结构进行填充
    g_cam_device->common.version = 1;
    g_cam_device->common.module  = const_cast<hw_module_t *>(module); //这里在device中记录下module指针。
    g_cam_device->common.close   = HAL_camera_device_close; 

    g_cam_device->ops = &camera_device_ops; //这个是最关键的全部camera的操作方法。

    ALOGV("DEBUG(%s):open camera %s", __func__, id);

    g_cam_device->priv = new ExynosCameraHWInterface(cameraId, g_cam_device); //这个是整个camera 的硬件抽象对象。通过这个对象就可以控制
                                                                              //camera了,实际上也是这样做的。

done:
    *device = (hw_device_t *)g_cam_device; //将该结构的指针,传回到native framework中。
    ALOGV("DEBUG(%s):opened camera %s (%p)", __func__, id, *device);
    return 0;
}

二、图解camera_module和camera_device_t关系

      camer module在系统中转指camera模块,camera_device_t 转指某一个camera 设备。在流程上,native framwork 先加载在hal层定义的camer_module对象,然后通过camera_module的methods open方法填充camera_device_t 结构体,并最终获取到camera ops这一整个camera最重要的操作集合。下图中我们可以看到struct hw_module_t在camera_module最上面 而camera_device_t最开始保存的是struct hw_device_t. 由此我们平时在看代码时,要注意一些指针转换。

备注:在native framework 中通过open方法获取camera_device_t camera 结构。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值