## 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>
227 lines
7.6 KiB
Markdown
227 lines
7.6 KiB
Markdown
# 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 有关的方法:
|
||
|
||
```text
|
||
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`
|
||
|
||
```text
|
||
String getCode()
|
||
String getMsg()
|
||
Map<String, Object> getDicDatas()
|
||
```
|
||
|
||
`"00000"` 表示调用成功;其他字符串代码表示失败。Bridge 不向领域层泄漏 `ABRet`、Map 或厂商类型。
|
||
|
||
### `com.dhc.absdk.IrInfo`
|
||
|
||
```text
|
||
String irDevName
|
||
String key
|
||
String code
|
||
String status
|
||
```
|
||
|
||
下载结果的 `status` 是字符串:`"0"` 表示失败,`"1"` 表示成功。
|
||
|
||
## 3. 文档与 JAR 差异
|
||
|
||
厂商 Markdown 文档把温湿度方法写成:
|
||
|
||
```text
|
||
getThStatusByDevName(String)
|
||
```
|
||
|
||
JAR 实际公开的方法是:
|
||
|
||
```text
|
||
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 的基础地址硬编码为:
|
||
|
||
```text
|
||
http://a-box.com.cn/abox/index.php/
|
||
```
|
||
|
||
SDK 使用 Apache HttpClient 4.x,所有已检查业务请求均为 POST。归档 Sample 同时包含:
|
||
|
||
```text
|
||
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)` 向以下端点发送初始化请求:
|
||
|
||
```text
|
||
POST abjhsdk/init
|
||
```
|
||
|
||
四个参数作为表单字段提交:
|
||
|
||
```text
|
||
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. 响应结构
|
||
|
||
字节码解析的响应结构为:
|
||
|
||
```json
|
||
{
|
||
"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 的生产分发许可;
|
||
- 所有端点的完整表单编码和可重复签名测试向量。
|
||
|
||
这些事项验证前不得作为产品实现承诺。
|