feat: Queue special app PortNums when disconnected#4495
Conversation
This change introduces a queue for specific `PortNum` data packets when the device is disconnected. The following `PortNum` types will now be queued and sent upon reconnection: - `ATAK_PLUGIN` - `ATAK_FORWARDER` - `DETECTION_SENSOR_APP` - `PRIVATE_APP` This functionality is verified through new unit tests in `MeshCommandSenderQueueTest`. Additionally, the `mesh_service_example` app has been updated to include buttons for sending these special packet types for testing purposes. Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR extends the app’s offline-send behavior by queuing additional “special app” PortNum data packets while the device is disconnected, then sending them after reconnection. It also adds unit tests around the queuing behavior and updates the mesh_service_example UI to exercise these packet types.
Changes:
- Expanded
MeshCommandSender’s queued/rememberedPortNumlist to includeATAK_PLUGIN,ATAK_FORWARDER,DETECTION_SENSOR_APP, andPRIVATE_APP. - Added
MeshCommandSenderQueueTestunit tests verifying queued vs non-queued behavior while disconnected. - Updated
mesh_service_exampleto send and listen for these special packet types (UI + broadcast intent filter).
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
app/src/main/java/com/geeksville/mesh/service/MeshCommandSender.kt |
Adds special PortNum values to the set of packet types that are queued while disconnected. |
app/src/test/java/com/geeksville/mesh/service/MeshCommandSenderQueueTest.kt |
New unit tests covering queuing behavior for the special PortNums. |
mesh_service_example/src/main/kotlin/com/meshtastic/android/meshserviceexample/MeshServiceViewModel.kt |
Adds a helper to send a DataPacket using an arbitrary PortNum. |
mesh_service_example/src/main/kotlin/com/meshtastic/android/meshserviceexample/MainScreen.kt |
Adds a UI section with buttons to send special-port packets from the example app. |
mesh_service_example/src/main/kotlin/com/meshtastic/android/meshserviceexample/MainActivity.kt |
Registers broadcast receiver actions for the additional special PortNum receive events. |
| Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp)) { | ||
| Button(onClick = { viewModel.sendSpecialPacket(PortNum.PRIVATE_APP) }, modifier = Modifier.weight(1f)) { | ||
| Text("Send Private") | ||
| } | ||
| } |
There was a problem hiding this comment.
The PR description says the example app includes buttons to send all special PortNum types (including ATAK_FORWARDER), but SpecialAppSection only exposes ATAK_PLUGIN, DETECTION_SENSOR_APP, and PRIVATE_APP. Add a button for PortNum.ATAK_FORWARDER (or update the PR description if it’s intentionally omitted).
| PortNum.ATAK_PLUGIN.value, | ||
| PortNum.ATAK_FORWARDER.value, | ||
| PortNum.DETECTION_SENSOR_APP.value, | ||
| PortNum.PRIVATE_APP.value, | ||
| ) |
There was a problem hiding this comment.
By adding more PortNums to rememberDataType, more packets can accumulate in offlineSentPackets, which currently has no size/age bound. Consider adding a max queue size (and/or dropping oldest with logging) to prevent unbounded memory growth if an app/plugin sends lots of these while disconnected.
| @Test | ||
| fun `sendData queues ATAK_PLUGIN when disconnected`() { | ||
| val packet = DataPacket(dataType = PortNum.ATAK_PLUGIN.value, bytes = ByteString.EMPTY) | ||
| commandSender.sendData(packet) | ||
|
|
||
| verify(exactly = 0) { packetHandler.sendToRadio(any<org.meshtastic.proto.MeshPacket>()) } | ||
|
|
||
| connectionStateFlow.value = ConnectionState.Connected | ||
| commandSender.processQueuedPackets() | ||
|
|
||
| verify(exactly = 1) { packetHandler.sendToRadio(any<org.meshtastic.proto.MeshPacket>()) } | ||
| } |
There was a problem hiding this comment.
This test file repeats the same arrange/act/assert pattern for each PortNum. Consider parameterizing these cases (e.g., a single test iterating over queued PortNums, plus one for a non-queued PortNum) to reduce duplication and make it easier to extend the list later.
| fun sendSpecialPacket(portNum: PortNum) { | ||
| meshService?.let { service -> | ||
| try { | ||
| val packet = |
There was a problem hiding this comment.
sendSpecialPacket() silently does nothing when meshService is null (unbound), unlike sendMessage() which logs a warning. For consistency and easier debugging, add the same ?: Log.w(...) / log entry fallback here so button presses aren’t ignored without feedback.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #4495 +/- ##
==========================================
+ Coverage 10.60% 10.64% +0.03%
==========================================
Files 427 427
Lines 14356 14387 +31
Branches 2386 2387 +1
==========================================
+ Hits 1523 1531 +8
- Misses 12538 12561 +23
Partials 295 295 ☔ View full report in Codecov by Sentry. |
This change introduces a queue for specific
PortNumdata packets when the device is disconnected. The followingPortNumtypes will now be queued and sent upon reconnection:ATAK_PLUGINATAK_FORWARDERDETECTION_SENSOR_APPPRIVATE_APPThis functionality is verified through new unit tests in
MeshCommandSenderQueueTest. Additionally, themesh_service_exampleapp has been updated to include buttons for sending these special packet types for testing purposes.