一句话看懂
项目地址:github.com/shihabal3am...

DiPlay 是一个安装在 BYD 车载头单(Android 系统)上的 APK,让 iPhone 通过 USB 或 Wi-Fi 直接投射 CarPlay 界面,不依赖任何外置硬件适配器。当前版本 0.2.7,处于公开预览阶段,931 颗 Star、158 个 Fork。
它解决什么问题

BYD 的 Android 车载系统(DiLink)原生不支持 CarPlay。官方解法是购买 Carlinkit 之类的硬件 dongle。DiPlay 把这个中间人的角色塞进一个 APK:头单已运行 Android,具备 USB Host 和 Wi-Fi 能力,缺的只是软件层面的 iAP2 协议握手、MFi 身份认证和 AirPlay 会话管理。DiPlay 把这三层全部在应用层实现,装上即用。包名 com.shihab.diplay,可与 DiAuto 共存,但每次只能运行一个投射应用。
核心概念速览
iAP2 :iPhone 和配件之间的握手与控制协议,负责建立 CarPlay 会话。DiPlay 在 Iap2LinkEngine 等类中实现了完整的链路层状态机。
AirPlay Session :CarPlay 的屏幕和音频流传输协议。AirPlaySession 负责 RTSP 帧交换、配对认证、HID 触摸输入编码,以及流的建立和释放。
MFi / Accessory Identity:iPhone 只信任持有合法身份证书的配件。DiPlay 使用从 Carlinkit 公开固件中提取的实验性 identity,并非正式 MFi 认证,私钥可从 APK 中提取。
HUD :DiPlay 通过 BydHudBridge 和 BydHudProtocol 将 iPhone 导航指令转换为 BYD 专有协议帧,发往车载 HUD。
Wi-Fi Direct :无线 CarPlay 的主要后端,由 WifiP2pGroupManager 管理,需要 Android 10+。
架构拆解

项目分四个 Gradle 模块:
- shared :核心库(Kotlin 1,638,681 字节),包含 AirPlay 会话、iAP2 协议、HUD 集成、媒体编解码、ADB 接口和传输层抽象。NDK 构建支持
arm64-v8a、armeabi-v7a、x86_64,minSdk=28,targetSdk=37。 - common :主 Activity、Compose 设置界面、会话服务(
DiPlaySessionService)、诊断导出、多语言支持和仪表盘显示逻辑。 - mobile:手机端构建变体及调试用 HUD 演示 Activity。
- automotive:车载端构建变体,含 Android Automotive 专用 Manifest 和混淆保留规则。
核心数据流向:
markdown
DiPlaySessionService(生命周期管理)
│
▼
CarPlayController(总编排)
├── IphoneUsbHost / WifiP2pGroupManager(iPhone 发现)
├── Iap2LinkEngine(iAP2 链路层状态机)
├── Iap2MfiAuthenticationClient(MFi 身份认证)
└── AirPlaySession(AirPlay 控制连接)
├── PairSetup / PairVerify(配对与加密)
├── H.264 解码渲染(屏幕流)
└── AirPlayHid(触摸/HID 输入回传)
NavigationOutputWorker → BydHudBridge → BYD HUD 硬件
关键实现走读
iAP2 链路握手起点:Iap2LinkEngine.start
kotlin
/** Starts a link attempt. Wired CarKit uses [wiredInitiator] = true. */
fun start(wiredInitiator: Boolean, nowMillis: Long) {
if (state != State.IDLE) return
state = State.DETECTING
if (!appendOutput(IAP2_MARKER)) return
markerDeadlineMillis = nowMillis + MARKER_RESEND_MILLIS
if (wiredInitiator && state != State.DEAD) enterNegotiating(nowMillis)
}
检查链路是否处于空闲状态,切换到 DETECTING,向输出缓冲区写入 IAP2_MARKER 标记帧,设置重发超时;有线模式下立即进入 NEGOTIATING 阶段。IDLE 守卫防止重复启动,appendOutput 返回值检查确保写入失败时不继续推进状态。wiredInitiator 在此处控制路径分叉------有线可立即协商,无线需等待网络层就绪。Iap2LinkEngine 不直接操作 Socket 或 USB 流,只写入输出缓冲区,由上层传输层负责发送。没有这个握手,iPhone 不会识别到合法 iAP2 配件,整个投射流程在最初阶段就会终止。
仪表盘 UI 切换:AirPlaySession.setClusterUiShown
kotlin
fun setClusterUiShown(shown: Boolean): Boolean {
val uuid = AirPlayInfoPlist.ALT_UUID
if (!shown) return sendCommand(mapOf("type" to "stopUI", "params" to mapOf("uuid" to uuid)))
val url = config.cluster?.initialUrl ?: return false
return sendCommand(mapOf("type" to "showUI", "params" to mapOf("uuid" to uuid, "url" to url))) &&
sendCommand(mapOf("type" to "forceKeyFrame", "params" to mapOf("uuid" to uuid)))
}
关闭时发 stopUI;打开时先发 showUI,再发 forceKeyFrame 强制 iPhone 立即输出一帧完整图像。forceKeyFrame 是必要的------若只发 showUI,iPhone 可能等到下一个 P 帧周期才更新,驾驶员会看到短暂空白。config.cluster?.initialUrl ?: return false 保证没有配置集群 URL 时不发出格式错误的命令。流本身不中断,只是 iPhone 切换绘制目标。没有它,仪表盘屏幕无法按需切换 CarPlay UI,地图/转向卡/双显模式全部失效。
动手上手

DiPlay 安装在车载头单上,不是手机上。
第一步:下载 APK 并安装到头单
可验证结果:ADB 返回 Success,头单应用列表中出现 DiPlay 图标。
第二步:有线 CarPlay
markdown
1. 在头单上启动 DiPlay
2. 用 USB 线将 iPhone 连接至头单 USB 口
3. iPhone 弹出「信任此电脑?」提示,点击「信任」
可验证结果:DiPlay 界面切换为 CarPlay 投射画面,iPhone 状态栏出现 CarPlay 图标。
第三步:无线 CarPlay(需要 Android 10+)
markdown
1. 在头单上启动 DiPlay,切换到无线模式
2. 应用自动通过 WifiP2pGroupManager 建立 Wi-Fi Direct 组
3. 在 iPhone「设置 → 通用 → CarPlay」中找到头单设备名并连接
第四步:验证 HUD(仅已验证固件)
在 CarPlay 内启动导航应用(Apple 地图或 Google Maps)
观察 HUD 或仪表盘区域是否出现导航箭头和街道名
可验证结果:HUD 显示当前导航指令(箭头类型、距离数值、街道名称),与 iPhone 导航画面同步。
应用场景
BYD 车主日常使用:拥有搭载 DiLink Android 系统的 BYD 车型,不想额外购买硬件适配器。安装 APK 后即可使用 Apple 地图、Waze、Spotify 等 CarPlay 应用,方向盘媒体键和长按 Siri 均正常工作。界面支持简体中文。
二手 BYD 头单二次开发 :DiPlay 提供了完整的 iAP2 + AirPlay 实现参考,Iap2LinkEngine 的状态机设计和 AirPlaySession 的协议处理逻辑可作为学习材料。
HUD 导航集成研究 :BydHudBridge 将 iPhone 导航指令转换为 BYD 专有 HUD 协议帧,是少见的公开 Kotlin 实现案例,具体固件适配范围见 docs/BYD_NAVIGATION.md。
多语言车载界面测试:界面支持英语、简体中文、阿拉伯语、俄语、西班牙语。
独立分析

以下为独立分析,仅代表基于项目结构和代码的判断,不构成官方立场。
CarPlayController 的职责边界问题
CarPlayController.kt 的 import 列表横跨 bluetooth、usb、network、transport、airplay、iap2、hud 几乎全部子系统,是典型的「上帝类」模式。短期内降低了接口层复杂度,但随着功能增加,这里会成为修改冲突最密集的地方。当前 0.2.7 的 31 个 open issue 中,若多个并发修复都需要动 CarPlayController,合并成本会持续升高。将热点管理和 iAP2 路径抽离为独立 coordinator 是值得考虑的方向。
与 Carlinkit dongle 的本质差异
Carlinkit 需要额外购买硬件,DiPlay 是纯 APK 方案------这是最直接的用户收益。但 DiPlay 使用的 accessory identity 正是从 Carlinkit 公开固件中提取的,两者在 identity 层面共享同一个法律风险敞口:Apple 若吊销该 identity,两个方案同时失效。
与上游 xcertplay 的关系
DiPlay 基于 xcertplay(GPL-3.0),新增了 BYD HUD 集成、多语言 UI、仪表盘模式和 ADB 电池上报等 BYD 专用功能。上游协议层的 bug 修复可以流入 DiPlay,但 BYD 专用功能的维护压力完全落在 DiPlay 自身。
accessory identity 是最大的不确定性
README 明确说明:「The APK bundles an experimental accessory identity recovered from public Carlinkit firmware, not a newly provisioned MFi identity for DiPlay. A bundled private key is extractable.」Apple 可以在任何时间吊销该 identity 使所有已安装版本失效;私钥可被提取意味着任何人可以用同一 identity 制作仿冒配件。对普通用户日常使用感知不强,对企业部署或安全敏感场景是明确的禁止项。
局限与风险
- 品牌锁定:官方仅支持 BYD 车型,其他品牌明确声明不支持,没有添加兼容性修复的计划。
- accessory identity 风险:私钥可从 APK 提取,Apple 未来可能吊销该 identity,届时所有版本同时失效,无法通过版本更新规避,除非项目获取新的合法 MFi identity。
- 0.2.7 未经单独车载测试:当前版本没有经过独立的车载环境测试,更广泛的头单和 iOS 版本兼容性无保证。
- 偶发音频中断:0.2.7 中此问题未修复,推迟至后续版本,对日常使用影响明显。
- HUD 功能依赖固件版本:仅在已验证的 DiLink 5.1 固件上确认可用,并非所有 BYD 车型都能用到。
- Android 版本要求:有线 CarPlay 需要 Android 9+,无线 CarPlay 需要 Android 10+,老款头单系统版本较低则无法使用无线模式。
- ADB 功能依赖头单支持:仪表盘流暂停和电池上报等功能需要头单支持 DiLink 5.0/5.1,是可选功能,不影响基础 CarPlay 投射。
- GPL-3.0 许可证:项目遵循 GPL-3.0,基于此进行的修改和分发需要开源。首页/设置 UI 来自 DiAuto(AGPL-3.0),AGPL 传染性更强,混合使用需注意许可证合规。
结论卡片

适合谁用:拥有 BYD Android 头单、不想额外购买硬件 dongle 的车主;Android 车载系统开发者;社区测试者。
不适合谁:非 BYD 品牌车主;对稳定性有生产级要求的场景;依赖 Apple 正式 MFi 认证的商业部署。
后续关注点:项目已进入可用状态,核心流程(iAP2 握手、AirPlay 会话、HUD 集成)的实现完整度超出了「实验性 demo」的范畴。最值得持续关注的不是功能迭代,而是 Apple 对所用 accessory identity 的态度------这一点项目自身无法控制,却决定了整个方案的生命周期上限。