Open Headunit项目架构总览:从USB接入到Android Auto投屏的完整数据链路
【免费下载链接】open-headunitHeadunit App for displaying Android Auto项目地址: https://gitcode.com/GitHub_Trending/he/open-headunit
Open Headunit 是一款开源的 Android Auto 投屏接收器应用,它能把安卓平板或手机变成一台"Android Auto 车机"(Headunit),接收手机端的 Android Auto 画面与音频并全屏呈现。本文带你从源码角度梳理它的整体架构,看懂从 USB 接入、SSL 握手、AAP 协议解析到音视频解码投屏的完整数据链路。
🚗 Open Headunit 能做什么?
简单来说,它的角色相当于车机里的"Android Auto 接收端":
- USB 有线接入:手机通过 USB 线连接平板,模拟 Android Auto 主机协议通信
- 无线接入:支持 Wi-Fi Direct(P2P)、车机热点、Headunit Server 等多种连接策略
- Self-Mode 模式:同一台设备上既当"手机"又当"车机",用于开发调试
- 完整投屏:解码手机端推送的视频流并全屏渲染,同时输出音频、回传触控与按键事件
项目根目录的 README.md 有详细的使用说明与已知问题清单。
🧩 四层架构:UI、连接、协议、解码
Open Headunit 的源码位于app/src/main/java/com/andrerinas/openheadunit/,按职责可以划分为四层:
| 层级 | 核心目录 | 职责 |
|---|---|---|
| UI 层 | main/ | 主页、设置、网络列表、自动连接等界面 |
| 连接层 | connection/ | USB / Wi-Fi / Self-Mode 三种接入方式的物理链路管理 |
| 协议层 | aap/ | Android Auto Protocol(AAP)消息收发、SSL 握手 |
| 解码层 | decoder/ | 音频解码(AAC)、视频解码(H.264 / HEVC) |
连接层内部又按接入方式分目录组织:
- USB 相关:
connection/usb/,含设备识别、权限申请、端点选择等策略 - Wi-Fi 相关:
connection/wifi/,又细分为direct/(Wi-Fi Direct)、modes/nativeaa/(原生无线协议握手)、modes/helper/(配合 Wireless Helper 应用) - Self-Mode 启动器:
connection/self/launchers/ - 投屏连接抽象:
connection/projection/,定义统一的 ProjectionConnection.kt 接口
🔌 完整数据链路:从 USB 接入到投屏的 5 步
整个连接生命周期由核心类 CommManager.kt 统一管理。它在类注释中直接给出了状态机图(CommManager.kt#L40-L67):
Disconnected → Connecting → Connected → StartingTransport → HandshakeComplete → TransportStarted → Disconnected
这正好对应下面 5 个阶段,ConnectionState状态定义见 CommManager.kt#L91-L122。
步骤 1:物理连接建立(Connecting → Connected)
USB 场景:UsbReceiver.kt 监听 USB 设备插入广播,UsbDeviceIdentityPolicy.kt 判断设备是否是 Android 手机,随后申请设备权限并打开 USB 接口。
无线场景:Wi-Fi Direct 由 WifiDirectManager.kt 负责组建 P2P 群组;原生无线模式则由 NativeAaHandshakeManager.kt 完成与 Android Auto 的握手,最终建立 TCP 连接。
无论哪种方式,最终都封装为ProjectionConnection(USB 直连、libusb 或 Socket 三种实现),物理链路就绪后状态推进到Connected。
步骤 2:SSL/TLS 握手(StartingTransport → HandshakeComplete)
AAP 协议全程运行在加密通道上。CommManager持有全局唯一的AapSslContext(基于 SslContextFactory.kt 构建),支持 TLS 会话恢复,让断线重连时的握手更快。SSL 完成后状态进入HandshakeComplete,并触发投屏界面启动。
步骤 3:AAP 协议层收发消息(TransportStarted)
传输层由 AapTransport.kt 实现,生命周期为startHandshake → startReading → 消息循环。消息采用"通道 + 类型"的二进制帧格式,具体帧拆分逻辑在 AapMessageFraming.kt。
协议消息体由 Protobuf 定义,源码在 app/src/main/proto/ 目录:
- common.proto:通用消息(Hello、ByeBye 等)
- media.proto / playback.proto:媒体元数据与播放状态
- input.proto:触控事件与按键事件
- wireless.proto:无线握手相关
解密后的每条消息会分发到 AapMessageHandler.kt 处理,视频帧、音频帧、媒体信息、触控事件各走各的通道。
步骤 4:音视频解码
- 音频:AudioDecoder.kt 将手机端推来的 AAC 音频流解码后写入 AudioTrack,MicRecorder.kt 负责反向采集麦克风上行
- 视频:VideoDecoder.kt 处理 H.264 解码;HEVC 则走 C++ 层的 FFmpeg 原生解码器 ffmpeg_hevc_decoder.cpp(预编译库在
app/src/main/jniLibs/)。解码层还配有大量"策略类"(如丢帧恢复、码流背压、关键帧修复),保证投屏流畅
步骤 5:投屏渲染与状态广播
AapProjectionActivity.kt 是全屏投屏 Activity,解码出的画面渲染在 TextureProjectionView.kt 等视图上;触控坐标经 TouchCoordinateMapper.kt 换算后回传给手机。
服务层 AapService.kt 是整条链路的"总调度":它在 AapService.kt#L1037-L1091 的observeConnectionState()中统一观察连接状态流,HandshakeComplete时启动投屏界面,TransportStarted时开始正式投屏,并向自动化工具(Tasker 等)广播会话状态。
📦 快速上手与源码导航
想动手体验的话,可以先看 README.md 中的 USB 与无线接入步骤;想深入某条链路,建议按下面的顺序读源码:
| 想看什么 | 从哪里读起 |
|---|---|
| 连接状态机 | CommManager.kt |
| USB 接入全流程 | connection/usb/ |
| Wi-Fi Direct 组网 | connection/wifi/direct/ |
| AAP 消息收发 | aap/AapTransport.kt |
| 视频解码策略 | decoder/video/ |
| 投屏界面 | aap/AapProjectionActivity.kt |
🎯 小结
Open Headunit 的架构非常清晰:CommManager 管链路、AapTransport 管协议、Decoder 管解码、ProjectionActivity 管呈现,一条"USB/Wi-Fi 接入 → SSL 握手 → AAP 消息流 → 音视频解码 → 全屏投屏"的单向数据流贯穿始终,反向的触控与音频上行则走同样的通道回传。理解了这个数据链路,再去阅读各模块源码就会事半功倍。
【免费下载链接】open-headunitHeadunit App for displaying Android Auto项目地址: https://gitcode.com/GitHub_Trending/he/open-headunit
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考