一.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
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 结构。