HDMI-CEC(Consumer Electronics Control,消费电子控制)是 HDMI 接口中的一个重要功能,它允许通过一根 HDMI 线缆连接的多个设备互相通信和控制。简单来说,你可以用电视遥控器控制连接的机顶盒,或者用机顶盒遥控器控制电视的音量和开关,实现"一个遥控器控制所有设备"的体验。

在 Android 系统开发中,CEC 功能的实现涉及从硬件抽象层到上层应用框架的完整软件栈。下面我将从架构、核心机制、关键实现和调试方法几个方面来详细解析。


一、CEC 核心概念

在深入代码之前,先了解两个 CEC 协议中最基础的概念:

概念 说明 示例
物理地址 (Physical Address) 由 HDMI 树形拓扑结构决定,标识设备在网络中的物理位置。根设备(通常是电视)为 0.0.0.0,其 HDMI 端口1上的设备为 1.0.0.0,端口2上的设备为 2.0.0.0,以此类推。 电视:0.0.0.0,HDMI 1 上的盒子:1.0.0.0
逻辑地址 (Logical Address) 标识设备的类型,由系统通过"Polling Message"机制动态分配。同一个物理设备可以有多个逻辑地址(例如一个机顶盒可以同时是"播放设备"和"录音设备")。 电视:0,播放设备:4,录音设备:1

二、Android CEC 架构全景

Android 的 CEC 实现以 HdmiControlService 为核心,这是一个系统服务,负责协调硬件、策略和其他系统服务(如 TV Input Framework、Audio Service)。整体架构分为四层:


三、各层实现详解

1. Framework 层:HdmiControlService

HdmiControlService 是整个 CEC 功能的"大脑",位于 frameworks/base/services/core/java/com/android/server/hdmi/。它的核心职责包括:

逻辑地址分配

当设备启动或 HDMI 线缆插入时,系统需要为设备分配一个逻辑地址。这个过程通过发送 Polling Message(一种源地址和目标地址相同的空消息)来实现:

  1. 根据设备类型确定候选地址列表(例如 Recorder 设备可使用 1、2、9)

  2. 优先尝试上一次使用过的地址(preferred address)

  3. 依次发送 Polling Message,如果没有收到 ACK,则该地址可用

  4. 分配结果通过 AllocateLogicalAddressCallback 回调上报

按键事件处理

当用户按下遥控器按键时,CEC 功能需要将按键转发给正确的设备。Android TV 对按键处理有特殊规则:

按键 电视设备行为 源设备(机顶盒等)行为
POWER 切换自身电源状态 切换自身电源状态,电视电源跟随
TV_POWER 同 POWER 切换电视电源状态,自身电源跟随
HOME 开机、切换到 TV 输入、回主页 开机、回主页
音量键 自身处理或转发给 Soundbar 转发给电视或 Soundbar

2. HAL 层:硬件抽象

HAL 层是系统与硬件驱动的桥梁。根据 Android 版本不同,使用不同的接口:

Android U (14) 及之后版本优先使用 AIDL HAL:

interface IHdmiCec {
    Result addLogicalAddress(in CecLogicalAddress addr);
    void clearLogicalAddress();
    SendMessageResult sendMessage(in CecMessage message);
    void setCallback(in @nullable IHdmiCecCallback callback);
    void enableCec(in boolean value);
    void enableSystemCecControl(in boolean value);
    // ...
}

HAL 层需要实现的核心能力:

  • 地址管理addLogicalAddress / clearLogicalAddress

  • 消息收发sendMessage 和回调 IHdmiCecCallback.onCecMessage

  • 配置管理enableCecenableSystemCecControlsetLanguage

  • ARC/eARC 控制enableAudioReturnChannel

3. Kernel 层:CEC 驱动框架

Linux 内核提供了完整的 CEC 框架(drivers/media/cec),驱动开发者只需要实现几个核心回调:

struct cec_adap_ops {
    /* 使能/禁用硬件 */
    int (*adap_enable)(struct cec_adapter *adap, bool enable);
    /* 设置逻辑地址 */
    int (*adap_log_addr)(struct cec_adapter *adap, u8 logical_addr);
    /* 发送消息 */
    int (*adap_transmit)(struct cec_adapter *adap, u8 attempts,
                         u32 signal_free_time, struct cec_msg *msg);
    /* 接收消息回调(由驱动调用) */
    void (*received)(struct cec_adapter *adap, struct cec_msg *msg);
};

驱动开发的关键点

  1. 调用 cec_allocate_adapter() 分配适配器,传入 adap_ops 和 capabilities

  2. 在中断处理中,发送完成时调用 cec_transmit_done(),收到消息时调用 cec_received_msg()

  3. 通过 cec_register_adapter() 注册设备,会在 /dev/cecX 创建设备节点


四、调试与测试方法

常用调试命令

命令 用途
dumpsys hdmi_control 查看 HdmiControlService 状态,包括逻辑地址、物理地址、连接状态等
logcat -s HdmiControlService 过滤 CEC 服务日志
cat /sys/kernel/debug/cec/cec0/status 查看内核 CEC 适配器状态(需要 debugfs)
cec-ctl Linux 用户空间工具,可发送原始 CEC 命令(用于测试)

常见问题排查

  1. CEC 不工作

    • 确认硬件支持 CEC(某些电视需要开启"HDMI控制"或"设备控制"设置)

    • 检查 dumpsys hdmi_control 中 mCecEnabled 是否为 true

    • 确认 HAL 层是否正确加载(检查 logcat | grep HdmiCecController

  2. 逻辑地址分配失败

    • 查看是否有其他设备占用了相同类型的地址

    • 检查驱动是否正确实现了 adap_log_addr

  3. 按键无法控制设备

    • 确认 CEC 键值映射是否正确(参考 HdmiCecKeycode 类)

    • 检查 Android TV 的电源控制模式配置


五、总结

Android 系统中的 CEC 功能是一个从硬件到应用层的完整实现:

层次 核心组件 开发者职责
应用层 HdmiControlManager 调用系统 API 获取 CEC 状态和控制设备
Framework HdmiControlService Google 已实现,通常无需修改
HAL AIDL/HIDL 接口 实现 SoC 厂商提供的 CEC HAL 库
Kernel CEC Framework + Platform Driver 实现 cec_adap_ops 回调,处理硬件寄存器

对于嵌入式 Android 系统开发者来说,适配 CEC 功能的主要工作集中在 HAL 层和内核驱动的实现,而 Framework 层的 HdmiControlService 已经提供了完整的协议处理和策略管理。理解逻辑地址分配、按键映射规则和调试工具,是快速定位问题、完成功能适配的关键。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐