Repository navigation
CarPToCarL 1.2.9 — 车机拒绝配对时,别再把拒绝读成成功
车机拒绝配对时,我们一直在把「拒绝」读成「成功」
2026-10-30 的日志 221747 里有一台车:画面出来了,声音一直没有,过一会断链。
日志里给过两条原因——「加密 SETUP 被拒(403)」、「弹窗里没勾声音」——两条都是假线索。
真因在配对应答里,而我们把那个字段丢了:
车机 pair-verify M4 回:{ Error: 2, State: 4 }
↑ ↑
真实原因 我们只看了这个
State: 4 是「正常完成」标记,Error: 2 是「不认这台手机的配对身份」。从 1.0 起,
代码只判 State == 4 就认为配对成功,于是双方派生出两把对不上的密钥,
加密 SETUP 被车机静默丢弃 10 秒,随后降级成明文会话——明文没有共享密钥,音频与触摸整条链路直接被跳过。
「画面有、声音没」就是这么来的,跟弹窗勾没勾、跟 403 都没有关系。
这一版做的事情,都是围绕**「别再把失败读成成功」和「失败之后要能看懂」**展开的。
① 配对错误码透传 + 失败定性(本版主项)
pair-setup / pair-verify 的每个应答现在先看 Error,再看 State,抛出的异常带着阶段与原始错误码。
PairingPolicy 把「阶段 + 错误码」翻成处置:
| 错误码 | 含义 | 处置 |
|---|---|---|
0x02 AUTHENTICATION |
不认本机配对身份 | 确定性失败;pair-setup 段改走已配对快路径(与 403 同义) |
0x04 MAX_PEERS |
车机保存的设备已满 | 确定性失败,立刻停 |
0x05 MAX_TRIES |
尝试次数用尽 | 确定性失败,立刻停 |
0x03 BACKOFF / 0x06 UNAVAILABLE / 0x07 BUSY |
暂时状态 | 可重试(BACKOFF 停 5s,其余 1s) |
| 私有码 / 无码 | 未知 | 可重试——宁可多试一次,也不把能连的车机判死 |
判定为确定性拒绝时,不再触发「断 BT 二段 hack」:那个补救只会用同一份身份再被拒一次,真车上还会把蓝牙控制通道拆掉。
明文会话继续兜底画面,但会当场写清后果:
AIRPLAY: ⚠ 本会话无共享密钥(车机拒绝配对):画面正常,音频与触摸不可用。
要恢复声音需在车机侧删掉本机的配对记录后重连
看到这一行就不用再翻别的日志了:没有声音是必然的,不是没配好。
② 短期记忆:省掉 120 秒白等
确定性拒绝之后,旧行为仍会在 120 秒配对窗口里每 2 秒重试一次,每次都拿到同一个错误码。
PairingMemory 按接收端记一条(pk → deviceid → SSID 三级降级),15 分钟内直接跳过 pair-setup。
三条自我纠正保证它不误伤本来能连的车机:15 分钟 TTL 自动失效;只跳过 pair-setup、不跳过明文 SETUP
(明文 SETUP 若被拒,retryWithPairing 仍会补做一次配对);任何一次配对成功立刻清除记忆。
记忆键不用 IP——车机热点每轮可能换地址,用地址当键要么永远命中不了,要么命中到隔壁设备。
③ 候选地址只是候选,身份只看 pk
现场事实:mDNS 通告的地址与 0x4301 自报的地址可能既不同族也不同值——一台 DiPlay 的 0x4301 报 IPv6 链路本地,mDNS 通告 IPv4 192.168.43.1。
代码结构本来就是这个原则(按 IPv4 → 全局 IPv6 → 链路本地 IPv6 逐个试),本版补的是两件「出了事看不出来」的事:
AIRPLAY: 连接候选 192.168.43.1(IPv4)、fe80::1%wlan0(链路本地 IPv6)(身份判定只看 pk,与地址无关)
以及 pk 签名不匹配时不再只是一句 =false,而是显式告警并指出风险(TXT 里的 pk 过期,或连到了同网段的另一台 AirPlay 设备)。
仍然不中断——部分自制接收端的 pk 本身就是空壳,硬失败会把能用的车机挡在门外。
④ 阶段预算:这次连接到底花了多久
七个阶段的耗时原来散落在各自日志里,每次都要人工掐表。util/ConnectBudget 现在逐阶段计时,结束打一行汇总:
STAGE: 本轮开始(0x4301)——预算:等端点 5min / 热点 60s / 配对 120s / SETUP 30s / 首帧 20s
STAGE: 会话建立(SETUP)开始(上段 AirPlay 配对 用时 120.4s,**超预算**,本轮累计 131.7s)
STAGE: 本轮小结(0x4301)共 23.5s:等车机连接信息 8.2s → 加入车机热点 3.1s → AirPlay 配对 12.2s → 会话建立 0.0s → 等首帧 0.0s
预算值全部沿用已有常量,不引入第二套超时。
首帧门:SETUP 成功后 20 秒仍无帧会写明原因(会话已建立,20 秒出不来就不是「慢」而是「没有」)。
故意只报告不拆链——拆链由视频看门狗与冻结恢复负责,再叠一套超时就是两套语义互相打架,这个坑踩过好几次。
⑤ 多屏选择
旧实现固定取第一块宽高可解析的屏。多屏车机(大连屏 / 仪表)若先声明副屏,画面就推到那块屏上,比例与触摸坐标全错。
现在的规则:车机没声明 type → 取第一块有效屏(与旧行为完全一致);声明了 type 时按
type == 0 → 面积 → 帧率 → 声明序选主屏。displayUuid 与 maxFps 也改为取选中那块屏的值。
AIRPLAY: /info 共 2 块屏候选:displays[0] 800×480 type=1;displays[1] 1920×720 type=0 maxFPS=60 → 选中 displays[1]
⚠ 这条排序是假设,不是实车结论——目前还没有多屏车机接入过,它只在车机自己声明
type时生效,且一定会把候选与选择结果写进日志。实车一跑就能验证。
排查时搜这几行
| 想知道 | 搜 |
|---|---|
| 这次连接一共多久、慢在哪 | STAGE: 本轮小结 |
| 哪个阶段超预算 | **超预算** |
| 车机是否拒绝配对、为什么 | Error= / 确定性拒绝配对 |
| 本会话有没有声音 | 本会话无共享密钥(有这行就必然没有音频/触摸) |
| 按什么顺序试了哪些地址 | AIRPLAY: 连接候选 |
| 选了哪块屏 | 块屏候选 / 选中 displays[ |
| 画面为什么没出来 | STAGE: ⚠ 等首帧超时 |
完整判据与「为什么不做某些事」见 docs/connect-hardening.md。
装包说明:CarPToCarL-1.2.9.apk 为主线正式包(versionCode 10209)。
与别克分支的 1.2.9-bieke 同 versionCode,可互相覆盖安装;升级不会丢配置与已配对车机信息。