上一节Android 13 Camera HAL启动流程(1)主要介绍了CameraProvider进程启动后加载[email protected]库并执行LegacyCameraProviderImpl_2_4的初始化函数,本节从LegacyCameraProviderImpl_2_4的初始化流程开始继续往下讲解。
// hardware/interfaces/camera/provider/2.4/default/LegacyCameraProviderImpl_2_4.cpp
LegacyCameraProviderImpl_2_4::LegacyCameraProviderImpl_2_4() :
camera_module_callbacks_t({
sCameraDeviceStatusChange,
sTorchModeStatusChange}) {
mInitFailed = initialize();
}
// initialize()函数的实现
bool LegacyCameraProviderImpl_2_4::initialize() {
camera_module_t *rawModule;
// 1. 通过hw_get_module加载对应的so库,这个库是由厂商提供的,一般只以so库的方式提供,
// 不过也有情况会提供so库的源码,不过核心的地方还是会封装在别的so库中。
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));
return true;
}
// 这里先省略后面的代码,将hw_get_module介绍完后我们再返回来继续
...
}
下面开始介绍hw_get_module函数。
第一个参数为CAMERA_HARDWARE_MODULE_ID ,定义的地方为
第二个参数为出参
// hardware/libhardware/include/hardware/camera_common.h
#define CAMERA_HARDWARE_MODULE_ID "camera"
// hardware/libhardware/hardware.c
int hw_get_module(const char *id, const struct hw_module_t **module)
{
return hw_get_module_by_class(id, NULL, module);
}
int hw_get_module_by_class(const char *class_id, const char *inst,
const struct hw_module_t **module)d_name
{
int i = 0;
char prop[PATH_MAX] = {
0};
char path[PATH_MAX] = {
0};
char name[PATH_MAX] = {
0};
char prop_name[PATH_MAX] = {
0};
// inst为NULL, 即为false
if (inst)
snprintf(name, PATH_MAX, "%s.%s", class_id, inst);
else
// name 为 “camera”
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.
*/
/* First try a property specific to the class and possibly instance */
// 查看ro.hardware.camera属性值是否为空,这个实际上定义了camera hal so库的名称
// 这个so库是厂商专门封装的so库,这样可以将自己的核心代码保持闭源
// 最后的标准名称应为path/camera.xxx.so, 这里的xxx就是prop属性值.
// 对于这个so库,google提供了一个默认实现,
// 位于hardware/libhardware/modules/camera/3_4, so库名称为camera.v4l2_test
// 如果这个属性值为空的话,会去判断其他几个属性值,下面的代码会看到
// 查找路径的话会依次从"/odm/lib64/hw" "/vendor/lib64/hw" "/system/lib64/hw"
// 寻找,一般在"/vendor/lib64/hw"路径,下面的代码就不详细分析了,比较简单。
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 */
// 如果ro.hardware.camera属性没有定义,则从如下几个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"
// };
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 */
// 如果以上几个属性均为定义或者对应so库未找到,则判断是否存在camera.default.so是否存在
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. */
// 找到对应的so库后,
return load(class_id, path, module);
}
// 我们接着分析上面找到so库后的load函数,这个函数很重要
/**
* Load the file defined by the variant and if successful
* return the dlopen handle and the hmi.
* @return 0 = success, !0 = failure.
*/
// 别忘了参数值:
// id: camera
// path: 上面找到的路径,假设为 /vendor/lib64/hw/
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;
#ifdef __ANDROID_VNDK__
const bool try_system = false;
#else
const bool try_system = true;
#endif
/*
* 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
*/
if (try_system &&
strncmp(path, HAL_LIBRARY_PATH1, strlen(HAL_LIBRARY_PATH1)) == 0) {
/* If the library is in system partition, no need to check
* sphal namespace. Open it with dlopen.
*/
//如果位于"/system/lib64/hw",则使用dlopen函数
handle = dlopen(path, RTLD_NOW);
} else {
#if defined(__ANDROID_RECOVERY__)
// recovery模式的话走这里
handle = dlopen(path, RTLD_NOW);
#else
// 非Recovery的话最终走的是这里
handle = android_load_sphal_library(path, RTLD_NOW);
#endif
}
if (handle == NULL) {
char const *err_str = dlerror();
ALOGE("load: module=%s\n%s", path, err_str?err_str:"unknown");
status = -EINVAL;
goto done;
}
// 上面的无非就是load so,拿到handle,接下来就是关键之处了
/* Get the address of the struct hal_module_info. */
// HAL_MODULE_INFO_SYM_AS_STR的定义如下
// #define HAL_MODULE_INFO_SYM_AS_STR "HMI"
// 所以sym也为 "HMI”
// 注意这个值是固定的,是实现hw_get_module实现的一个标准
// 厂商定义hal的时候,在so库里面必须定义一个结构体名称为“HMI”
// 这里是调用so库的入口
// 我们以Google的默认 camera hal so为例看下
// 这里定义了一个类型为camera_module_t,名称为HAL_MODULE_INFO_SYM的结构体
// 而HAL_MODULE_INFO_SYM的名称就是 “HMI”
// 找到这里后会调用该结构体变量的.common.methods.open函数
// 对应到这里的话就是v4l2_module_methods.open
// 最终会调用v4l2_camera_hal::open_dev,如下
// static hw_module_methods_t v4l2_module_methods = {
// .open = v4l2_camera_hal::open_dev};
// camera_module_t HAL_MODULE_INFO_SYM __attribute__((visibility("default"))) = {
// .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 = "V4L2 Camera HAL v3",
// .author = "The Android Open Source Project",
// .methods = &v4l2_module_methods,
// .dso = nullptr,
// .reserved = {0},
// },
// .get_number_of_cameras = v4l2_camera_hal::get_number_of_cameras,
// .get_camera_info = v4l2_camera_hal::get_camera_info,
// .set_callbacks = v4l2_camera_hal::set_callbacks,
// .get_vendor_tag_ops = v4l2_camera_hal::get_vendor_tag_ops,
// .open_legacy = v4l2_camera_hal::open_legacy,
// .set_torch_mode = v4l2_camera_hal::set_torch_mode,
// .init = nullptr,
// .get_physical_camera_info = nullptr,
// .reserved = {nullptr, nullptr}};
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;
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, hmi, handle);
}
// 返回hmi,这个是名称为“HMI”的结构体变量在so库中的地址,类型为hw_module_t
// 但是结构体定义的地方却是camera_module_t类型的,那么这两者有啥关系呢
// 通过查看定义就知道camera_module_t结构体的第一个成员就是hw_module_t common
// 这样是为了方便直接调用.common.methods.open函数吧
*pHmi = hmi;
return status;
}
// 上面的.common.methods.open函数是so库调用的入口和起点,记住这个是标准定义,如果不这么定义的话,
// hw_get_module就会执行失败,无法加载so库。那么这个open函数主要实现什么功能呢,可能不同厂家实现都不一样,
// 但是有一个是必须的就是遍历camera节点,检查video能力等,我们可以看下Google的默认实现
static int open_dev(const hw_module_t* module,
const char* name,
hw_device_t** device) {
return gCameraHAL.openDevice(module, name, device);
}
// 先看gCameraHAL的构造函数,主要做了以下几件事情
// 1. 遍历/dev/videoX节点
// 2. 尝试open节点并查询video能力
// 3. 构建V4L2Camera对象,放入mCameras
V4L2CameraHAL::V4L2CameraHAL() : mCameras(), mCallbacks(NULL) {
HAL_LOG_ENTER();
// Adds all available V4L2 devices.
// List /dev nodes.
DIR* dir = opendir("/dev");
if (dir == NULL) {
HAL_LOGE("Failed to open /dev");
return;
}
// Find /dev/video* nodes.
dirent* ent;
std::vector<std::string> nodes;
// 遍历/dev/video*节点
while ((ent = readdir(dir))) {
std::string desired = "video";
size_t len = desired.size();
if (strncmp(desired.c_str(), ent->d_name, len) == 0) {
if (strlen(ent->d_name) > len && isdigit(ent->d_name[len])) {
// ent is a numbered video node.
nodes.push_back(std::string("/dev/") + ent->d_name);
HAL_LOGV("Found video node %s.", nodes.back().c_str());
}
}
}
// Test each for V4L2 support and uniqueness.
std::unordered_set<std::string> buses;
std::string bus;
v4l2_capability cap;
int fd;
int id = 0;
for (const auto& node : nodes) {
// Open the node.
// 打开节点,拿到fd,用于后面ioctl
fd = TEMP_FAILURE_RETRY(open(node.c_str(), O_RDWR));
if (fd < 0) {
HAL_LOGE("failed to open %s (%s).", node.c_str(), strerror(errno));
continue;
}
// Read V4L2 capabilities.
// 查询是否可以作为Camera设备使用
if (TEMP_FAILURE_RETRY(ioctl(fd, VIDIOC_QUERYCAP, &cap)) != 0) {
HAL_LOGE(
"VIDIOC_QUERYCAP on %s fail: %s.", node.c_str(), strerror(errno));
} else if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
HAL_LOGE("%s is not a V4L2 video capture device.", node.c_str());
} else {
// If the node is unique, add a camera for it.
bus = reinterpret_cast<char*>(cap.bus_info);
if (buses.insert(bus).second) {
HAL_LOGV("Found unique bus at %s.", node.c_str());
// 构建V4l2Camera对象用于后续作用
std::unique_ptr<V4L2Camera> cam(V4L2Camera::NewV4L2Camera(id++, node));
if (cam) {
mCameras.push_back(std::move(cam));
} else {
HAL_LOGE("Failed to initialize camera at %s.", node.c_str());
}
}
}
close(fd);
}
}
好的,本节主要讲了hw_get_module的函数实现,Camera HAL的启动流程我们就先讲到这里,当然还没结束,具体So库的地方也是简单介绍了一下。后面讲CameraServer进程启动流程的时候还会涉及到CameraProvider进程,到时候我们继续。对于具体厂商So库的实现,我们只把关键的地方介绍一下(以Google默认实现为例),不会做深入的分析,后续有机会的话会把几个大平台的Camera HAL实现大概介绍一下(以网上公开资料讲解),或者尝试自己去写一个完整的Camera HAL实现,这就是后话了。