真机实测(Android,恒玄耳机)逐条定位后修,原生能改的都在原生层改。 字幕乱(AliyunBailianE2EHelper,Android/iOS 同改) - 原文流与译文流各自按到达顺序自己发号,两条流的切句本来就不一致,off-by-one 一旦发生就一路错到底。改成认服务端的 item_id:译文帧的 item_id 经 itemParent (response item → input item)映射回原文那条,取不到才退回本地发号。 - response.audio_transcript 的 text 是累计值、delta 才是增量,此前一律当增量 append,于是出现「Hello,Hello,Hello,」。改成按字段名分支:有 delta 就追加, 只有 text 就整段替换。 - connectionGeneration 守住 onOpen/onMessage/onFailure/onClosed(iOS 用 activeTask 弱引用按任务身份判),旧 socket 的迟到回调不再打进新会话;stopContinuousConversation 同步清 webSocket/isStarted。 翻译结束后「总结内容」还留在屏幕上 - AliyunAstCallback.onSessionFinished 会把整场累计文本当作一条 translated 再发 一次,表现就是一大坨总结。不再发;原文缓存改为按 utteranceId 存,上限 32 条。 结束后无法再翻译、返回按钮像被遮挡 - 退出时那颗延时 Get.delete 会误杀刚重新进来的页面。按 tag 记活跃页面数 (_PageAliveMarker + _aliveCount),还有人用就跳过销毁。 重进页面提示「未在通话中」 - 耳机对 0xD1/0xD2(进/出通话模式)的应答被当成通话状态上报,把 isCallOngoing 改掉了。这两个 type 改为只记日志,不动通话状态。 切后台功能被关掉 - 前台服务类型加 phoneCall:microphone|phoneCall (132),ROM 的进程冻结不再掐断 通话翻译。startForeground 带类型失败时回退到仅 microphone。stopService 改用 context.stopService,后台也能调。 - AST 掉线自动重连(1s/2s/4s,最多 3 次),回到前台按 _astHealthy 补一次; 重连后的新会话用 800ms 宽限期,否则会被原来的 2s 窗口吞掉重试。 B 路说话人门限(PeerSpeechGate,新增 + 11 个单测) - 声音复刻配的是 frequency=once,只在会话开头刻一次音色。B 路会话一启动就建立, 此时对端多半还没开口,先收到的是机主的串音(实测 B 路 ASR 配 language=en 却 识别出机主刚说的中文),于是把机主音色刻成了「对端音色」,两个人听起来一样。 - 未开门期间音频进环形缓冲,开门瞬间整段补发——对端先开口时第一句的开头一个字 都不能丢。fallbackFrames 到时强行开门:门限太严会让对端整通电话不被翻译, 太松只是音色可能刻错,后者轻得多。 - 每次会话重建(含断线重连)都 reset,否则重连后照样刻到串音。 已知未尽:Android 14+ 会拒绝 phoneCall 这个前台服务类型并回退成仅 microphone, 那里的冻结保护不生效;rmsThreshold=600 尚未在真机上标定(上一轮跑到了兜底强开)。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>