本文摘自 罗升阳的《Anroid系统源代码情景分析》,更新至Android7.0分析
一、概念
一、Android系统为硬件抽象层中的模块接口定义了编写规范,我们必须按照这个规范来编写自己的硬件模块接口。
二、Android系统的硬件抽象层以模块的形式来管理各个硬件访问接口。每一个硬件模块都对应有一个动态链接库文件,这些动态链接库文件的命名需要符合一定的规范。同时,在系统内部,每一个硬件抽象层模块都是用结构体**hw_module_t
来描述,而硬件设备则使用结构体hw_device_t
**来描述。
二、硬件抽象层模块文件命名规范
硬件抽象层模块文件的命名规范定义在
hardware/libhardware/hardware.c,基于android 7.0
,文件中,
/**
* There are a set of variant filename for modules. The form of the filename
* is "<MODULE_ID>.variant.so" so for the led module the Dream variants
* of base "ro.product.board", "ro.board.platform" and "ro.arch" would be:
*
* led.trout.so
* led.msm7k.so
* led.ARMV6.so
* led.default.so
*/
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"
};
以上代码和注释的意思是:硬件抽象模块文件的命名规范为**“<MODULE_ID>.variant.so”**,其中MODULE_ID 标识模块的ID,variant 表示四个系统属性
ro.hardware
、ro.product.board
、ro.board.platform
、ro.arch
之一。
size=3
系统在加载硬件抽象层模块时,依次按照ro.hardware.MODULE_ID
、ro.hardware
、ro.product.board
、ro.board.platform
、ro.arch
的顺序来取他们的属性值。
如果其中一个系统属性存在,那么就把它的值作为variant的值,然后在检查对应的文件是否存在,如果存在,那么就找到要加载的硬件抽象层文件了;
如果四个属性都不存在,或者四个属性对应的系统硬件抽象层文件不存在,就是用 “<MODULE_ID>.default.so” 来作为要加载的硬件抽象层模块文件的名称
三、硬件抽象层模块结构体定义规范
结构体 hw_module_t
和 hw_device_t
及其相关的其他结构体定义在文件 hardware/libhardware/include/hardware/hardware.h,基于android 7.0
中。
struct hw_module_t
/*
* Value for the hw_module_t.tag field
*/
#define MAKE_TAG_CONSTANT(A,B,C,D) (((A) << 24) | ((B) << 16) | ((C) << 8) | (D))
#define HARDWARE_MODULE_TAG MAKE_TAG_CONSTANT('H', 'W', 'M', 'T')
/**
* Every hardware module must have a data structure named HAL_MODULE_INFO_SYM
* and the fields of this data structure must begin with hw_module_t
* followed by module specific information.
*/
typedef struct hw_module_t {
/** tag must be initialized to HARDWARE_MODULE_TAG */
uint32_t tag;
/**
* The API version of the implemented module. The module owner is
* responsible for updating the version when a module interface has
* changed.
*
* The derived modules such as gralloc and audio own and manage this field.
* The module user must interpret the version field to decide whether or
* not to inter-operate with the supplied module implementation.
* For example, SurfaceFlinger is responsible for making sure that
* it knows how to manage different versions of the gralloc-module API,
* and AudioFlinger must know how to do the same for audio-module API.
*
* The module API version should include a major and a minor component.
* For example, version 1.0 could be represented as 0x0100. This format
* implies that versions 0x0100-0x01ff are all API-compatible.
*
* In the future, libhardware will expose a hw_get_module_version()
* (or equivalent) function that will take minimum/maximum supported
* versions as arguments and would be able to reject modules with
* versions outside of the supplied range.
*/
uint16_t module_api_version;
#define version_major module_api_version
/**
* version_major/version_minor defines are supplied here for temporary
* source code compatibility. They will be removed in the next version.
* ALL clients must convert to the new version format.
*/
/**
* The API version of the HAL module interface. This is meant to
* version the hw_module_t, hw_module_methods_t, and hw_device_t
* structures and definitions.
*
* The HAL interface owns this field. Module users/implementations
* must NOT rely on this value for version information.
*
* Presently, 0 is the only valid value.
*/
uint16_t hal_api_version;
#define version_minor hal_api_version
/** Identifier of module */
const char *id;
/** Name of this module */
const char *name;
/** Author/owner/implementor of the module */
const char *author;
/** Modules methods */
struct hw_module_methods_t* methods;
/** module's dso */
void* dso;
#ifdef __LP64__
uint64_t reserved[32-7];
#else
/** padding to 128 bytes, reserved for future use */
uint32_t reserved[32-7];
#endif
} hw_module_t;
一、从hw_module_t 注释可以看出,每个硬件抽象层模块都必须有一个名称为 HAL_MODULE_INFO_SYM 的结构体,且这个结构体的第一个字段必须是
hw_module_t
二、hw_module_t.tag
的值必须是 HARDWARE_MODULE_TAG ,即(H) << 24) | ((W) << 16) | ((M) << 8) | (T)
,用来标识硬件抽象层模块结构体。
三、hw_module_t.methods定义了一个硬件抽象层模块的操作列表:
typedef struct hw_module_methods_t {
/** Open a specific device */
int (*open)(const struct hw_module_t* module, const char* id,
struct hw_device_t** device);
} hw_module_methods_t;
hw_module_methods_t 只有一个成员变量,他是一个函数指针,用来打开硬件抽闲层模块中的硬件设备。其中参数module表示要打开的硬件设备所在模块;参数id表示要打开的硬件设备的ID;参数device是一个输出参数,用来描述一个已经打开的硬件设备
struct hw_device_t
#define HARDWARE_DEVICE_TAG MAKE_TAG_CONSTANT('H', 'W', 'D', 'T')
/**
* Every device data structure must begin with hw_device_t
* followed by module specific public methods and attributes.
*/
typedef struct hw_device_t {
/** tag must be initialized to HARDWARE_DEVICE_TAG */
uint32_t tag;
/**
* Version of the module-specific device API. This value is used by
* the derived-module user to manage different device implementations.
*
* The module user is responsible for checking the module_api_version
* and device version fields to ensure that the user is capable of
* communicating with the specific module implementation.
*
* One module can support multiple devices with different versions. This
* can be useful when a device interface changes in an incompatible way
* but it is still necessary to support older implementations at the same
* time. One such example is the Camera 2.0 API.
*
* This field is interpreted by the module user and is ignored by the
* HAL interface itself.
*/
uint32_t version;
/** reference to the module this device belongs to */
struct hw_module_t* module;
/** padding reserved for future use */
#ifdef __LP64__
uint64_t reserved[12];
#else
uint32_t reserved[12];
#endif
/** Close this device */
int (*close)(struct hw_device_t* device);
} hw_device_t;
一、从hw_device_t 注释可以看出硬件抽象层模块的每一个硬件设备都必须自定义一个硬件设备结构体,且他的第一个成员变量的类型必须是
hw_device_t
二、hw_device_t.tag
的值必须是 **HARDWARE_DEVICE_TAG ** ,即(H) << 24) | ((W) << 16) | ((D) << 8) | (T)
,用来标识硬件抽象中的硬件设备结构体。
三、结构体hw_device_t
的成员变量close是一个函数指针,它用来关闭一个硬件设备。
注意:硬件抽象层中的硬件设备是由其所在的模块提供接口来打开的,而关闭是由硬件设备自身提供接口来完成的
四、硬件抽象层模块的加载过程
在Android硬件抽象层中,负责加载硬件抽象层模块的函数是,它的原型如下:
/**
* 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.<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);
返回0表示加载成功,<0则表示失败,class_id 表示具体要加载的模块名称,例如audio,inst 为NULL,hw_module_t为输出参数。
接下来分析具体实现:
int hw_get_module_by_class(const char *class_id, const char *inst,
const struct hw_module_t **module)
{
int i = 0;
char prop[PATH_MAX] = {0};
char path[PATH_MAX] = {0};
char name[PATH_MAX] = {0};
char prop_name[PATH_MAX] = {0};
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.
*/
/* 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. */
return load(class_id, path, module);
}
在第三小节我们已经说过了,硬件抽象层模块动态库的命名规范,首先系统会按照规范去获取ro.hardware.ID
以及variant_keys
中的属性值 ,判断是否存在,不存在使用default
,接着通过hw_module_exists
去拼接完整的动态库路径和名称,并访问,访问成功通过load函数
去加载该硬件抽象层模块动态库,首先看到hw_module_exists
这个函数:
/** Base path of the hal modules */
#if defined(__LP64__)
#define HAL_LIBRARY_PATH1 "/system/lib64/hw"
#define HAL_LIBRARY_PATH2 "/vendor/lib64/hw"
#define HAL_LIBRARY_PATH3 "/odm/lib64/hw"
#else
#define HAL_LIBRARY_PATH1 "/system/lib/hw"
#define HAL_LIBRARY_PATH2 "/vendor/lib/hw"
#define HAL_LIBRARY_PATH3 "/odm/lib/hw"
#endif
/*
* Check if a HAL with given name and subname exists, if so return 0, otherwise
* otherwise return negative. On success path will contain the path to the HAL.
*/
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_PATH3, name, subname);
if (access(path, R_OK) == 0)
return 0;
snprintf(path, path_len, "%s/%s.%s.so",
HAL_LIBRARY_PATH2, name, subname);
if (access(path, R_OK) == 0)
return 0;
snprintf(path, path_len, "%s/%s.%s.so",
HAL_LIBRARY_PATH1, name, subname);
if (access(path, R_OK) == 0)
return 0;
return -ENOENT;
}
访问路径优先级分别是:HAL_LIBRARY_PATH3(/odm/lib/hw)、 HAL_LIBRARY_PATH2(/vendor/lib/hw)、HAL_LIBRARY_PATH1 (/vendor/lib/hw)
最后通过access函数
去访问该路径名,访问成功返回0,否则<0
最后访问成功,来到load函数:
/**
* Load the file defined by the variant and if successful
* return the dlopen handle and the hmi.
* @return 0 = success, !0 = failure.
*/
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
*/
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. */
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, *pHmi, handle);
}
*pHmi = hmi;
return status;
}
硬件抽象层模块文件实际上是一个动态链接库文件,即so库文件。因此,首先调用
dlopen函数
将他加载到内存中
接着通过dlsym函数
来获得里面名称为HAL_MODULE_INFO_SYM_AS_STR的符号。这个HAL_MODULE_INFO_SYM_AS_STR指向的是一个自定义硬件抽象层模块结构体,它包含了对应的硬件抽象层模块的所有信息。HAL_MODULE_INFO_SYM_AS_STR是一个宏,它的值定义为“HMI”
根据硬件抽象层的编写规范,每一个硬件抽象层模块都必须包含一个名称为HMI
的符号,而且这个符号的第一个成员变量的类型必须是hw_module_t
,因此,可以安全的将模块中的HMI
符号转换成一个hw_module_t
结构体指针。
得到hw_module_t
指针之后,通过strcmp函数
来验证加载得到的硬件抽象层ID是否与所要求加载的硬件抽象层模块ID一致。如果不一致,就说明出错了,函数返回一个错误值:-EINVAL
、
最后将成功加载后得到的模块句柄值handle
保存在hw_module_t
结构体指针hmi
的成员变量dso
中,然后将它返回给调用者。