Android HAL(Hardware Abstraction Layer,硬件抽象层)的核心作用是隔离 Framework 层与底层硬件驱动,使上层应用 / Framework 无需关注硬件实现细节,同时避免驱动代码直接暴露在用户空间。其开发流程围绕 "接口定义 - 实现 - 编译 - 集成 - 测试" 展开,以下是详细步骤(基于 Android 10 + 主流 HIDL 架构,兼容传统 HAL 模块):
一、前置准备:明确目标与搭建环境
1. 需求定义
- 明确硬件功能:如 "LED 控制(亮 / 灭)""传感器数据采集""摄像头预览" 等;
- 确定交互方式:Framework 与 HAL 的接口(如方法名、参数、返回值);
- 驱动依赖:确认内核驱动已实现(如 /dev/led 节点、sysfs 接口),HAL 需通过驱动提供的接口操作硬件。
2. 开发环境搭建
- 核心工具:AOSP 源码(需包含目标设备的 Device Tree)、NDK(可选,用于独立编译)、Android Studio(调试 APP);
- 依赖库:
libhardware(传统 HAL)、libhidlbase/libhidltransport(HIDL)、libutils(Android 基础工具); - 编译工具:
soong(Android.bp)或make(Android.mk,旧架构)、hidl-gen(HIDL 接口生成工具)。
二、核心步骤 1:定义 HAL 接口(HIDL/AIDL)
HAL 接口是 Framework 与 HAL 的 "契约",需明确暴露给上层的方法 / 数据结构。Android 8.0 + 推荐使用HIDL(Hardware Interface Definition Language) ,支持跨进程通信(Binderized HAL),旧架构可使用传统 HAL 模块(Passthrough HAL)。
1. HIDL 接口定义(推荐)
HIDL 文件以.hal后缀结尾,存放路径为 hardware/interfaces/<模块名>/<版本>/(如 hardware/interfaces/led/1.0/)。
示例:LED 的 HIDL 接口(ILed.hal)
// 包名:硬件接口+模块名+版本(需与路径一致)
package android.hardware.led@1.0;
// 接口定义(上层通过此接口调用HAL)
interface ILed {
// 方法1:设置LED状态(参数:ledId-LED编号,on-是否点亮;返回值:操作结果)
setLedState(int32_t ledId, bool on) generates (bool success);
// 方法2:获取LED当前状态
getLedState(int32_t ledId) generates (bool on, bool success);
};
关键规则:
-
数据类型:支持基础类型(int32_t、bool、string)、结构体(struct)、枚举(enum);
-
版本管理:接口变更时需升级版本(如 1.0→1.1),避免破坏兼容性;
-
生成绑定代码:通过
hidl-gen工具生成 C++/Java 绑定代码(上层 Framework 用 Java,HAL 实现用 C++):进入AOSP根目录,执行命令生成代码
m -j hidl-gen
hidl-gen -o hardware/interfaces/led/1.0/default/
-Lc++-impl
-randroid.hardware:hardware/interfaces/
-randroid.hidl:system/libhidl/transport/
android.hardware.led@1.0 -
生成后会在
default/目录下生成接口实现的模板(如Led.cpp、Led.h)。
2. 传统 HAL 模块接口(兼容旧架构)
若使用传统 HAL(无跨进程,直接加载 so 库),需定义struct hw_module_t和struct hw_device_t结构体(遵循libhardware规范),示例:
// hardware/libhardware/include/hardware/led.h
#include <hardware/hardware.h>
// 模块ID(Framework通过此ID查找HAL)
#define LED_HARDWARE_MODULE_ID "led"
// 设备结构体(存储硬件状态和方法指针)
struct led_device_t {
struct hw_device_t common; // 必须继承hw_device_t(HAL标准)
int (*setLedState)(struct led_device_t* dev, int ledId, bool on);
int (*getLedState)(struct led_device_t* dev, int ledId, bool* on);
};
三、核心步骤 2:实现 HAL 接口(C/C++)
HAL 实现需遵循接口定义,核心逻辑是:接收 Framework 的调用 → 操作内核驱动 → 返回结果 (驱动交互通过ioctl、sysfs、mmap等方式)。
1. HIDL 接口实现(示例:Led.cpp)
基于hidl-gen生成的模板,填充驱动交互逻辑:
// hardware/interfaces/led/1.0/default/Led.cpp
#include "Led.h"
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <android-base/logging.h>
// 内核驱动定义的IOCTL命令(需与驱动代码一致)
#define LED_IOCTL_SET_STATE _IOW('L', 0, int32_t)
#define LED_IOCTL_GET_STATE _IOR('L', 1, int32_t)
#define LED_DEVICE_PATH "/dev/led" // 驱动节点路径
namespace android {
namespace hardware {
namespace led {
namespace V1_0 {
namespace implementation {
// 构造函数:打开驱动节点
Led::Led() {
mFd = open(LED_DEVICE_PATH, O_RDWR);
if (mFd < 0) {
LOG(ERROR) << "Failed to open LED device: " << strerror(errno);
}
}
// 析构函数:关闭驱动节点
Led::~Led() {
if (mFd >= 0) {
close(mFd);
}
}
// 实现setLedState接口
Return<bool> Led::setLedState(int32_t ledId, bool on) {
if (mFd < 0) return false;
// 构造参数(ledId + 状态),通过ioctl通知驱动
int32_t param = (ledId << 1) | (on ? 1 : 0);
int ret = ioctl(mFd, LED_IOCTL_SET_STATE, ¶m);
if (ret < 0) {
LOG(ERROR) << "setLedState failed: " << strerror(errno);
return false;
}
return true;
}
// 实现getLedState接口
Return<void> Led::getLedState(int32_t ledId, getLedState_cb _hidl_cb) {
bool on = false;
bool success = false;
if (mFd >= 0) {
int32_t param = ledId;
int ret = ioctl(mFd, LED_IOCTL_GET_STATE, ¶m);
if (ret >= 0) {
on = (param & 1) != 0;
success = true;
}
}
_hidl_cb(on, success); // HIDL异步回调返回结果
}
// 注册HAL服务(Binderized模式)
ILed* HIDL_FETCH_ILed(const char* /* name */) {
return new Led();
}
} // namespace implementation
} // namespace V1_0
} // namespace led
} // namespace hardware
} // namespace android
2. 传统 HAL 模块实现(示例:led.cpp)
需实现hw_module_t和hw_device_t的初始化、方法绑定:
// hardware/libhardware/modules/led/led.cpp
#include <hardware/led.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
static int led_open(const struct hw_module_t* module, const char* id, struct hw_device_t** device) {
struct led_device_t* dev = (struct led_device_t*)malloc(sizeof(struct led_device_t));
dev->common.tag = HARDWARE_DEVICE_TAG;
dev->common.module = (struct hw_module_t*)module;
dev->common.close = led_close;
// 打开驱动节点
dev->fd = open("/dev/led", O_RDWR);
*device = &dev->common;
return 0;
}
static int led_set_state(struct led_device_t* dev, int ledId, bool on) {
// 驱动交互逻辑(同HIDL实现)
}
// 模块结构体(Framework通过LED_HARDWARE_MODULE_ID查找)
static struct hw_module_methods_t led_module_methods = {
.open = led_open
};
struct hw_module_t HAL_MODULE_INFO_SYM = {
.tag = HARDWARE_MODULE_TAG,
.id = LED_HARDWARE_MODULE_ID,
.methods = &led_module_methods
};
四、核心步骤 3:编译配置(Android.bp/Android.mk)
需编写编译脚本,将 HAL 代码编译为动态库(.so),并指定依赖库、编译选项。Android 10 + 推荐使用Android.bp(Soong 构建系统)。
示例:HIDL HAL 的 Android.bp
// hardware/interfaces/led/1.0/default/Android.bp
cc_library_shared {
name: "android.hardware.led@1.0-impl", // 库名(需包含版本)
relative_install_path: "hw", // 安装路径:/vendor/lib64/hw/
vendor: true, // 标记为vendor分区库(硬件相关库默认放vendor)
srcs: [
"Led.cpp", // 源码文件
],
// 依赖库(HIDL基础库、驱动交互库)
shared_libs: [
"liblog",
"libutils",
"libhidlbase",
"libhidltransport",
"android.hardware.led@1.0", // 依赖HIDL接口库(生成的绑定代码)
],
// 编译选项(C++11、调试符号)
cflags: [
"-Wall",
"-Wextra",
"-Werror",
"-std=c++11",
],
}
// 可选:编译HIDL服务启动脚本(Binderized HAL需独立进程)
cc_binary {
name: "led-hal-service",
vendor: true,
srcs: ["service.cpp"], // 简单的main函数,启动HIDL服务
shared_libs: [
"liblog",
"libhidlbase",
"libhidltransport",
"android.hardware.led@1.0",
],
}
编译命令(AOSP 根目录)
# 编译HIDL接口库(生成Java/C++绑定代码)
m android.hardware.led@1.0-java android.hardware.led@1.0-cpp
# 编译HAL实现库和服务
m android.hardware.led@1.0-impl led-hal-service
# 生成目标设备的镜像(如system.img、vendor.img)
m bootimage vendorimage
五、核心步骤 4:系统集成与权限配置
1. 部署 HAL 库
编译生成的.so库(如android.hardware.led@1.0-impl.so)需部署到设备的/vendor/lib64/hw/(64 位)或/vendor/lib/hw/(32 位)目录。
2. 注册 HAL 服务(Binderized 模式)
-
编写启动脚本:
vendor/<厂商>/<设备>/init.led-hal.rc,让系统启动时加载 HAL 服务:service led-hal /vendor/bin/led-hal-service
class hal
user root
group root system
配置 VINTF Manifest:在vendor/<厂商>/<设备>/manifest.xml中声明 HAL,让 Framework 识别:
<manifest version="1.0" type="device">
<hal format="hidl">
<name>android.hardware.led</name>
<version>1.0</version>
<interface>
<name>ILed</name>
<instance>default</instance>
</interface>
</hal>
</manifest>
3. SELinux 权限配置(关键)
Android 强制启用 SELinux,需给 HAL 服务和驱动节点配置权限,否则会被拒绝访问:
-
定义 HAL 服务的 SELinux 类型:
device/<厂商>/<设备>/sepolicy/vendor/led_hal.te:定义服务类型
type led_hal_service, domain;
type led_hal_service_exec, exec_type, vendor_file_type, file_type;允许服务访问驱动节点
allow led_hal_service led_device:chr_file rw_file_perms;
允许Binder通信
allow led_hal_service hal_client_domain:binder call;
关联服务与 SELinux 类型:device/<厂商>/<设备>/sepolicy/vendor/file_contexts:
/vendor/bin/led-hal-service u:object_r:led_hal_service_exec:s0
/dev/led u:object_r:led_device:s0
六、核心步骤 5:测试验证
1. 单元测试(HAL 层)
使用gtest编写测试用例,直接调用 HAL 接口:
// hardware/interfaces/led/1.0/default/tests/LedTest.cpp
#include <gtest/gtest.h>
#include <android/hardware/led/1.0/ILed.h>
#include <hidl/GtestPrinter.h>
#include <hidl/ServiceManagement.h>
using namespace android::hardware::led::V1_0;
using namespace android::hardware;
TEST(LedTest, SetAndGetState) {
// 获取HAL服务实例
sp<ILed> led = ILed::getService();
ASSERT_NE(led, nullptr) << "Failed to get ILed service";
// 测试设置LED 0点亮
bool success = led->setLedState(0, true);
ASSERT_TRUE(success) << "setLedState(0, true) failed";
// 测试获取LED 0状态
bool on, getSuccess;
led->getLedState(0, [&](bool _on, bool _success) {
on = _on;
getSuccess = _success;
});
ASSERT_TRUE(getSuccess) << "getLedState(0) failed";
ASSERT_TRUE(on) << "LED 0 should be on";
}
3. 硬件验证
-
烧录编译好的
vendor.img到设备; -
启动设备后,通过
adb shell检查 HAL 服务是否运行:adb shell ps -A | grep led-hal-service
运行测试用例或 APP,观察硬件是否正常响应(如 LED 点亮),通过logcat查看日志:
adb logcat -s "Led"
七、关键补充:HAL 的两种模式
1. Binderized HAL(推荐)
- 基于 Binder 跨进程通信,HAL 运行在独立进程(如
led-hal-service); - 优势:Framework 与 HAL 隔离,稳定性高,支持多进程调用;
- 适用场景:Android 8.0 + 的新硬件模块。
2. Passthrough HAL(传统)
- 无跨进程,Framework 直接加载 HAL 的
.so库(如led.default.so); - 优势:性能开销小,兼容旧驱动;
- 适用场景:旧设备、对性能要求极高的模块。
八、常见问题与避坑
- 驱动节点访问失败 :检查
/dev/节点是否存在、权限是否正确(如chmod 666 /dev/led)、SELinux 是否放行; - HAL 服务启动失败 :检查
init.rc脚本路径、服务名是否正确,日志中是否有权限拒绝(SELinux); - Framework 无法获取 HAL 服务:检查 VINTF Manifest 配置、HAL 库安装路径、接口版本是否匹配;
- 编译错误 :确认依赖库是否添加(如
libhidltransport)、HIDL 接口代码是否生成。