DiPlay 编译实录:给比亚迪车机从源码编一个 CarPlay

改装与用品 · 17 分钟阅读· 61 次阅读

车机上的 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 Direct
  • shared/media shared/telecom shared/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)

还没有评论,来说两句吧~

发表评论

验证码

提交后需站长审核,通过后才会显示。