mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
refactor(service): harden KMP service layer — database init, connection reliability, handler decomposition (#4992)
This commit is contained in:
parent
e111b61e4e
commit
6af3ad6f0c
62 changed files with 3808 additions and 735 deletions
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.core.repository
|
||||
|
||||
import org.meshtastic.proto.MeshPacket
|
||||
|
||||
/** Interface for handling admin messages from the mesh (config, metadata, session passkey). */
|
||||
interface AdminPacketHandler {
|
||||
/**
|
||||
* Processes an admin message packet.
|
||||
*
|
||||
* @param packet The received mesh packet.
|
||||
* @param myNodeNum The local node number.
|
||||
*/
|
||||
fun handleAdminMessage(packet: MeshPacket, myNodeNum: Int)
|
||||
}
|
||||
|
|
@ -56,6 +56,21 @@ interface CommandSender {
|
|||
initFn: () -> AdminMessage,
|
||||
)
|
||||
|
||||
/**
|
||||
* Sends an admin message and suspends until the radio acknowledges it.
|
||||
*
|
||||
* This is used when the caller needs to guarantee a packet has been accepted by the radio before proceeding, such
|
||||
* as sending a shared contact before the first DM to a node.
|
||||
*
|
||||
* @return `true` if the radio accepted the packet, `false` on timeout or failure.
|
||||
*/
|
||||
suspend fun sendAdminAwait(
|
||||
destNum: Int,
|
||||
requestId: Int = generatePacketId(),
|
||||
wantResponse: Boolean = false,
|
||||
initFn: () -> AdminMessage,
|
||||
): Boolean
|
||||
|
||||
/** Sends our current position to the mesh. */
|
||||
fun sendPosition(pos: org.meshtastic.proto.Position, destNum: Int? = null, wantResponse: Boolean = false)
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ interface MeshActionHandler {
|
|||
fun start(scope: CoroutineScope)
|
||||
|
||||
/** Processes a service action from the UI. */
|
||||
fun onServiceAction(action: ServiceAction)
|
||||
suspend fun onServiceAction(action: ServiceAction)
|
||||
|
||||
/** Sets the owner of the local node. */
|
||||
fun handleSetOwner(u: MeshUser, myNodeNum: Int)
|
||||
|
|
|
|||
|
|
@ -54,8 +54,11 @@ interface NodeManager : NodeIdLookup {
|
|||
/** Starts the node manager with the given coroutine scope. */
|
||||
fun start(scope: CoroutineScope)
|
||||
|
||||
/** The local node number. */
|
||||
var myNodeNum: Int?
|
||||
/** The local node number as a thread-safe [StateFlow]. */
|
||||
val myNodeNum: StateFlow<Int?>
|
||||
|
||||
/** Sets the local node number. */
|
||||
fun setMyNodeNum(num: Int?)
|
||||
|
||||
/** Loads the cached node database from the repository. */
|
||||
fun loadCachedNodeDB()
|
||||
|
|
|
|||
|
|
@ -32,6 +32,17 @@ interface PacketHandler {
|
|||
/** Adds a mesh packet to the queue for sending. */
|
||||
fun sendToRadio(packet: MeshPacket)
|
||||
|
||||
/**
|
||||
* Adds a mesh packet to the queue and suspends until the radio acknowledges it via [QueueStatus].
|
||||
*
|
||||
* Unlike [sendToRadio], which is fire-and-forget, this method provides back-pressure so the caller can ensure a
|
||||
* packet has been accepted by the radio before proceeding. This is critical for operations where ordering matters
|
||||
* (e.g., sending a shared contact before the first DM).
|
||||
*
|
||||
* @return `true` if the radio accepted the packet, `false` on timeout or failure.
|
||||
*/
|
||||
suspend fun sendToRadioAndAwait(packet: MeshPacket): Boolean
|
||||
|
||||
/** Processes queue status updates from the radio. */
|
||||
fun handleQueueStatus(queueStatus: QueueStatus)
|
||||
|
||||
|
|
|
|||
|
|
@ -68,6 +68,9 @@ interface RadioInterfaceService {
|
|||
/** Called by an interface when it has received raw data from the radio. */
|
||||
fun handleFromRadio(bytes: ByteArray)
|
||||
|
||||
/** Flow of user-facing connection error messages (e.g. permission failures). */
|
||||
val connectionError: SharedFlow<String>
|
||||
|
||||
/** The scope in which interface-related coroutines should run. */
|
||||
val serviceScope: CoroutineScope
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.core.repository
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import org.meshtastic.core.model.DataPacket
|
||||
import org.meshtastic.proto.MeshPacket
|
||||
|
||||
/** Interface for handling telemetry packets from the mesh, including battery notifications. */
|
||||
interface TelemetryPacketHandler {
|
||||
/** Starts the handler with the given coroutine scope. */
|
||||
fun start(scope: CoroutineScope)
|
||||
|
||||
/**
|
||||
* Processes a telemetry packet.
|
||||
*
|
||||
* @param packet The received mesh packet.
|
||||
* @param dataPacket The decoded data packet.
|
||||
* @param myNodeNum The local node number.
|
||||
*/
|
||||
fun handleTelemetry(packet: MeshPacket, dataPacket: DataPacket, myNodeNum: Int)
|
||||
}
|
||||
|
|
@ -130,7 +130,10 @@ class SendMessageUseCaseImpl(
|
|||
|
||||
private suspend fun sendSharedContact(node: Node) {
|
||||
try {
|
||||
radioController.sendSharedContact(node.num)
|
||||
val accepted = radioController.sendSharedContact(node.num)
|
||||
if (!accepted) {
|
||||
Logger.w { "Shared contact for node ${node.num} was not acknowledged by the radio" }
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
Logger.e(ex) { "Send shared contact error" }
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue