无网络野外沟通。 手机之间只靠 BLE 自组多跳 Mesh,传文本、快捷符号、小图片、 条式语音、定位与 SOS、连续记录包(轨迹 + 事件)。没有服务器,没有账号, 不联网也不需要联网 —— 出发前当面扫码组队,之后整趟行程都在离线状态下工作。
当前版本 v0.1.0(内测),协议 WLP/1。仅 Android(minSdk 26 / Android 8.0)。
BLE 链路层还没有在真机上跑过(STATUS.md K7)。 所有协议逻辑都在 JVM 与
:transport-sim上验过、UI 在模拟器上跑过, 但「两台手机之间真能不能连上」这件事,要按docs/agent/field-test.md实测才算数。
| 承载 | 仅 BLE(GATT 双角色:中心 + 外设)。不做 Wi-Fi Direct、不做公网回退 |
| 组网 | 受控泛洪 + TTL 钳制 + 10–220 ms 抖动抑制;优先级队列 P0–P3;发件箱 spray-and-wait |
| 加密 | 群消息 ChaCha20-Poly1305 + 群密钥;私聊 Noise XX(X25519 / ChaChaPoly / SHA-256);广播 Ed25519 签名 |
| 语音 | Codec2 3200 bps,条式(先录后发,≤ 30 s,5 s 分段)。不做实时对讲 |
| 图片 | 长边 ≤ 320 px、WebP q≈60、整图 ≤ 40 KB;先发 64 px 缩略图 |
| 地图 | 高德 3D SDK(离线底图);没有 Key 或这个 ABI 没有 .so 时退到占位地图,功能不减 |
| 存储 | Room;媒体正文存 filesDir/blobs/<sha256>;私钥与群密钥由 Android Keystore 包装的主密钥加密 |
协议的字节级定义在 docs/spec/protocol.md —— 那是唯一事实来源,
代码与它不一致时以文档为准。
需要 JDK 21(编译用;产物是 Java 17 字节码)、Android SDK 36、NDK 28.2.13676358、CMake 3.22.1。
git clone <repo> && cd wildlink
# 开发包(含调试面板,x86_64 可在模拟器上跑)
./gradlew :app:assembleDevDebug
# 内测包(R8 + 资源压缩,只出 arm64-v8a 与 armeabi-v7a)
./gradlew :app:assembleDevRelease产物在 app/build/outputs/apk/<flavor>/<buildType>/,按 ABI 分开出包
(高德的 .so 有 20 MB,打通用包会到 42 MB)。装的时候挑你手机的那一份:
| ABI | devRelease 大小 | 说明 |
|---|---|---|
arm64-v8a |
24.7 MiB | 绝大多数在售手机 |
armeabi-v7a |
18.3 MiB | 老设备 |
x86_64 |
仅 debug | 模拟器。高德不提供 x86 的 .so,所以模拟器上只有占位地图 |
Key 与「包名 + 签名 SHA1」绑定,放在不入库的 local.properties:
AMAP_KEY_DEV=<32 位 Key> # com.wildlink.app.dev
AMAP_KEY_PROD=<32 位 Key> # com.wildlink.app取不到 Key 时 BuildConfig.AMAP_KEY 为空串,App 照常编译运行,地图走占位画布。
申请步骤与两个 SHA1 见 docs/spec/build-config.md §3。
- debug:仓库内的
keystore/wildlink-debug.jks(口令android),保证换机器 SHA1 不变。 - release:
keystore/wildlink-release.jks+keystore/release.properties,都不入库。 缺失时 release 构建不签名(能编译,不能安装)。
:app (Compose UI / Service / 高德地图) → :core ← :transport-ble (Android BLE)
↑ ← :transport-sim (TCP / 进程内,测试用)
:codec2 (NDK + JNI)
| 模块 | 是什么 | 能不能在 JVM 上测 |
|---|---|---|
:core |
纯 Kotlin/JVM。协议编解码、路由、去重、分片、优先级队列、加密、记录包、§8 同步。禁止引用任何 android.* |
✅ 全部 |
:transport-ble |
Android BLE 双角色实现,只实现 :core 的 Transport 接口 |
❌ 要真机 |
:transport-sim |
同一接口的 TCP / 进程内实现,用来在 JVM 里跑多节点 Mesh | ✅ |
:codec2 |
从源码编译的 drowe67/codec2(LGPL-2.1,动态链接),JNI 暴露编解码 |
|
:app |
UI、Service、存储、地图。只与 :core 的 WildLinkNode 门面打交道,不碰蓝牙对象 |
部分 |
这条边界是有代价的(:core 里的坐标转换、GPX、日志都得自己写一遍),
换来的是协议逻辑全部可以在 JVM 上跑:现在 :core 有 225 条单测(另加 sim 7 + app 18),
BLE 一行都还没上真机,协议部分照样是绿的。
# 一次跑完(提交前的门槛)
./gradlew :core:test :transport-sim:test :app:testDevDebugUnitTest \
:app:lintDevDebug :core:jacocoCoverageGate :app:assembleDevDebug
# 覆盖率报告:core/build/reports/jacoco/test/html/index.html
./gradlew :core:jacocoTestReport
# 要设备 / 模拟器
./gradlew :codec2:connectedDebugAndroidTest # Codec2 编解码,9 条
./gradlew :app:connectedDevDebugAndroidTest # 12 屏 × 深浅 × 中英 = 48 次渲染:core 指令覆盖率 82.7%(闸门 70%,低于就直接失败)。
真机测试项(BLE、后台保活、耗电、Codec2 可懂度、二维码扫描)在
docs/agent/field-test.md,做完把记录表填回来。
| 路径 | 内容 |
|---|---|
CLAUDE.md |
项目宪法:已定死的决策、模块边界、完成定义、不要做的事 |
docs/spec/requirements.md |
功能需求与验收点(R 编号) |
docs/spec/protocol.md |
WLP/1 字节级规范,唯一事实来源 |
docs/spec/design-system.md |
颜色 / 字体 / 尺寸 Token,多语言与主题规则 |
docs/spec/build-config.md |
包名、变体、签名、SHA1、高德 Key |
docs/spec/acceptance.md |
三层验收清单(JVM / 模拟器 / 真机) |
docs/agent/STATUS.md |
进度、已知问题(K 编号)、决策记录(D 编号) |
docs/agent/security-review.md |
安全审计:校验顺序、重放、握手、模糊测试、日志、密钥存储 |
docs/agent/field-test.md |
真机测试手册与记录表 |
docs/agent/screens/diff.md |
截图与设计稿的逐项比对 |
本项目自身的代码采用 GPL-3.0(见 LICENSE)。
第三方部分:
- Codec2 —— LGPL-2.1。以独立的
libcodec2.so动态链接,可自行替换; 源码在codec2/src/main/cpp/vendor/codec2/。 动态链接是有意的选择:LGPL 要求使用者能换掉这个库,静态链接会让这件事做不到。 因此 codec2 保持 LGPL-2.1,本项目保持 GPL-3.0,两者各自独立生效。 - 高德地图 Android SDK —— 遵循高德开放平台服务条款。只用离线底图,不发起在线请求。
- Bouncy Castle(MIT 风格)、ZXing(Apache-2.0)、AndroidX / Jetpack Compose(Apache-2.0)、 Chakra Petch 与 IBM Plex Mono(SIL OFL 1.1)。
App 内「设置 → 开源许可」里有同一份声明。