410 型优卡特称重台 · 2026.09.09

DeviceSDK / PC681 扫码识别中断只读审计

硬件类型澄清(用户2026-09-09确认):本项目使用扫码器扫描二维码。rfidData / rfidLen 是厂商SDK字段名,不代表此设备采用射频识别;原字节码证据和变量名保留。

审计日期:2026-09-09。方法:AAR ZIP/SHA256 核对、JDK javap 对 Java 字节码逐方法审计、对原始 classes.jar 的纯解析方法做最小 Java 运行验证。没有修改业务仓库,没有初始化串口,没有调用或加载厂商 JNI;没有把会议中的推测当作代码事实。

结论

附件正是本机 ZhctWeightingTableYoukate/app/libs/DeviceSDK-80.1.10.12.20260129.aar 使用的同一包。首页秤体+盘码应审计 PC681ScaleManager,而不是包内名称更像扫码的 SerialScanCodeImpl。PC681 Java 层证实了合帧上送、盘码长度和内容解析,但没有证据证明会议所说“扫码头每 300ms 更新、100ms 合帧沿用旧码”由该 Java SDK 实现。这需要扫码头/秤板固件协议及隔离验证原始帧说明。

PC681 存在可复现的缓存、组帧和异常语义问题,会破坏“每收到一帧就代表新鲜的 100ms 采样”这个前提。它们是代码层已证实缺陷/风险;仍不能据此认定已经找到现场中断根因。

1. 包与范围固定

项目 结果
用户附件 AAR 与业务项目 AAR 完全相同
文件长度 90,728 bytes
两份 AAR SHA256 adabb1f0f4750081abe131a3baa6f76057ff931e05d7264c56bf6fba5d99aeec
PC681ScaleManager.version() 80.1.10.12.20260129
PC681ScaleManager.class SHA256 74b0a5ab481a2eb643e427d0a9f4e26dc827cdb0e53e932ae737f04716525d70
PC681ScaleManager$ReceiveThread.class SHA256 f23d4bcd00e2bb381111b98ef0c2bd38e455f0d1ce2d538c4d68860da0492fe4
AAR 内容 classes.jar、manifest/resources,arm64-v8a/armeabi-v7a 下 mserial_port / humansensor / yfgpio JNI 库
当前有效证据层 SDK Java 实现、纯解析特征测试
未覆盖 厂商固件/电气层、JNI 原生实现、现场 APK 和日志、真实扫码头的温升/采样周期

SerialScanCodeImpl.version() 另返回 85.1.10.12.20260122,该类还带 3000ms 扫码间隔限制;这是同一 AAR 内另一个扫描通道的实现,不能把其版本或 3000ms 逻辑用于解释本次 PC681 路径。两份 AAR 元数据与关键类 SHA 已记录于本节。完整 ZIP 清单及厂商字节码转储仅在本机临时审计目录,不作为仓库交付。

2. 真实数据链和计时含义

org.winplus.serial.utils.SerialPort → FileInputStream → PC681ScaleManager.ReceiveThread → addDataToCache() → parseCacheData() → isFrameValid() → parseFrameData() → Handler.post(mainLooper) → 应用 ScaleDataCallback。

3. 与中断最相关的发现

F1 — 多个完整帧落入一次 read 时,每次只解析一帧,尾帧可无限等下一次 read

代码证据:PC681ScaleManager.javap.txt 第 677 行 parseCacheData()。字节码 128–176 只解析一个候选帧,177–205 移走已处理帧并直接 return,没有继续消耗完整余帧的循环。ReceiveThread.javap.txt 第 54 行 processData() 仅调用一次 addDataToCache 和 parseCacheData;第 78 行 checkPackageTimeout() 字节码是 0: return。

实际 SDK 验证:一次送入两个完整合法 35 字节帧,第一次只回调 123456,还剩 35 字节;调用 idle timeout 后仍只回调一次;新帧 999999 到达后回调旧尾帧 654321,新帧再留在缓存。

影响:帧龄增加,回调和真实采样时间脱节,空码和有码都可能迟到。若持续出现多帧同读,积压会增大,1024 字节缓存溢出后已有缓存被清空。这一行为本身不凭空创造空码,也不能单独证明“连续空码误离盘”已经发生。

修复方向:按协议持续消费缓存中所有完整帧,保留最后半帧;明确陈旧帧丢弃/上送策略;每帧携带接收时间、序号或可核对的原始数据。需要厂商修改 SDK 或提供可审计解析层替换方案。

F2 — 帧 checksum 正确但 SDK码字段长度不匹配时,SDK 输出业务空码,且不报告解析错误

代码证据:PC681ScaleManager.javap.txt 第 901 行 parseFrameData(),字节码 430–449 从帧偏移 25–26 两个 ASCII 字符解析 rfidLen;456–499 仅在 rfidLen > 0 && 27 + rfidLen <= frameLength - 2 时提取字符串;否则 502–506 设置 rfidData="",509–510 仍返回 ScaleData。

实际 SDK 验证:构造实际只有 6 字节盘码、长度字段声明 7 的 35 字节帧,重新计算正确 checksum;SDK 自己的 isFrameValid(fullFrame) 返回 true,回调一次空字符串,onScaleError 次数为 0。这里的“有效”仅指 SDK 的帧头/帧尾/校验和通过,SDK码字段语义已不一致,不能称为业务有效空盘帧。

影响:若现场发生这种长度异常,应用会收到看似正常的空码。如果应用把空码计作离盘,就把协议异常与真实未识别混为一种业务事件。是否发生该输入必须由现场原始串口 HEX 证明。

补充:SDK 对提取字符串会 trim,合法 rfidLen=5 会返回 12345,并不限定 6 字节。业务层的“必须 6 字符”是应用规则,不能称作 SDK 固定契约。

修复方向:区分 EMPTY_CODE / VALID_CODE / INVALID_FRAME / STALE_FRAME / TRANSPORT_TIMEOUT;SDK码字段长度不一致进入协议错误,不能直接产生可计数的空盘证据。

F3 — 校验字节刚好为 0x3C 时,被组帧逻辑误当作 '<' 帧尾

代码证据:parseCacheData() 字节码 89–122 从索引 28 开始寻找首个 0x3C,直接作为帧尾;没有依据 rfidLen 定位尾部,也没有区分 checksum 与尾标记。isFrameValid() 只校验已经截出来的候选帧。

实际 SDK 验证:构造 6 字节盘码且 checksum=0x3C 的完整帧,完整帧直接 isFrameValid() 为 true;经过 addDataToCache/parseCacheData 后,回调数 0,产生“接收到非法帧,校验失败”,遗留 1 字节实际帧尾。

验证帧(人工协议边界样本,不是现场采集):3e0a0d303030303130303030303031303030303030303100c730363132333435363c3c。尾部 3c3c 依次是 checksum 和真实 '<'。样本温度字段是 0x00C7,是否属于具体硬件的正常温度编码范围尚无厂商协议可验证。

影响:存在格式和校验自洽的输入被误丢弃。它导致缺帧,不自动生成空码;对离盘流程的影响取决于上层对缺帧的处理。

修复方向:依据明确帧长/字段长度完成组帧,并用实际协议边界数据覆盖帧尾字符与 checksum 碰撞。

F4 — 主线程回调和 ScaleData 缺少新鲜度信息,不能将回调次数直接等同时间

代码证据见第 2 节。即使硬件严格每 100ms 发一帧,串口合包、F1 缓存滞留和 Android 主线程排队都能改变应用收到回调的时间。SDK 没有给对象附原始采样时间,因而计数 3/7/10 次只能是“连续收到这些判定样本”,不能不经测量就写成可靠的 300/700/1000ms。

修复方向:应用记录原始读时间、帧解析完成时间、业务回调时间,给判定输入附质量状态;离盘目标应规定最大容忍空档和响应时延,计数阈值需结合真实帧频和计数起点验证。

4. 次要恢复/控制路径发现

5. 会议说法逐项裁定

会议口径 本次 SDK 证据裁定
秤体重量和扫码头盘码在一起返回 支持:一个 ScaleData 来自一个帧,含重量、状态、rfidLen、rfidData
秤体设为100ms 有 FRQ=10 发送能力;实际设备生效和精确时序未验证
扫码头300ms更新 PC681 Java 层没有该实现,属待核实固件/硬件事实
两个100ms中间帧沿用旧码 未发现Java主动复制上个码;上游缓存需原始帧验证;SDK自身确有尾帧积压机制
100ms会发热、不可靠 静态软件分析不能证实,需厂商规格/温升隔离验证
连续3次无数据可认为拿走 SDK只给数据,没有离盘规则;必须由应用审计和现场验证定义无数据/空码/异常区别
3改7或9–12次就可解决 SDK分析不能证明;计数的来源和时间、新鲜度及错误语义仍未闭合
问题只能是理解错误或代码没按理解实现 还必须核实设备固件实际输入、版本一致性、协议异常、SDK缓存与主线程时序这些可观测前提

6. 运行验证与交付证据

sdk-verification/harness/ParserAudit.java 直接反射调用原 classes.jar 的 addDataToCache / parseCacheData / isFrameValid 等方法,未重写这些算法。Android Looper/Handler/Log/Context 和 SerialPort 是测试替身;Handler.post 立即执行仅为观测解析结果,不模拟 Android 主线程排队/硬件时序。SerialPort 构造函数会抛 AssertionError,禁止意外进入真实串口路径。不得将该 harness 当作设备稳定性测试。

共 11 项特征检查通过。PASS 表示观测结果匹配断言,其中多项断言专门证明缺陷能够复现,不是 SDK 质量合格。三个基本控制样本验证:合法单帧、两段到达的单帧、trim 后返回码。全部结果见 sdk-verification/parser-audit-results.txt。

从本报告目录复跑(只写本机 /tmp/scan-interruption-audit-20260909/sdk/):

bash sdk-verification/run-parser-audit.sh /absolute/path/DeviceSDK-80.1.10.12.20260129.aar

7. 建议列入任务目标

  1. 固定现场 APK、AAR SHA、秤板/扫码头固件版本及持久化参数,与本机版本匹配。
  2. 获取100ms/300ms双周期的厂商协议定义和带时间戳原始帧;区分真实空码、长度异常、校验失败、无帧和旧帧。
  3. 厂商修正或说明 PC681 单帧消费、checksum尾字符碰撞、短响应路径问题,以本报告的最小样本验证新包。
  4. 应用禁止用协议错误、格式拒绝、陈旧帧充当离盘证据;阈值设计同时满足误断率和真实离盘响应时间。
  5. 做端到端设备回放:持续放盘、1/2/3次空档、超过阈值空档、真实离盘、快速换盘、合包/半包/异常包、断流和恢复;同时验证订单状态和最终金额,按事件序列定位第一处分歧。