Files
abox-mgr/docs/protocol/absdk-0630.md
T
KremeCN 6f7d3248b0 feat: add ABSDK 0630 bridge (#3)
## Summary

- add a pure Kotlin/JVM anti-corruption layer for ABSDK 0630
- serialize blocking SDK calls and enforce one provider-scoped session
- map `ABRet`, raw maps, SDK exceptions, and error codes into typed domain results
- implement session, socket, temperature/humidity, and infrared gateways
- document bytecode-confirmed protocol facts, SDK limitations, and unresolved behavior
- align JVM module bytecode with the Android Java 17 target

## Scope boundaries

- the bridge is not wired into the Android app in this PR; debug continues using Fakes
- `setHostInfo` is intentionally not called or implemented as a provider flow
- logout clears only the bridge-local session because the JAR exposes no logout or token setter
- no real credentials, network calls, or hardware were used in tests
- `docs/requirement_spec.md` and `docs/project_plan.md` are unchanged

## Verification

```text
gradlew.bat --stop
gradlew.bat clean :absdk-bridge:test :domain:test :testing:fakes:test :android:app:lintDebug :android:app:assembleDebug :android:app:assembleRelease build
```

Result: `BUILD SUCCESSFUL`

The public JAR API was also rechecked with `javap`, including the actual `getThStatus(String)` method, absence of a public logout API, `ABRet.getDicDatas()`, `IrInfo.getStatus()`, and `downloadIrCodes(List<IrInfo>)`.
Reviewed-on: #3
Co-authored-by: KremeCN <3265494849@qq.com>
2026-08-23 07:19:05 +08:00

7.6 KiB
Raw Blame History

ABSDK 0630 已确认事实与边界

1. 文档范围

本文记录 libABSDK0630.jar、厂商接口文档和仓库历史 Sample 中能够复核的事实,并明确仍待真实环境确认的内容。

证据来源:

  • JAR:samples/abox-sdk-sample/login-mac/libs/libABSDK0630.jar;
  • 厂商文档:docs/ABSDK接口部分_0630.md;
  • 历史 Sample:samples/abox-sdk-sample/login-mac/;
  • 公开 API 使用 javap 验证,协议细节通过授权字节码分析验证。

本文不是自有兼容后端的最终规范。真实连接参数、部署行为和硬件结果仍须通过授权测试环境验证。

2. JAR 公开类型

com.dhc.absdk.ABSDK

JAR 提供 getInstance() 单例入口以及以下与 MVP 有关的方法:

ABRet loginWithUsername(String username, String password)
ABRet isValidateToken()
ABRet setHostInfo(String url, String hotelName, String roomNo, String apikey)
ABRet getSockStatus(String soDevName)
ABRet sockCtrl(String soDevName, String status)
ABRet getThStatus(String thDevName)
ABRet sendIr(String irDevName, String irKey)
ABRet studyIrByIrDevName(String irDevName, String irKey)
ABRet downloadIrCodes(List<IrInfo> irInfos)

JAR 还包含窗帘、开关、射灯、门磁、人体活动和一氧化碳相关接口,但不属于当前 MVP。

JAR 没有公开的设备列表、设备发现、logout 或 Token setter API。

com.dhc.absdk.ABRet

String getCode()
String getMsg()
Map<String, Object> getDicDatas()

"00000" 表示调用成功;其他字符串代码表示失败。Bridge 不向领域层泄漏 ABRet、Map 或厂商类型。

com.dhc.absdk.IrInfo

String irDevName
String key
String code
String status

下载结果的 status 是字符串:"0" 表示失败,"1" 表示成功。

3. 文档与 JAR 差异

厂商 Markdown 文档把温湿度方法写成:

getThStatusByDevName(String)

JAR 实际公开的方法是:

getThStatus(String)

Bridge 必须以实际 JAR 方法为准。

setHostInfo 是 JAR 的公开方法,但未出现在厂商 Markdown 接口清单中。

4. 全局状态与线程边界

JAR 使用单例 ABSDK,登录成功后把 Token 保存在 SDK 的静态状态中;后续请求从该全局状态读取 Token。HTTP Client 和 HTTP Context 也由 SDK 共享。

由此得到当前 Bridge 约束:

  • 任一时刻只维护一个活动 ABSDK 会话;
  • 所有 SDK 调用必须在后台执行;
  • 所有 SDK 调用默认串行化;
  • 切换提供方前必须退出 Bridge 本地会话;
  • JAR 没有 logout,Bridge 的 logout 只能清除本地会话并阻止后续调用;
  • Bridge 不通过反射访问或清理 SDK 私有 Token;重新登录会覆盖 SDK 中的 Token。

JAR 自身是否保证并发线程安全仍未得到厂商确认。

5. 传输事实

字节码确认 SDK 的基础地址硬编码为:

http://a-box.com.cn/abox/index.php/

SDK 使用 Apache HttpClient 4.x,所有已检查业务请求均为 POST。归档 Sample 同时包含:

httpclient-4.3.6.jar
httpcore-4.3.3.jar
commons-codec-1.6.jar
commons-logging-1.1.3.jar

JAR 内含宽松的 TLS 信任实现。正式产品不得把这一历史传输行为扩展为 App 其他网络请求的安全策略,也不得记录密码、Token、API Key 或敏感红外载荷。

真实 Bridge 工厂使用 @CleartextAbsdkTransport 作为编译期 opt-in 门禁,并且当前没有接入 Android App。该标记只防止误接入,不会为链路提供加密。生产集成前必须确定并批准 HTTPS、受控加密代理或其他能够提供机密性与完整性的传输控制;在此之前不得启用真实 ABox 入口。

6. setHostInfo 的已确认行为

setHostInfo(url, hotelName, roomNo, apikey) 向以下端点发送初始化请求:

POST abjhsdk/init

四个参数作为表单字段提交:

url
hotelName
roomNo
apikey

其中第一个参数只写入请求字段 url,不会修改 SDK 的硬编码 HTTP 基础地址。因此原始 JAR 不能通过该公开 API 直接切换到任意自有后端。

仍未确认:

  • 正式业务中四个字段的具体语义;
  • ABox 官方环境是否要求每次启动调用;
  • hotel、room 和 API Key 的正式取值;
  • 初始化失败对后续登录和设备操作的影响。

所以 feat/absdk-bridge 不实现或调用 setHostInfo。

7. MVP 端点与数据

SDK 方法 端点 关键参数或返回
setHostInfo abjhsdk/init url、hotelName、roomNo、apikey
loginWithUsername abjhsdk/user/login username、SDK 内部 MD5 处理后的 password;返回 token
isValidateToken abjhsdk/user/validate 返回 status:0/1
getSockStatus abjhsdk/sock/properties name;返回 status:0/1
sockCtrl abjhsdk/zigbee/sock/{device} status:0/1
getThStatus abjhsdk/th/properties name;返回 temperature、humidity
sendIr abjhsdk/ir/{device}/send irKey
studyIrByIrDevName abjhsdk/ir/{device}/study irKey
downloadIrCodes abjhsdk/ir/{device}/download 每项发送 irKey、irCode

downloadIrCodes(List<IrInfo>) 并不是单次批量 HTTP 请求。JAR 按输入顺序逐项调用下载端点,并返回每项 status。只要有任一项失败,总体代码为 20204,同时 dicDatas["ret"] 仍包含逐项结果。

8. 请求签名

字节码显示 SDK 会:

  1. 取得秒级时间戳;
  2. 对业务参数键排序并组装签名原文;
  3. 加入端点、时间戳和 SDK 内部 secret;
  4. 使用 MD5 生成摘要;
  5. 按时间戳计算的位置插入时间片段并添加固定前缀;
  6. 在请求的 system 数据中提交签名、时间戳和 Token。

这些事实用于判断协议边界,不在本轮重新实现签名算法或对外宣称兼容性。正式兼容后端开始前,必须建立脱敏签名测试向量并复核每个字段和编码细节。

9. 响应结构

字节码解析的响应结构为:

{
  "code": "00000",
  "msg": "...",
  "data": {}
}

data 被转换为 ABRet.dicDatas。解析或请求失败时,SDK 可返回 20000 和请求失败信息。

当前 Bridge 对成功数据进行防御性解析:缺失字段、未知状态或错误类型不会导致 ClassCastException 泄漏到调用方。

10. 错误码来源

完整错误码表来自厂商文档和历史 AboxConstants.java。Bridge 保留原始 externalCode,并映射为以下领域分类:

  • Token 无效;
  • 认证失败;
  • 网络错误;
  • 请求参数错误;
  • 设备不存在;
  • 设备离线;
  • 无数据;
  • 操作失败;
  • 未知错误。

错误 msg 只作为诊断 detail,不直接作为最终 UI 文案。

11. 历史 Sample 观察

历史 Sample:

  • 直接调用 ABSDK.getInstance();
  • 使用 Executor 或 AsyncTask 把同步调用移出 UI 线程;
  • 没有调用 setHostInfo;
  • logout 是空实现;
  • 使用普通 SharedPreferences 明文保存密码;
  • 全局允许明文网络。

正式实现只复用已确认的 SDK 调用事实,不复制 AsyncTask、明文凭据或全局明文网络配置。

12. 仍待确认

  • ABox 官方初始化的完整流程和参数;
  • 文档所称 Token 两小时有效期在真实环境中的精确表现;
  • Token 失效时各设备接口实际返回的代码;
  • SDK 的厂商线程安全保证;
  • 真实账号、设备名称和硬件环境;
  • 原始 JAR 指向自有兼容后端的最终授权方案;
  • ABSDK 和附属 JAR 的生产分发许可;
  • 所有端点的完整表单编码和可重复签名测试向量。

这些事项验证前不得作为产品实现承诺。