Android Hal层开发流程

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, &param);
    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, &param);
        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);
  • 优势:性能开销小,兼容旧驱动;
  • 适用场景:旧设备、对性能要求极高的模块。

八、常见问题与避坑

  1. 驱动节点访问失败 :检查/dev/节点是否存在、权限是否正确(如chmod 666 /dev/led)、SELinux 是否放行;
  2. HAL 服务启动失败 :检查init.rc脚本路径、服务名是否正确,日志中是否有权限拒绝(SELinux);
  3. Framework 无法获取 HAL 服务:检查 VINTF Manifest 配置、HAL 库安装路径、接口版本是否匹配;
  4. 编译错误 :确认依赖库是否添加(如libhidltransport)、HIDL 接口代码是否生成。
相关推荐
传奇开心果编程3 小时前
【Compose Multiplatform 跨端开发学与练】第8课 资源管理与主题
android·windows·学习·ios·kotlin·web·composer
传奇开心果编程4 小时前
【Compose Multiplatform 跨端开发学与练】第9课 测试与调试
android·学习·macos·ios·kotlin·web·composer
传奇开心果编程4 小时前
【Compose Multiplatform 跨端开发学与练】第4课 导航与路由
android·windows·学习·ui·ios·kotlin·composer
传奇开心果编程4 小时前
【Compose Multiplatform 跨端开发学与练】第6课 状态管理与架构
android·学习·ui·ios·架构·kotlin·composer
事圆则缓5 小时前
Android AOSP 定制常见概念:源码目录、系统镜像与刷机流程
android
传奇开心果编程5 小时前
【Compose Multiplatform 跨端开发学与练】第2课 Compose 基础语法
android·windows·学习·ui·ios·kotlin·composer
supabc1235 小时前
Celium:连接 Windows、Mac、Linux 与 Android,让远程访问和设备管理更简单
android·linux·windows·macos·远程访问·网络管理·celium
Dovis(誓平步青云)5 小时前
导览音频切换太快,旧讲解不能覆盖新展品
android·前端·javascript·ecmascript·音视频·宠物
五彩小白1 天前
自回归和上下文
android
事圆则缓1 天前
Android 多渠道打包实战:Product Flavors、签名、AAB 与发包校验
android