Repository navigation
Architecture
Clean MVVM in Kotlin and Jetpack Compose (Material 3), Coroutines and Flow/StateFlow throughout. The only non-Kotlin code is the vendored Codec2 speech codec (C, LGPL-2.1) behind a tiny JNI wrapper.
UI (Compose) -> MainViewModel (StateFlow) -> MeshEngine -> MeshMessenger -> MeshTransport -> radio
| |
storage, identity protocol rules (JVM-testable)
- The UI only observes
MainViewModel. Anything with a rule in it (conversations, known nodes, positions, activity) is computed in theprotocolpackage so it is JVM-tested. -
Hardware radio APIs are strictly isolated behind
MeshTransport, which moves opaque bytes between peers identified by a stable node ID. - The application protocol (framing, chat, ACKs, relay) lives in
com.nodewave.mesh.protocoland must stay free of radio/BLE APIs.
The radio, identity, storage and messenger belong to MeshEngine (owned by NodeWaveApp) and run under RadioService, a foreground service. View models only observe. The radio is never tied to an activity or view-model lifecycle.
| Package / file | Role |
|---|---|
MainActivity, MainViewModel
|
Compose host, permission requests, thin StateFlow adapter |
NodeWaveApp, MeshEngine, RadioService
|
process-wide engine and foreground service |
PositionTracker, VoiceController
|
location and voice orchestration |
ui/ |
AppShell and the five tabs (chats, network, map, nodes, settings), components (shared tactical look), theme
|
crypto/ |
Crypto, Hkdf, Sealed, HandshakeMessage, SessionManager, LocalIdentity, ContactCode. Pure JVM, no Android APIs |
data/ |
framework SQLite (NodeWaveDb), MessageStore/PeerKeyStore implementations, KeystoreCipher, AppSettings, map files and downloader |
protocol/ |
Packet, Router, RoutingTable, HelloPayload, MeshMessenger, Group, Position, DataFrame, TransferAssembler, Blob policy, LinkClass, LinkCodec, Directory, ChatMessage
|
transport/ |
MeshTransport, BleTransport, CompositeTransport, FastLink, WifiDirectLink, LinkBenchmark, RadioSettings, MeshLog
|
audio/ |
the only place with Android audio APIs: recorder, player, Opus and Codec2 codecs, image compressor |
-
Concurrency: BLE callbacks run on arbitrary system threads, so they are wrapped with
callbackFlowor run onDispatchers.IO. Every GATT connection and write has a strict timeout (withTimeoutOrNull). -
Logging: every radio state transition, error and permission check is logged with the tag
MESH_RADIO. - Persistence: SQLite behind store interfaces (JVM tests use in-memory fakes). Message text is encrypted at rest with the Keystore.
-
Sessions: keys exist only in memory (
SessionManager), are wiped when a session ends, and are never written to disk or logs. Only the identity key (Keystore-encrypted) and public keys are stored. -
Transport links: the transport hands out opaque link ids. A packet may be sent back on the link it arrived on (
SendOptions.viaLink), and a send that expects an answer keeps the link open briefly (expectReply). The messenger trusts a link hint only after the packet was authenticated, and never for relayed packets. -
Bulk data: the transport reports a
LinkClassper peer. Chunking, acknowledgement bitmaps, class rules for relays and reassembly limits live inprotocol. A new kind of blob must go throughBlobPolicyand be refused on slow links. -
Fast link: Wi-Fi Direct is an optional
FastLinkTransportbehindMeshTransport.fastLink. The main radio finds peers and carries the sealed negotiation (LINKpackets); the fast radio only moves bytes between two nodes that agreed.CompositeTransporthides the difference. - Dependencies: only open-source libraries under an AGPL-compatible licence: Codec2 (LGPL-2.1), AndroidX CameraX and ZXing core (Apache-2.0, contact QR codes), osmdroid with the Mapsforge renderer (Apache-2.0 / LGPL-3.0, maps). No proprietary dependency.
Multi-hop logic (routing, seen-cache, TTL, HELLO) lives in MeshMessenger, Router and RoutingTable. Router picks the next hop: direct neighbour, learned route, or flood. Routes come from neighbours' sealed HELLOs and expire after 150 s. Authenticated packets enter a seen-cache keyed by (senderId, messageId). Relay behaviour is covered by the in-memory MeshNetworkTest.
Each node advertises a GATT service (6e0d7a10-4e57-4c0e-9b1f-6d6573680001) with a write characteristic (...0002) and a notify characteristic (...0003), carries its node ID and name in the scan response, scans for others and connects on demand. A reply goes back over the same connection by notification. A watchdog restarts a scan or advertisement that stays silent, and idle links close quickly to free the radio.