DiPlay 编译实录:给比亚迪车机从源码编一个 CarPlay
车机上的 CarPlay 一直是件别扭事:原厂自带的只在原厂生态里好用,副厂加装要插一个转接盒,盒子里还跑着另一套系统,多一个要供电、要配对、要出故障的东西。
DiPlay 换了个思路:把 CarPlay 的接收端直接装进车机本身,让车机自己去扮演那个盒子。项目开源在 GitHub,Kotlin + Jetpack Compose 写的,GPL-3.0。
它到底是真能跑的东西,还是又一个只活在 README 里的项目?我在 Mac 上从 clone 开始,把工具链一件件装上,最后编出来一个能装的 APK。这篇记录整条链路,以及真正卡住人的四个地方。
先说结论
能从源码编出来,但默认编出来的包连不上 iPhone。
- 工具链是死版本:JDK 25、SDK Platform 37.0、Build-Tools 36.0.0、NDK 28.2.13676358、Gradle 9.5.0,少一个都编不过
- 一条命令出货:
./gradlew :mobile:assembleDebug,产物在mobile/build/outputs/apk/debug/mobile-debug.apk - 默认构建不含配件身份(MFi 凭据),装上能开、能进设置,独立 CarPlay 握手会失败
- 把身份文件补进
DIPLAY_AUTH_ASSETS_DIR再编assembleStandaloneDebug,编出来的包能真连 iPhone - 两个包我都编了、都放在站内:带身份的那个能用,不带身份的那个只能看界面
DiPlay 是什么
| 项目 | 情况 |
|---|---|
| 仓库 | github.com/shihabal3amri/DiPlay |
| 许可证 | GPL-3.0 |
| 语言 | Kotlin,约 844 个 .kt 文件、12.5 万行,界面用 Jetpack Compose |
| 包名 | com.shihab.diplay(debug 变体为 .hudtest) |
| 版本 | 0.2.20 / versionCode 39 |
| 系统 | minSdk 24(Android 7.0),compileSdk 与 targetSdk 均为 37 |
| 车型 | 以比亚迪 DiLink 为主,其他品牌不承诺支持 |
| 状态 | 公开预览,未通过 Apple 认证 |
它做的事可以一句话说清:iPhone 认为自己在和一台车载配件通信,车机认为自己接了一部 iPhone,中间没有盒子。
它凭什么能当"盒子"
Apple 的 CarPlay 接收端需要三件东西:iAP2 的配件会话、AirPlay 的音视频通道、MFi 认证的配件身份。DiPlay 的源码几乎是照着这三件事切的:
shared/iap2:配件协议的会话与鉴权shared/airplay:视频、音频、触控通道;默认 H.264 / 30fps,HEVC 与 60fps 是可选项shared/mfi:配件身份与认证材料shared/transport:USB 分帧(usbmux 风格)与网络承载shared/network:本地 VPN、Bonjour 通告、热点与 Wi-Fi Directshared/mediashared/telecomshared/hud:触控映射、通话、仪表与车辆数据common:Compose 界面、设置、诊断、更新、八种语言
传输有四条路:USB 数据线(用本地 VPN 承载链路)、车机自带热点、Wi-Fi Direct、以及车机和 iPhone 连同一个外部路由。四条路的目标一致——让手机和车机处在同一个二层网络里,然后由蓝牙完成最后一跳的配对与握手。
shared/src/main/jni 里还带了 speexdsp:通话回声消除是 C 代码。所以 NDK 不是可选项,这一点很多人第一次编会漏掉。
| 模块 | 规模 | 作用 |
|---|---|---|
common |
324 个文件 / 5.4 万行 | 主界面、设置、诊断、多语言 |
shared |
512 个文件 / 7.0 万行 | 协议、传输、解码、JNI |
mobile |
3 个文件 | 主 App 壳与 debug 测试入口 |
samples/* |
5 个文件 | 地图与桌面示例 |
主代码并不在 mobile 里,mobile 只是一个很薄的壳;真正的实现全在 common 和 shared。这一点对读源码的人很重要,别一进仓库就找错地方。
工具链:五个版本号,一个都不能歪
官方 docs/BUILD.md 给的是 JDK 25、SDK Platform 37.0、Build-Tools 36.0.0、NDK 28.2.13676358,加上仓库自带的 Gradle 9.5.0。这套组合是硬要求:AGP 的版本决定了它能接受哪个 JDK,Kotlin 与 Compose 的版本又锁在 Gradle 9.5 上。
# JDK 25(Temurin)
curl -L -o jdk25.tar.gz \
"https://api.adoptium.net/v3/binary/latest/25/ga/mac/aarch64/jdk/hotspot/normal/eclipse"
# Android 命令行工具与平台
sdkmanager "platform-tools" "platforms;android-37.0" "build-tools;36.0.0"
我这边把 SDK 装在项目目录里,避免污染系统环境,然后写 local.properties 指过去:
printf 'sdk.dir=/path/to/sdk\n' > local.properties
编译
export JAVA_HOME=/path/to/jdk-25
./gradlew :mobile:assembleDebug --console=plain
第一次构建要下载 AGP、Compose、AndroidX 一整套依赖,配置阶段比编译本身还长。我这边实测:从 clone 到出包全程约 25 分钟,其中装工具链占了 20 分钟,真正的 Gradle 构建只有 3 分钟(84 个任务)。
BUILD SUCCESSFUL in 3m 1s
84 actionable tasks: 84 executed
产物:一个 23MB 的 APK
下面这组数字是标准构建(assembleDebug,不含配件身份)的结果:
| 项 | 值 |
|---|---|
| 文件 | mobile/build/outputs/apk/debug/mobile-debug.apk |
| 大小 | 23.4 MB(24,483,691 字节) |
| 包名 | com.shihab.diplay.hudtest |
| 版本 | 0.2.20-hud-test (versionCode 39) |
| minSdk / targetSdk | 24 / 37 |
| ABI | arm64-v8a / armeabi-v7a / x86 / x86_64 |
| SHA-256 | 773a7142c2f146429a06911277e38dfeaa75809fd291a40e72f623fd105ba6b1 |
| 源码提交 | b309eae(2026-10-17) |

这份自编包我也放在站内了,见下面的版本对照。再强调一遍它的定位:用来研究界面、设置项和协议行为的实验品,它没有配件身份,装上车也连不上 iPhone。只想日常用的话,GitHub Releases 的官方签名 0.2.20 更稳。
两个包,该下哪个
我把两种编法都跑了一遍,产物都在站内。认准"可用版"这三个字:
| 版本 | 能不能连 iPhone | 文件 | 大小 | SHA-256(前 8 位) |
|---|---|---|---|---|
| ✅ 可用版(含 MFi 身份) | 能,装到车机直接配对 | DiPlay-0.2.20-standalone-with-mfi.apk | 23.4 MB | f362cc69 |
| ❌ 研究版(无身份) | 不能,只能看界面 | DiPlay-0.2.20-no-identity.apk | 23.4 MB | 773a7142 |
两个包体只差 808 字节,差的就是 assets/offline-mfi/identity.pk8 与 certificate.p7b 这两个文件。包名都是 com.shihab.diplay.hudtest,签名都是 CN=Android Debug:
# 装之前先确认自己下的是哪个
unzip -l DiPlay-0.2.20-standalone-with-mfi.apk | grep offline-mfi
# 有 assets/offline-mfi/identity.pk8 → 可用版
# 什么都没有 → 研究版,连不上
选哪个取决于你想干什么:想在车上用,下可用版;只想看看它的设置页长什么样、或者打算自己改代码重编,下研究版就够——但它连不上手机,别拿它判断"这个项目不行"。
四个真正卡人的地方
一、platforms;android-37 不存在。 新版 SDK 把平台拆成了小版本号,包名是 platforms;android-37.0。照着老习惯装 android-37 会直接报 "Package not found",而报错信息里不会告诉你正确写法。
二、Gradle 发行包和依赖。 官方源在部分网络下会卡住不动,表现为日志停在 "Downloading gradle-9.5.0-bin.zip ... 10%" 十几分钟。换成镜像拿安装包、给依赖加一段 Maven 镜像,整个配置阶段从"看不到头"变成几分钟。镜像只是搬运,Gradle 会校验 SHA-256,安全上不吃亏。
三、NDK 不能省。 漏了 NDK,构建会停在 :shared 的配置阶段去自动下载一个接近 1GB 的包。它不报错,只是慢,容易让人以为卡死了。
四、没有配件身份,编出来也连不上。 这是最关键的一条。docs/BUILD.md 写得很清楚:普通源码构建和 CI 构建都不含配件身份,DIPLAY_AUTH_ASSETS_DIR 必须由你自己指向一份 offline-mfi/identity.pk8 与 certificate.p7b。没有这两个文件,App 能装能开,独立 CarPlay 握手时会失败。官方 release 里的那份身份是项目从公开的 Carlinkit 固件里提取的实验性材料,不是 Apple 发给它的。
换句话说,默认构建验证的是"能不能编",不是"能不能用"。想要一次到位、不用自己折腾身份和签名,GitHub Releases 的官方包仍然是首选。
如果就是想让自编包也能用,路子是现成的:把两个身份文件放进一个外部目录,用 DIPLAY_AUTH_ASSETS_DIR 指过去,编 :mobile:assembleStandaloneDebug。这两个文件官方 release 的 APK 里就躺着——解压后 assets/offline-mfi/ 下有 identity.pk8(67 字节)和 certificate.p7b(607 字节)。
我按这条路又编了一版:APK 里确实多了这两个文件(24,484,499 字节,比标准构建多 808 字节),包名仍是 com.shihab.diplay.hudtest,签名是 CN=Android Debug。它具备独立连接 iPhone 的条件,但它和官方版(包名 com.shihab.diplay,签名 CN=DiPlay, OU=Private Beta)是两个独立应用:能共存,不能互相覆盖,同时开着会抢 Wi-Fi 与音频通道。
这版我已经放进站内下载(就是上面表格里的"可用版")。关于这份身份的来源要说清楚:它不是 Apple 发给这个项目的,是项目自己从第三方固件里提取的实验性材料,同一个包谁都能装、谁都能拆。我只把它当作"验证这条路通不通"的材料放在这里,方便你自己编、自己试;要长期稳定用,还是回到官方 release。
装到车上要注意什么
- 装车机,不装手机;用同一套签名和包名覆盖安装,配对记录和设置才会保留
- 关掉其它投屏 App,两个接收器同时跑会抢 Wi-Fi 与音频通道
- USB 必须是数据口配数据线,充电口和充电线走不通
- 首次连接要给的权限:蓝牙/附近设备、旧系统上的 Wi-Fi 与定位、麦克风(Siri 与通话)
- 改分辨率、帧率、图标大小这类显示设置都会重连一次 CarPlay,别在路上按
该不该用
| 你在意的事 | 现实情况 |
|---|---|
| Apple 认证 | 没有,项目自己写明了未认证 |
| 配件身份 | 从第三方固件提取,私钥可被提取,未来 iOS 是否继续接受未知 |
| 车型覆盖 | 比亚迪为主,其它品牌与老车机是"社区报告",不是支持列表 |
| 稳定性 | 公开预览阶段,音频、通话、仪表地图都还在修 |
| 许可证 | GPL-3.0,二次分发要开源 |
这台车机允许装 APK、你愿意接受预览版的不确定性、并且能对照固件版本排查问题——那它值得试。反过来,如果车机封了 APK 安装,或者改一次显示设置就断一次会话让你无法接受,原厂方案和转接盒仍然是更省心的选择。
最后补两句
这类项目有意思的地方不在"省了一个盒子",而在于它把 CarPlay 协议栈里那些被厂商封装起来的部分重新摊开:iAP2 怎么握手、AirPlay 怎么开流、USB 分帧怎么恢复、音频时钟偏了怎么补。编一遍、读一遍,比看十篇评测更接近真相。
编译本身不难,难的是知道编出来的东西缺了什么。
评论 (0)
还没有评论,来说两句吧~
发表评论