Fix: Unicast source-routed packets at origin instead of broadcasting

This commit is contained in:
callebtc
2026-01-15 15:58:57 +07:00
parent 83fbcca557
commit 6b7f438c8b
2 changed files with 44 additions and 2 deletions
@@ -37,7 +37,7 @@ class BluetoothConnectionManager(
// Component managers
private val permissionManager = BluetoothPermissionManager(context)
private val connectionTracker = BluetoothConnectionTracker(connectionScope, powerManager)
private val packetBroadcaster = BluetoothPacketBroadcaster(connectionScope, connectionTracker, fragmentManager)
private val packetBroadcaster = BluetoothPacketBroadcaster(connectionScope, connectionTracker, fragmentManager, myPeerID)
// Delegate for component managers to call back to main manager
private val componentDelegate = object : BluetoothConnectionManagerDelegate {
@@ -42,7 +42,8 @@ import kotlinx.coroutines.channels.actor
class BluetoothPacketBroadcaster(
private val connectionScope: CoroutineScope,
private val connectionTracker: BluetoothConnectionTracker,
private val fragmentManager: FragmentManager?
private val fragmentManager: FragmentManager?,
private val myPeerID: String
) {
companion object {
@@ -349,6 +350,47 @@ class BluetoothPacketBroadcaster(
val senderNick = senderPeerID.let { pid -> nicknameResolver?.invoke(pid) }
val route = packet.route
val routeInfo = if (!route.isNullOrEmpty()) "routed: ${route.size} hops" else null
// Source Routing for Originating Packets
// If we are the sender and a source route is defined, we must send ONLY to the first hop.
if (packet.senderID.toHexString() == myPeerID && !packet.route.isNullOrEmpty()) {
val firstHop = packet.route!![0].toHexString()
Log.d(TAG, "Source Routing: Packet has explicit route, attempting to send to first hop: $firstHop")
var sent = false
// Try to find first hop in server connections (subscribedDevices)
val serverTarget = connectionTracker.getSubscribedDevices()
.firstOrNull { connectionTracker.addressPeerMap[it.address] == firstHop }
if (serverTarget != null) {
Log.d(TAG, "Source Routing: sending directly to first hop (server conn) $firstHop: ${serverTarget.address}")
if (notifyDevice(serverTarget, data, gattServer, characteristic)) {
val toPeer = connectionTracker.addressPeerMap[serverTarget.address]
logPacketRelay(typeName, senderPeerID, senderNick, incomingPeer, incomingAddr, toPeer, serverTarget.address, packet.ttl, packet.version, routeInfo)
sent = true
}
}
// Try to find first hop in client connections if not sent yet
if (!sent) {
val clientTarget = connectionTracker.getConnectedDevices().values
.firstOrNull { connectionTracker.addressPeerMap[it.device.address] == firstHop }
if (clientTarget != null) {
Log.d(TAG, "Source Routing: sending directly to first hop (client conn) $firstHop: ${clientTarget.device.address}")
if (writeToDeviceConn(clientTarget, data)) {
val toPeer = connectionTracker.addressPeerMap[clientTarget.device.address]
logPacketRelay(typeName, senderPeerID, senderNick, incomingPeer, incomingAddr, toPeer, clientTarget.device.address, packet.ttl, packet.version, routeInfo)
sent = true
}
}
}
if (sent) return
Log.w(TAG, "Source Routing: First hop $firstHop not connected. Falling back to standard broadcast logic.")
}
if (packet.recipientID != SpecialRecipients.BROADCAST) {
val recipientID = packet.recipientID?.let {