Android8.0 硬體抽象層實現分析
眾所周知Android是基於Linux核心的開放性系統,我們可以看到Google開放的大部分作業系統實現程式碼。之所以說它是開放的而不是開源的,是因為Android系統程式碼不是完全開源的。這始於Linux核心開源協議和第三方廠商隱私安全的矛盾,為了繞過之一矛盾,Google構建了HAL,通過它在在遵守Linux協議的同時,又能保護第三方廠商的利益;既然能這麼牛,那麼Google是如何做到的呢,祕密就在硬體抽象層(HAL)。So, shutup, Let’s read the fucking sourcecode !
硬體抽象層 (HAL)。HAL 可定義一個標準介面以供硬體供應商實現,這可讓 Android 忽略較低級別的驅動程式實現。藉助 HAL,您可以順利實現相關功能,而不會影響或更改更高級別的系統。HAL 實現會被封裝成模組,並會由 Android 系統適時地載入。HAL 可定義一個標準介面以供硬體供應商實現,這可讓 Android 忽略較低級別的驅動程式實現。藉助 HAL,您可以順利實現相關功能,而不會影響或更改更高級別的系統。HAL 實現會被封裝成模組,並由 Android 系統適時地載入。
為了能夠清晰的分析硬體抽象層,我們選取power來分析,一來銜接上一篇中Hw中的服務,二來power部分結構較為簡單易於分析理解;
1. HARDWARE抽象結構
hardware/libhardware/include/hardware.h
主要是三個結構體 hw_module_t ,hw_module_methods_t ,hw_device_t
typedef struct hw_module_t {
/** tag must be initialized to HARDWARE_MODULE_TAG */
uint32_t tag;
uint16_t module_api_version;
#define version_major module_api_version
uint16_t hal_api_version;
#define version_minor hal_api_version
/** Identifier of module */ 模組識別ID
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;
模組方法結構體,用於開啟模組對應的裝置
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;
具體裝置對應的結構體
typedef struct hw_device_t {
/** tag must be initialized to HARDWARE_DEVICE_TAG */
uint32_t tag;
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;
hardware/libhardware//hardware.c
熟悉的方法 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);
}
動態庫檔案和數量
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"
};
static const int HAL_VARIANT_KEYS_COUNT =
(sizeof(variant_keys)/sizeof(variant_keys[0]));
檢查動態庫是否存在,查詢動態庫並執行動態庫載入
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 (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); //載入模組
}
按照 class_id, path, module 載入動態庫
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;
if (strncmp(path, "/system/", 8) == 0) {
/* If the library is in system partition, no need to check
* sphal namespace. Open it with dlopen.
*/
handle = dlopen(path, RTLD_NOW); //呼叫載入
} else {
handle = android_load_sphal_library(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;
}
2. Power的呼叫
回到上上一篇繫結模式下載入動態庫時的程式碼段會執行 hw_get_module 方法
//ServiceManagement.cpp -> PassthroughServiceManager 載入動態庫
IPower* HIDL_FETCH_IPower(const char* /* name */) {
const hw_module_t* hw_module = nullptr;
power_module_t* power_module = nullptr;
//取Power驅動模型結構
int err = hw_get_module(POWER_HARDWARE_MODULE_ID, &hw_module);
if (err) {
ALOGE("hw_get_module %s failed: %d", POWER_HARDWARE_MODULE_ID, err);
return nullptr;
}
if (!hw_module->methods || !hw_module->methods->open) {
power_module = reinterpret_cast<power_module_t*>(
const_cast<hw_module_t*>(hw_module)); //強轉
} else {
//開啟
err = hw_module->methods->open(
hw_module, POWER_HARDWARE_MODULE_ID,
reinterpret_cast<hw_device_t**>(&power_module));
if (err) {
ALOGE("Passthrough failed to load legacy HAL.");
return nullptr;
}
}
return new Power(power_module);
}
hardware/libhardware/include/power.h
標頭檔案中有一個包含 hw_module_t 的結構體
typedef struct power_module {
struct hw_module_t common; // 模組結構體 hw_module_t
void (*init)(struct power_module *module);
void (*setInteractive)(struct power_module *module, int on);
void (*powerHint)(struct power_module *module, power_hint_t hint,
void *data);
void (*setFeature)(struct power_module *module, feature_t feature, int state);
int (*get_platform_low_power_stats)(struct power_module *module,
power_state_platform_sleep_state_t *list);
ssize_t (*get_number_of_platform_modes)(struct power_module *module);
int (*get_voter_list)(struct power_module *module, size_t *voter);
} power_module_t;
hardware\libhardware\modules\power\power.c
static void power_init(struct power_module *module UNUSED_ARGUMENT)
{
}
static void power_set_interactive(struct power_module *module UNUSED_ARGUMENT,
int on UNUSED_ARGUMENT)
{
}
static void power_hint(struct power_module *module UNUSED_ARGUMENT,
power_hint_t hint,
void *data UNUSED_ARGUMENT) {
switch (hint) {
default:
break;
}
}
static struct hw_module_methods_t power_module_methods = {
.open = NULL,
};
//關鍵資料結構
struct power_module HAL_MODULE_INFO_SYM = {
.common = {
.tag = HARDWARE_MODULE_TAG,
.module_api_version = POWER_MODULE_API_VERSION_0_2,
.hal_api_version = HARDWARE_HAL_API_VERSION,
.id = POWER_HARDWARE_MODULE_ID,
.name = "Default Power HAL",
.author = "The Android Open Source Project",
.methods = &power_module_methods, //關聯 hw_module_methods_t
},
.init = power_init, //關聯本地方法
.setInteractive = power_set_interactive,
.powerHint = power_hint,
};
通過結構體轉換後就可以繞過Linux協議的限制,通過呼叫Linux核心的方法開啟位於核心之外的驅動。不得不說這種做法保護了各廠商的利益,卻失去了打造一個完全開源的作業系統的機會。今天就到這裡,再回。