Files
Meshtastic-Android/core/takserver
James Rich 4f57e65097 refactor: remove AIDL API and modernize radio architecture
Remove the deprecated AIDL/IPC API surface and perform deep architectural
modernization of the radio command pipeline, aligning with the meshtastic-sdk
AdminApiImpl pattern for future SDK migration.

Key changes:

1. AIDL Removal & Infrastructure Cleanup
   - Delete core:api module and all AIDL interfaces
   - Remove ServiceBroadcasts + CommonParcelable infrastructure
   - Remove core:api from CI workflow lint/publish steps

2. Model Modernization
   - Introduce NodeAddress sealed class with type-safe addressing
   - Remove deprecated DataPacket constants in favor of NodeAddress
   - Consolidate dual node maps into single source with getNodeById
   - Split large model files, deduplicate NodeEntity, flatten RadioController

3. Service Layer Refactoring (SDK-aligned)
   - Remove ServiceAction sealed class, use direct suspend calls
   - Convert CommandSender & MeshActionHandler to suspend APIs
   - Merge MeshActionHandler into DirectRadioControllerImpl
     (ViewModel → RadioController → CommandSender, no intermediate layer)
   - Build AdminMessage protos directly with typed protos end-to-end
   - Apply structured concurrency to NodeRequestActions/NodeManagementActions
   - Fix CancellationException handling throughout

Architecture (before → after):
  ViewModel → RadioController → Handler → CommandSender → PacketHandler
  ViewModel → RadioController → CommandSender → PacketHandler

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-23 15:00:49 -05:00
..

:core:takserver

Overview

The :core:takserver module implements the Meshtastic ↔ TAK (Team Awareness Kit) bridge. It embeds an mTLS TCP server (port 8089) compatible with ATAK (Android), iTAK (iOS), and WinTAK clients, enabling mesh-networked position sharing and GeoChat with TAK-enabled devices.

Targets: Android · JVM (Desktop) · iOS — fully multiplatform with expect/actual splits for compression, file I/O, and the TCP server itself.

Key Responsibilities

  • Serve an mTLS TCP listener (port 8089) compatible with the CoT (Cursor-on-Target) protocol
  • Convert Meshtastic protobuf packets (TAKPacketV2) to CoT XML events and vice versa
  • Generate ATAK Data Package .zip exports (team contacts, map overlays)
  • Compress CoT payloads using Zstd (TAK SDK format) with expect/actual platform implementations
  • Buffer up to 50 CoT messages for 5 minutes when no TAK clients are connected; drain on reconnect
  • Provide Crowdin-localised TAK preference XML for ATAK client provisioning

Source Structure

src/
├── commonMain/kotlin/org/meshtastic/core/takserver/
│   ├── TAKServer.kt                 ← interface + expect createTAKServer()
│   ├── TAKServerManager.kt          ← interface + TAKServerManagerImpl (offline queue)
│   ├── TAKMeshIntegration.kt        ← bridges mesh service ↔ TAK server
│   ├── CoTConversion.kt             ← Position/User → CoTMessage extension fns
│   ├── CoTXml.kt / CoTXmlParser.kt / CoTXmlFrameBuffer.kt
│   ├── CoTXmlDataClasses.kt
│   ├── CoTDetailStripper.kt
│   ├── TAKModels.kt                 ← CoTMessage, TAKClientInfo, TAKConnectionEvent
│   ├── TAKPacketConversion.kt
│   ├── TAKPacketV2Conversion.kt
│   ├── TAKDefaults.kt
│   ├── TAKDataPackageGenerator.kt
│   ├── RouteDataPackageGenerator.kt
│   ├── TAKPrefXmlDataClasses.kt
│   ├── TakV2TypeMapper.kt
│   ├── TakConversionHelpers.kt
│   ├── XmlUtils.kt
│   ├── AtakFileWriter.kt            ← expect
│   ├── TakSdkCompressor.kt          ← expect (Zstd TAK-SDK frame)
│   ├── TakV2Compressor.kt           ← expect (Zstd TAKPacketV2 frame)
│   ├── ZipArchiver.kt               ← expect
│   ├── TakFixtureLoader.kt          ← expect (test fixtures)
│   ├── TakMeshTestRunner.kt
│   └── di/
│       └── CoreTakServerModule.kt
├── jvmAndroidMain/kotlin/           ← actual TAKServerJvm, TAKClientConnection, TakCertLoader
├── androidMain/kotlin/              ← actual AtakFileWriter (Android)
├── jvmMain/kotlin/                  ← actual AtakFileWriter (Desktop), XML pull-parser
└── iosMain/kotlin/                  ← actual TAKServerIos, actual compression impls

Notable APIs

TAKServer (interface)

interface TAKServer {
    val connectionCount: StateFlow<Int>
    var onMessage: ((CoTMessage, TAKClientInfo?) -> Unit)?
    var onClientConnected: (() -> Unit)?

    suspend fun start(scope: CoroutineScope): Result<Unit>
    fun stop()
    suspend fun broadcast(cotMessage: CoTMessage)
    suspend fun broadcastRawXml(xml: String)
    suspend fun hasConnections(): Boolean
}

The mTLS listener binds on port 8089 using a bundled server.p12 / ca.pem identity, compatible with the ATAK Data Package provisioning flow.

TAKServerManager (interface)

interface TAKServerManager {
    val isRunning: StateFlow<Boolean>
    val connectionCount: StateFlow<Int>
    val inboundMessages: SharedFlow<InboundCoTMessage>

    suspend fun start(scope: CoroutineScope)
    fun stop()
    suspend fun broadcast(cotMessage: CoTMessage)
    suspend fun broadcastRawXml(xml: String)
}

TAKServerManagerImpl adds an offline queue: buffers up to 50 CoT messages for 5 minutes when no clients are connected and drains them automatically on the next onClientConnected callback.

CoTMessage

@Serializable
data class CoTMessage(
    val uid: String,
    val type: String,              // e.g. "a-f-G-U-C" (friendly ground unit)
    val time: Instant,
    val lat: Double, val lon: Double, val hae: Double,
    val contact: CoTContact?,
    val group: CoTGroup?,
    val track: CoTTrack?,
    val chat: CoTChat?,
    val remarks: String?,
    // ...
)

// Factory helpers
CoTMessage.pli(uid, callsign, lat, lon, ...)   // Position Location Information
CoTMessage.chat(senderUid, callsign, message, chatroom)

CoT Conversion

// Meshtastic proto → CoT
org.meshtastic.proto.Position.toCoTMessage(uid, callsign, team, role, battery): CoTMessage
org.meshtastic.proto.User.toCoTMessage(position, team, role, battery): CoTMessage

Dependency Graph

core:takserver
  ├── api → core:repository     (exported)
  ├── core:common, core:di, core:model, core:proto
  ├── okio, kotlinx.serialization.json
  ├── xmlutil-core, xmlutil-serialization
  ├── ktor-client-core, ktor-network   (TCP socket)
  └── zstd-jni (jvmAndroid/jvm), kotlinx.datetime

Local TAK Server Feature

The Local TAK Server can be enabled from the app's Settings screen. When running, ATAK/iTAK clients on the same network can connect to <device-ip>:8089 and their position reports are automatically bridged onto the mesh. Mesh node positions are broadcast to all connected TAK clients in real time.

Dependency Graph

graph TB
  :core:takserver[takserver]:::kmp-library
  :core:takserver --> :core:repository
  :core:takserver -.-> :core:common
  :core:takserver -.-> :core:di
  :core:takserver -.-> :core:model
  :core:takserver -.-> :core:proto
  :core:takserver -.-> :core:testing

classDef android-application fill:#CAFFBF,stroke:#000,stroke-width:2px,color:#000;
classDef android-application-compose fill:#CAFFBF,stroke:#000,stroke-width:2px,color:#000;
classDef compose-desktop-application fill:#CAFFBF,stroke:#000,stroke-width:2px,color:#000;
classDef android-feature fill:#FFD6A5,stroke:#000,stroke-width:2px,color:#000;
classDef android-library fill:#9BF6FF,stroke:#000,stroke-width:2px,color:#000;
classDef android-library-compose fill:#9BF6FF,stroke:#000,stroke-width:2px,color:#000;
classDef android-test fill:#A0C4FF,stroke:#000,stroke-width:2px,color:#000;
classDef jvm-library fill:#BDB2FF,stroke:#000,stroke-width:2px,color:#000;
classDef kmp-feature fill:#FFD6A5,stroke:#000,stroke-width:2px,color:#000;
classDef kmp-library-compose fill:#FFC1CC,stroke:#000,stroke-width:2px,color:#000;
classDef kmp-library fill:#FFC1CC,stroke:#000,stroke-width:2px,color:#000;
classDef unknown fill:#FFADAD,stroke:#000,stroke-width:2px,color:#000;