mirror of
https://github.com/hpd840321/craftlabs-authorization-sdk.git
synced 2026-06-09 10:00:30 +08:00
docs: add BitAnswer 1:1 mapping refactor design spec
Architecture audit revealed AuthProvider (8 methods) only covers ~15% of BitAnswer's 50+ C API surface. This spec proposes: - 6 capability interfaces (LicenseLifecycle, FeatureManagement, DataItemStore, LicenseInfoQuery, CheckoutManager, LicenseUtility) - 40+ JNI → Rust → BitAnswer C API mappings - 4-phase migration path with backward compatibility
This commit is contained in:
@@ -0,0 +1,423 @@
|
||||
# BitAnswer 1:1 映射重构设计
|
||||
|
||||
> **状态**: 待审核
|
||||
> **日期**: 2026-05-01
|
||||
> **触发**: 架构审计发现 `AuthProvider` (8 方法) 与 BitAnswer C API (50+ 函数) 之间存在显著缺口
|
||||
> **策略**: 大幅重构为 BitAnswer 1:1 原语映射,按能力域拆分接口
|
||||
|
||||
---
|
||||
|
||||
## 1. 审计摘要
|
||||
|
||||
### 1.1 当前状态
|
||||
|
||||
```
|
||||
Java AuthProvider (8 methods)
|
||||
└─ BitAnswerProvider / SelfHostedAuthProvider
|
||||
└─ NativeBridge (JNI, 9 native methods)
|
||||
└─ craft-core cdylib (Rust, 9 craft_* C ABI functions)
|
||||
└─ ⚠️ 全部返回 ok_result(桩实现,未调用真实 BitAnswer API)
|
||||
└─ [已废弃] .deprecated-cmake/ bitanswer_adapter(空实现)
|
||||
```
|
||||
|
||||
### 1.2 覆盖度缺口
|
||||
|
||||
| BitAnswer API 分组 | 原生函数数 | 当前覆盖 | 缺失 |
|
||||
|---|---|---|---|
|
||||
| 认证与会话 | 10+ | 3 (activate/release/close) | LoginEx, LoginByToken, Revoke, RemoveSn, SessionControl |
|
||||
| 激活与升级 | 6 | 1 (activate) | 离线升级三步骤 |
|
||||
| **特征项** | **15+** | **1 (hasFeature)** | Read/Write/Query/Release/Encrypt/Decrypt/Convert/Sign/Batch |
|
||||
| 配置项(Data) | 5 | **0** | Set/Get/Remove/Enum |
|
||||
| 借出/归还 | 9 | **0** | CheckOut/In, Borrow |
|
||||
| 信息查询 | 5+ | 1 (getLicenseInfo) | SessionInfo, ServerInfo, FeatureInfo |
|
||||
| 工具类 | 8+ | **0** | SetRootPath, SetProxy, SetAttr, CustomInfo |
|
||||
|
||||
---
|
||||
|
||||
## 2. 目标架构
|
||||
|
||||
### 2.1 接口分层(6 能力接口 + 1 入口)
|
||||
|
||||
```
|
||||
CraftLicense (顶层入口)
|
||||
- initialize(configJson) → LicenseSession
|
||||
- getVersion() → String
|
||||
- setRootPath(path) / setProxy(...) / setLocalServer(...)
|
||||
|
||||
LicenseSession (会话句柄,实现 5 个能力接口)
|
||||
├─ LicenseLifecycle — 认证/激活/心跳/释放
|
||||
├─ FeatureManagement — 特征项读写/加解密/占用释放
|
||||
├─ DataItemStore — 配置项(Data Item)存储
|
||||
├─ LicenseInfoQuery — 信息查询
|
||||
├─ CheckoutManager — 浮动授权借出/归还
|
||||
├─ LicenseUtility — 工具方法(SetAttr, CustomInfo, SessionState)
|
||||
└─ close() / isClosed() / getNativeHandle()
|
||||
```
|
||||
|
||||
### 2.2 能力接口详细定义
|
||||
|
||||
#### LicenseLifecycle
|
||||
```java
|
||||
public interface LicenseLifecycle {
|
||||
LoginResult login(LoginRequest request);
|
||||
LoginResult loginEx(LoginExRequest request);
|
||||
LoginResult loginByToken(LoginByTokenRequest request);
|
||||
LoginResult loginByPassword(LoginByPasswordRequest request);
|
||||
ActivationResult activate(ActivationRequest request);
|
||||
UpdateResult updateOnline(String url, String sn);
|
||||
OfflineUpdateRequest getRequestInfo(String sn, BindingType type);
|
||||
UpdateInfo getUpdateInfo(String url, String sn, String requestInfo);
|
||||
ApplyResult applyUpdateInfo(String updateInfo);
|
||||
HeartbeatResult heartbeat();
|
||||
ReleaseResult release();
|
||||
RevokeResult revoke(String sn);
|
||||
void removeSn(String sn);
|
||||
void sessionControl(String url, String sessionId, SessionCtlType type);
|
||||
}
|
||||
```
|
||||
|
||||
#### FeatureManagement
|
||||
```java
|
||||
public interface FeatureManagement {
|
||||
int readFeature(int featureId);
|
||||
void writeFeature(int featureId, int value);
|
||||
int convertFeature(int featureId, int p1, int p2, int p3, int p4);
|
||||
byte[] encryptFeature(int featureId, byte[] plainData);
|
||||
byte[] decryptFeature(int featureId, byte[] cipherData);
|
||||
int queryFeature(int featureId);
|
||||
int queryFeatureEx(int featureId, QueryMode mode, int required, String scope);
|
||||
Ticket queryFeatureEx2(String featureName, QueryMode mode, int required, String scope);
|
||||
int releaseFeature(int featureId);
|
||||
int releaseFeatureEx(int featureId, int consumed, String scope);
|
||||
void releaseFeatureEx2(Ticket ticket, int consumed);
|
||||
FeatureInfo getFeatureInfo(int featureId);
|
||||
int getFeatureInfo2(String featureName, String scope);
|
||||
FeatureInfoEx getFeatureInfoEx2(String featureName, String scope);
|
||||
TicketInfo getTicketInfo(Ticket ticket, TicketInfoType type);
|
||||
byte[] signFeature(int featureId, byte[] data);
|
||||
FeatureInfo getFeatureInfoByIndex(int index);
|
||||
// 批量操作
|
||||
BatchResult batchBegin(BatchMode mode);
|
||||
BatchResult batchEnd();
|
||||
}
|
||||
```
|
||||
|
||||
#### DataItemStore
|
||||
```java
|
||||
public interface DataItemStore {
|
||||
void setDataItem(String name, byte[] value);
|
||||
byte[] getDataItem(String name);
|
||||
void removeDataItem(String name);
|
||||
int getDataItemCount();
|
||||
String getDataItemName(int index);
|
||||
}
|
||||
```
|
||||
|
||||
#### LicenseInfoQuery
|
||||
```java
|
||||
public interface LicenseInfoQuery {
|
||||
LicenseInfo getLicenseInfo();
|
||||
String getSessionInfo(SessionType type);
|
||||
String getInfo(InfoType type);
|
||||
String getServerInfo(String url, String sn, String scope, ServerInfoType type);
|
||||
}
|
||||
```
|
||||
|
||||
#### CheckoutManager
|
||||
```java
|
||||
public interface CheckoutManager {
|
||||
void checkOut(String url, String scope, String featureList, int durationDays);
|
||||
void checkOutSn(String url, int featureId, int durationDays);
|
||||
void checkOutSnEx(String url, int featureId, String scope, int durationDays);
|
||||
void checkOutFeatures(String url, int[] featureIds, int durationDays);
|
||||
void checkIn(String url, int featureId);
|
||||
void checkInEx(String url, int featureId, String scope);
|
||||
String getBorrowRequest(String sn, int durationDays);
|
||||
String getBorrowFeatureRequest(int durationDays, String scope);
|
||||
void applyBorrowInfo(String borrowInfo);
|
||||
}
|
||||
```
|
||||
|
||||
#### LicenseUtility
|
||||
```java
|
||||
public interface LicenseUtility {
|
||||
void setAttr(int type, byte[] value);
|
||||
void setCustomInfo(int infoId, byte[] data);
|
||||
void setSessionState(int state);
|
||||
String getProductPath();
|
||||
int getLastError();
|
||||
String getErrorMessage();
|
||||
void testBitService(String url, String sn, int featureId);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 原生 Rust 层重构
|
||||
|
||||
### 3.1 目录结构
|
||||
|
||||
```
|
||||
native/craft-core/src/
|
||||
├── lib.rs # C ABI 入口,craft_* 函数声明
|
||||
├── ffi/
|
||||
│ ├── mod.rs
|
||||
│ ├── bitanswer.rs # BitAnswer C API 的 Rust extern 声明
|
||||
│ └── bridge.rs # craft_* → Bit_* 的桥接实现
|
||||
├── session.rs # 会话管理,BIT_HANDLE 映射
|
||||
├── activate.rs # 对接 Bit_Login / Bit_UpdateOnline
|
||||
├── license.rs # 对接 Bit_GetSessionInfo / Bit_GetInfo
|
||||
├── feature.rs # 对接 Bit_ReadFeature / Bit_WriteFeature / Bit_QueryFeature 等
|
||||
├── data_item.rs # 对接 Bit_SetDataItem / Bit_GetDataItem 等
|
||||
├── checkout.rs # 对接 Bit_CheckOutSn / Bit_CheckIn 等
|
||||
├── heartbeat.rs # 对接 Bit_Heartbeat
|
||||
├── security/ # 安全模块(保持不变)
|
||||
│ ├── mod.rs
|
||||
│ ├── anti_debug.rs
|
||||
│ ├── dynamic_api.rs
|
||||
│ ├── integrity.rs
|
||||
│ ├── obfuscation.rs
|
||||
│ └── string_encrypt.rs
|
||||
└── error.rs # BIT_ERROR_CODES → LicenseError 映射
|
||||
```
|
||||
|
||||
### 3.2 JNI 映射表(NativeBridge 扩展)
|
||||
|
||||
| Java 方法 | Rust craft_* | BitAnswer C API |
|
||||
|---|---|---|
|
||||
| `nativeInitialize(String)` | `craft_initialize` | 内部引导 |
|
||||
| `nativeLogin(long, String, int)` | `craft_login` | `Bit_Login` |
|
||||
| `nativeLoginEx(long, String, int, String, int)` | `craft_login_ex` | `Bit_LoginEx` |
|
||||
| `nativeLoginByToken(long, String, String)` | `craft_login_by_token` | `Bit_LoginByToken` |
|
||||
| `nativeLoginByPassword(long, ...)` | `craft_login_by_password` | `Bit_LoginByPassword` |
|
||||
| `nativeLogout(long)` | `craft_logout` | `Bit_Logout` |
|
||||
| `nativeActivate(long, String)` | `craft_activate` | `Bit_UpdateOnline` |
|
||||
| `nativeGetRequestInfo(long, String, int)` | `craft_get_request_info` | `Bit_GetRequestInfo` |
|
||||
| `nativeGetUpdateInfo(long, String, String, String)` | `craft_get_update_info` | `Bit_GetUpdateInfo` |
|
||||
| `nativeApplyUpdateInfo(long, String)` | `craft_apply_update_info` | `Bit_ApplyUpdateInfo` |
|
||||
| `nativeReadFeature(long, int)` | `craft_read_feature` | `Bit_ReadFeature` |
|
||||
| `nativeWriteFeature(long, int, int)` | `craft_write_feature` | `Bit_WriteFeature` |
|
||||
| `nativeConvertFeature(long, int, int, int, int, int)` | `craft_convert_feature` | `Bit_ConvertFeature` |
|
||||
| `nativeEncryptFeature(long, int, byte[], int)` | `craft_encrypt_feature` | `Bit_EncryptFeature` |
|
||||
| `nativeDecryptFeature(long, int, byte[], int)` | `craft_decrypt_feature` | `Bit_DecryptFeature` |
|
||||
| `nativeQueryFeature(long, int)` | `craft_query_feature` | `Bit_QueryFeature` |
|
||||
| `nativeQueryFeatureEx(long, int, int, int, String)` | `craft_query_feature_ex` | `Bit_QueryFeatureEx` |
|
||||
| `nativeReleaseFeature(long, int)` | `craft_release_feature` | `Bit_ReleaseFeature` |
|
||||
| `nativeSignFeature(long, int, byte[], int)` | `craft_sign_feature` | `Bit_SignFeature` |
|
||||
| `nativeSetDataItem(long, String, byte[], int)` | `craft_set_data_item` | `Bit_SetDataItem` |
|
||||
| `nativeGetDataItem(long, String)` | `craft_get_data_item` | `Bit_GetDataItem` |
|
||||
| `nativeRemoveDataItem(long, String)` | `craft_remove_data_item` | `Bit_RemoveDataItem` |
|
||||
| `nativeGetDataItemNum(long)` | `craft_get_data_item_num` | `Bit_GetDataItemNum` |
|
||||
| `nativeGetDataItemName(long, int)` | `craft_get_data_item_name` | `Bit_GetDataItemName` |
|
||||
| `nativeCheckLicense(long)` | `craft_check_license` | `Bit_GetSessionInfo` |
|
||||
| `nativeGetLicenseInfo(long)` | `craft_get_license_info` | `Bit_GetInfo` |
|
||||
| `nativeGetSessionInfo(long, int)` | `craft_get_session_info` | `Bit_GetSessionInfo` |
|
||||
| `nativeGetServerInfo(long, String, String, String, int)` | `craft_get_server_info` | `Bit_GetServerInfo` |
|
||||
| `nativeGetFeatureInfo(long, int)` | `craft_get_feature_info` | `Bit_GetFeatureInfo` |
|
||||
| `nativeCheckOutSn(long, String, int, int)` | `craft_check_out_sn` | `Bit_CheckOutSn` |
|
||||
| `nativeCheckOutFeatures(long, String, int[], int, int)` | `craft_check_out_features` | `Bit_CheckOutFeatures` |
|
||||
| `nativeCheckIn(long, String, int)` | `craft_check_in` | `Bit_CheckIn` |
|
||||
| `nativeHeartbeat(long)` | `craft_heartbeat` | `Bit_Heartbeat` |
|
||||
| `nativeRevoke(long, String)` | `craft_revoke` | `Bit_Revoke` |
|
||||
| `nativeRemoveSn(long, String)` | `craft_remove_sn` | `Bit_RemoveSn` |
|
||||
| `nativeSetAttr(long, int, byte[])` | `craft_set_attr` | `Bit_SetAttr` |
|
||||
| `nativeSetCustomInfo(long, int, byte[])` | `craft_set_custom_info` | `Bit_SetCustomInfo` |
|
||||
| `nativeSetRootPath(long, String)` | `craft_set_root_path` | `Bit_SetRootPath` |
|
||||
| `nativeSetProxy(...)` | `craft_set_proxy` | `Bit_SetProxy` |
|
||||
| `nativeSetLocalServer(...)` | `craft_set_local_server` | `Bit_SetLocalServer` |
|
||||
| `nativeGetProductPath(long)` | `craft_get_product_path` | `Bit_GetProductPath` |
|
||||
| `nativeGetVersion()` | `craft_get_version` | `Bit_GetVersion` |
|
||||
| `nativeGetLastError(long)` | `craft_get_last_error` | `Bit_GetLastError` |
|
||||
| `nativeDestroy(long)` | `craft_destroy` | 资源释放 |
|
||||
|
||||
### 3.3 句柄管理
|
||||
|
||||
```rust
|
||||
// session.rs
|
||||
struct SessionState {
|
||||
bit_handle: BIT_HANDLE, // Bit_Login 返回的句柄
|
||||
config: AuthConfig, // 解析后的配置
|
||||
application_data: Vec<u8>, // 产品识别码(来自 AuthConfig)
|
||||
logged_in: bool,
|
||||
}
|
||||
|
||||
static SESSIONS: Lazy<Mutex<HashMap<i64, SessionState>>> = ...;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 数据流
|
||||
|
||||
### 4.1 完整调用链(浮动作业激活)
|
||||
|
||||
```
|
||||
Java: CraftLicense.initialize(configJson)
|
||||
└─> Rust: craft_initialize(config_json)
|
||||
├─ 解析 JSON → AuthConfig
|
||||
├─ 安全加固检查(anti_debug, integrity)
|
||||
├─ 创建 SessionState,分配 session_id
|
||||
└─ 返回 session_id (i64)
|
||||
|
||||
Java: session.activate(ActivationRequest { sn: "SN-XXXX" })
|
||||
└─> Rust: craft_activate(session_id, sn)
|
||||
├─ 从 AuthConfig 获取 bitanswer.url, loginMode
|
||||
├─ 调用 Bit_UpdateOnline(url, sn, &app_data)
|
||||
└─ 返回 ActivationResult
|
||||
|
||||
Java: session.login(LoginRequest { sn: "SN-XXXX", mode: AUTO })
|
||||
└─> Rust: craft_login(session_id, sn, mode)
|
||||
├─ 调用 Bit_Login(url, sn, &app_data, &bit_handle, BIT_MODE_AUTO)
|
||||
├─ 存储 bit_handle → SessionState
|
||||
└─ 返回 LoginResult { handle }
|
||||
|
||||
Java: session.readFeature(FACE_FEATURE_ID)
|
||||
└─> Rust: craft_read_feature(session_id, feature_id)
|
||||
├─ 获取 bit_handle
|
||||
├─ 调用 Bit_ReadFeature(bit_handle, feature_id, &value)
|
||||
└─ 返回 value (i32)
|
||||
|
||||
Java: session.checkOutSn(url, FEATURE_ZERO, 30)
|
||||
└─> Rust: craft_check_out_sn(session_id, url, feature_id, duration)
|
||||
├─ 调用 Bit_CheckOutSn(url, feature_id, &app_data, duration)
|
||||
└─ 返回 CraftResult
|
||||
|
||||
Java: session.release()
|
||||
└─> Rust: craft_release(session_id)
|
||||
├─ 调用 Bit_Logout(bit_handle)
|
||||
└─ 返回 CraftResult
|
||||
|
||||
Java: session.close()
|
||||
└─> Rust: craft_destroy(session_id)
|
||||
├─ 如果未 logout,调用 Bit_Logout
|
||||
├─ 移除 SessionState
|
||||
└─ 释放内存
|
||||
```
|
||||
|
||||
### 4.2 错误码映射
|
||||
|
||||
Rust 侧将 200+ 个 `BIT_ERROR_CODES` 分组映射为 `LicenseError` 枚举:
|
||||
|
||||
```rust
|
||||
pub enum LicenseError {
|
||||
Success,
|
||||
NetworkError,
|
||||
WrongHandle,
|
||||
InvalidParameter,
|
||||
ApplicationDataError,
|
||||
LicenseExpired,
|
||||
LicenseNotFound,
|
||||
LicenseDisabled,
|
||||
FeatureNotFound(i32),
|
||||
FeatureExpired(i32),
|
||||
FeatureTypeNotMatch(i32),
|
||||
SnInvalid,
|
||||
SnNotFound,
|
||||
SnDisabled,
|
||||
SnRevoked,
|
||||
SnExpired,
|
||||
CapacityExhausted,
|
||||
ServerBusy,
|
||||
ServerDown,
|
||||
Revoked,
|
||||
Timeout,
|
||||
TokenError,
|
||||
BorrowError,
|
||||
Unknown(i32), // 兜底
|
||||
}
|
||||
```
|
||||
|
||||
Java 侧使用 sealed interface 提供类型安全的结果:
|
||||
|
||||
```java
|
||||
public sealed interface LicenseResult {
|
||||
boolean isSuccess();
|
||||
Optional<LicenseError> error();
|
||||
String message();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 配置模型适配
|
||||
|
||||
`AuthConfig` 已有正确结构,重构后需将字段驱动到运行时:
|
||||
|
||||
| AuthConfig 字段 | 驱动行为 |
|
||||
|---|---|
|
||||
| `bitanswer.url` | `Bit_Login` / `Bit_UpdateOnline` 的 `szURL` |
|
||||
| `bitanswer.loginMode` | `Bit_Login` 的 `mode` 参数 |
|
||||
| `bitanswer.rootPath` | `Bit_SetRootPath` |
|
||||
| `bitanswer.applicationData` | 替代硬编码的 `application_data[]`,使不同产品可携带不同识别码 |
|
||||
| `features[].bitanswerFeatureId` | `readFeature(featureId)` / `queryFeature(featureId)` |
|
||||
| `features[].bitanswerFeatureName` | `queryFeatureEx2(featureName, ...)` |
|
||||
| `floating.projectId` | floating 场景校验(schema 已有) |
|
||||
| `school.edgeDeviceId` | school 场景标识 |
|
||||
|
||||
---
|
||||
|
||||
## 6. 迁移路径
|
||||
|
||||
### Phase 1 — 基础设施(不破坏现有接口)
|
||||
- Rust: `ffi/bitanswer.rs` FFI 声明 + `bridge.rs` 桥接实现
|
||||
- Rust: 实现 `craft_activate`, `craft_check_license`, `craft_heartbeat` 的真实调用
|
||||
- Java: `AuthProvider` 标记 `@Deprecated`,内部委托给 `CraftLicense`
|
||||
- Java: 新增 `CraftLicense` + `LicenseSession` + 5 个能力接口(空实现)
|
||||
- 测试: 现有测试保持通过
|
||||
|
||||
### Phase 2 — 核心 API 扩展
|
||||
- Java: 实现 `LicenseLifecycle`(login, loginEx, revoke, removeSn)
|
||||
- Java: 实现 `FeatureManagement`(read, write, query, encrypt 等)
|
||||
- Rust: 实现对应的 `craft_*` 函数
|
||||
- 测试: 每个能力域独立集成测试
|
||||
|
||||
### Phase 3 — 高级功能
|
||||
- Java: 实现 `DataItemStore`(set/get/remove/enum)
|
||||
- Java: 实现 `CheckoutManager`(checkOut/In 系列)
|
||||
- Rust: 实现离线升级流程(GetRequestInfo → GetUpdateInfo → ApplyUpdateInfo)
|
||||
- 文档: 更新 `bitanswer-client-api-overview.md` 映射表
|
||||
|
||||
### Phase 4 — 清理
|
||||
- 移除 `@Deprecated AuthProvider`
|
||||
- 移除 `.deprecated-cmake/` 下的旧适配器代码
|
||||
- 全量回归测试
|
||||
|
||||
---
|
||||
|
||||
## 7. 兼容性
|
||||
|
||||
- `schemas/craftlabs-auth-config.schema.json` — **不变**
|
||||
- 现有 `examples/config/*.json` — **不变**
|
||||
- `AuthConfig` / `AuthConfigs` — **不变**
|
||||
- `AuthProvider` — Phase 1-3 保持可用(`@Deprecated`),Phase 4 移除
|
||||
- `NativeBridge` — 现有 9 个方法保留,新增 30+ 方法
|
||||
- `craft-core` C ABI — 现有 9 个函数签名不变,新增 30+ 函数
|
||||
|
||||
---
|
||||
|
||||
## 8. 风险与缓解
|
||||
|
||||
| 风险 | 缓解 |
|
||||
|------|------|
|
||||
| Rust FFI 调用 BitAnswer .so/.dll 的链接问题 | Phase 1 先用 `libloading` 动态加载 BitAnswer 库,验证 ABI 兼容性 |
|
||||
| 200+ 错误码映射不完整 | 只映射文档中明确列出的错误码,其余走 `Unknown(code)` |
|
||||
| 多线程安全(BIT_HANDLE 是线程局部的) | `LicenseSession` 文档注明非线程安全,建议调用方池化或加锁 |
|
||||
| native 库缺失时测试无法运行 | 单元测试 mock native 层,集成测试用 `@EnabledIfNativeLibraryPresent` |
|
||||
|
||||
---
|
||||
|
||||
## 附录 A:审计发现记录
|
||||
|
||||
审计过程中发现的具体代码问题:
|
||||
|
||||
1. **Rust `activate.rs:core_activate`** — 返回硬编码 `ok_result`,未调用任何 BitAnswer API
|
||||
2. **Rust `license.rs:check_license`** — 返回硬编码 `ok_result`
|
||||
3. **Rust `license.rs:get_license_info`** — 返回固定数据(`is_licensed=1`, `expiration_date="2099-12-31"`, `feature_count=0`)
|
||||
4. **Rust `license.rs:has_feature`** — 无条件返回 `true`
|
||||
5. **Rust `heartbeat.rs:do_heartbeat`** — 返回硬编码 `ok_result`
|
||||
6. **Java `BitAnswerProvider.initialize`** — 配置 JSON 未被传递给 native 层做运行时行为驱动
|
||||
7. **`NativeBridge`** — 只有 9 个 JNI 方法,BitAnswer 有 50+ 函数
|
||||
8. **`.deprecated-cmake/bitanswer_adapter.cpp`** — `bitanswer_adapter_register()` 是空函数
|
||||
|
||||
## 附录 B:BitAnswer C API 完整清单
|
||||
|
||||
见 `examples/vcsample/bitanswer.h`(1211 行),包含 50+ 个 `Bit_*` 函数声明和 200+ 个错误码枚举。
|
||||
Reference in New Issue
Block a user