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)或makeAndroid.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.cppLed.h)。

2. 传统 HAL 模块接口(兼容旧架构)

若使用传统 HAL(无跨进程,直接加载 so 库),需定义struct hw_module_tstruct 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 的调用 → 操作内核驱动 → 返回结果 (驱动交互通过ioctlsysfsmmap等方式)。

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_thw_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 接口代码是否生成。
相关推荐
李小轰_Rex2 小时前
把手机变成听诊器!摄像头 30 秒隔空测心率 - 开箱即用
android·音视频开发
为码消得人憔悴3 小时前
Android perfetto - 记录分析memory
android·性能优化
尤老师FPGA4 小时前
使用ZYNQ芯片和LVGL框架实现用户高刷新UI设计系列教程(第四十二讲)
android·java·ui
成都大菠萝4 小时前
2-2-29 快速掌握Kotlin-过滤函数filter
android
成都大菠萝4 小时前
2-2-18 快速掌握Kotlin-扩展属性
android
成都大菠萝4 小时前
2-2-21 快速掌握Kotlin-定义扩展文件
android
成都大菠萝4 小时前
2-2-19 快速掌握Kotlin-可空类型扩展函数
android
成都大菠萝4 小时前
2-2-23 快速掌握Kotlin-apply函数详解
android
2501_916007475 小时前
iOS 证书如何创建,从能生成到能长期使用
android·macos·ios·小程序·uni-app·cocoa·iphone