Restore VPN acquisition logic and improve peer cleanup

- Revert removal of NET_CAPABILITY_NOT_VPN to allow hardware handle acquisition while VPN is active.
- Refactor handlePeerDisconnection to more reliably cleanup initial and routed IDs.
- Switch cleanup logging to debug level to reduce log noise.
This commit is contained in:
aidenvalue
2026-01-03 15:24:59 +01:00
parent ddba841280
commit 2bd8f84e6a
@@ -168,10 +168,10 @@ class WifiAwareMeshService(private val context: Context) {
sock.getOutputStream().write(bytes) sock.getOutputStream().write(bytes)
sent++ sent++
} catch (e: IOException) { } catch (e: IOException) {
Log.e(TAG, "TX: write failed to ${pid.take(8)}: ${e.message}") Log.e(TAG, "TX: write failed to ${pid.take(8)}: ${e.message}")
} }
} }
Log.i(TAG, "TX: broadcast via WiFi Aware to $sent peers (bytes=${bytes.size})") Log.i(TAG, "TX: broadcast via Wi-Fi Aware to $sent peers (bytes=${bytes.size})")
} }
/** /**
@@ -179,12 +179,12 @@ class WifiAwareMeshService(private val context: Context) {
*/ */
private fun broadcastPacket(routed: RoutedPacket) { private fun broadcastPacket(routed: RoutedPacket) {
Log.d(TAG, "TX: packet type=${routed.packet.type} broadcast (ttl=${routed.packet.ttl})") Log.d(TAG, "TX: packet type=${routed.packet.type} broadcast (ttl=${routed.packet.ttl})")
// WiFi Aware uses full packets; no fragmentation // Wi-Fi Aware uses full packets; no fragmentation
val data = routed.packet.toBinaryData() ?: return val data = routed.packet.toBinaryData() ?: return
serviceScope.launch { broadcastRaw(data) } serviceScope.launch { broadcastRaw(data) }
} }
// Expose a public method so BLE can forward relays to WiFi Aware // Expose a public method so BLE can forward relays to Wi-Fi Aware
fun broadcastRoutedPacket(routed: RoutedPacket) { fun broadcastRoutedPacket(routed: RoutedPacket) {
broadcastPacket(routed) broadcastPacket(routed)
} }
@@ -193,19 +193,19 @@ class WifiAwareMeshService(private val context: Context) {
* Send packet to connected peer. * Send packet to connected peer.
*/ */
private fun sendPacketToPeer(peerID: String, packet: BitchatPacket) { private fun sendPacketToPeer(peerID: String, packet: BitchatPacket) {
// WiFi Aware uses full packets; no fragmentation // Wi-Fi Aware uses full packets; no fragmentation
val data = packet.toBinaryData() ?: return val data = packet.toBinaryData() ?: return
serviceScope.launch { serviceScope.launch {
val sock = peerSockets[peerID] val sock = peerSockets[peerID]
if (sock == null) { if (sock == null) {
Log.w(TAG, "TX: no socket for ${peerID.take(8)}") Log.w(TAG, "TX: no socket for ${peerID.take(8)}")
return@launch return@launch
} }
try { try {
sock.getOutputStream().write(data) sock.getOutputStream().write(data)
Log.d(TAG, "TX: packet type=${packet.type} ${peerID.take(8)} (bytes=${data.size})") Log.d(TAG, "TX: packet type=${packet.type} to ${peerID.take(8)} (bytes=${data.size})")
} catch (e: IOException) { } catch (e: IOException) {
Log.e(TAG, "TX: write to ${peerID.take(8)} failed: ${e.message}") Log.e(TAG, "TX: write to ${peerID.take(8)} failed: ${e.message}")
} }
} }
} }
@@ -415,7 +415,7 @@ class WifiAwareMeshService(private val context: Context) {
fun startServices() { fun startServices() {
if (isActive) return if (isActive) return
isActive = true isActive = true
Log.i(TAG, "Starting WiFi Aware mesh with peer ID: $myPeerID") Log.i(TAG, "Starting Wi-Fi Aware mesh with peer ID: $myPeerID")
awareManager?.attach(object : AttachCallback() { awareManager?.attach(object : AttachCallback() {
@SuppressLint("MissingPermission") @SuppressLint("MissingPermission")
@@ -425,7 +425,7 @@ class WifiAwareMeshService(private val context: Context) {
]) ])
override fun onAttached(session: WifiAwareSession) { override fun onAttached(session: WifiAwareSession) {
wifiAwareSession = session wifiAwareSession = session
Log.i(TAG, "WiFi Aware attached; starting publish & subscribe (peerID=$myPeerID)") Log.i(TAG, "Wi-Fi Aware attached; starting publish & subscribe (peerID=$myPeerID)")
// PUBLISH (server role) // PUBLISH (server role)
session.publish( session.publish(
@@ -446,7 +446,7 @@ class WifiAwareMeshService(private val context: Context) {
val peerId = try { String(serviceSpecificInfo) } catch (_: Exception) { "" } val peerId = try { String(serviceSpecificInfo) } catch (_: Exception) { "" }
handleToPeerId[peerHandle] = peerId handleToPeerId[peerHandle] = peerId
if (peerId.isNotBlank()) discoveredTimestamps[peerId] = System.currentTimeMillis() if (peerId.isNotBlank()) discoveredTimestamps[peerId] = System.currentTimeMillis()
Log.d(TAG, "PUBLISH: onServiceDiscovered ssi='${peerId.take(16)}' len=${serviceSpecificInfo.size}") Log.d(TAG, "PUBLISH: onServiceDiscovered ssi='${peerId.take(16)}' len=${serviceSpecificInfo.size}")
} }
@RequiresApi(Build.VERSION_CODES.Q) @RequiresApi(Build.VERSION_CODES.Q)
@@ -487,7 +487,7 @@ class WifiAwareMeshService(private val context: Context) {
val msgId = (System.nanoTime() and 0x7fffffff).toInt() val msgId = (System.nanoTime() and 0x7fffffff).toInt()
subscribeSession?.sendMessage(peerHandle, msgId, myPeerID.toByteArray()) subscribeSession?.sendMessage(peerHandle, msgId, myPeerID.toByteArray())
if (peerId.isNotBlank()) discoveredTimestamps[peerId] = System.currentTimeMillis() if (peerId.isNotBlank()) discoveredTimestamps[peerId] = System.currentTimeMillis()
Log.d(TAG, "SUBSCRIBE: sent ping to '${peerId.take(16)}' (msgId=$msgId)") Log.d(TAG, "SUBSCRIBE: sent ping to '${peerId.take(16)}' (msgId=$msgId)")
} }
@RequiresApi(Build.VERSION_CODES.Q) @RequiresApi(Build.VERSION_CODES.Q)
@@ -499,7 +499,7 @@ class WifiAwareMeshService(private val context: Context) {
val peerId = handleToPeerId[peerHandle] ?: return val peerId = handleToPeerId[peerHandle] ?: return
if (peerId == myPeerID) return if (peerId == myPeerID) return
Log.d(TAG, "SUBSCRIBE: onMessageReceived() → server-ready from ${peerId.take(8)} payload=${message.size}B") Log.d(TAG, "SUBSCRIBE: onMessageReceived() → server-ready from ${peerId.take(8)} payload=${message.size}B")
handleServerReady(peerHandle, message) handleServerReady(peerHandle, message)
} }
}, },
@@ -587,16 +587,22 @@ class WifiAwareMeshService(private val context: Context) {
val ss = ServerSocket(0) val ss = ServerSocket(0)
serverSockets[peerId] = ss serverSockets[peerId] = ss
val port = ss.localPort val port = ss.localPort
Log.d(TAG, "SERVER: listening for ${peerId.take(8)}… on port $port")
// Ensure port is set to reuse if connection was recently closed (TIME_WAIT)
try {
ss.reuseAddress = true
} catch (_: Exception) {}
Log.d(TAG, "SERVER: listening for ${peerId.take(8)} on port $port")
val spec = WifiAwareNetworkSpecifier.Builder(pubSession, peerHandle) val spec = WifiAwareNetworkSpecifier.Builder(pubSession, peerHandle)
.setPskPassphrase(PSK) .setPskPassphrase(PSK)
.setPort(port) .setPort(port)
.build() .build()
// Default capabilities include NET_CAPABILITY_NOT_VPN.
// Keeping defaults for hardware interface handle acquisition compatibility with global VPNs.
val req = NetworkRequest.Builder() val req = NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE) .addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.setNetworkSpecifier(spec) .setNetworkSpecifier(spec)
.build() .build()
@@ -604,9 +610,9 @@ class WifiAwareMeshService(private val context: Context) {
override fun onAvailable(network: Network) { override fun onAvailable(network: Network) {
try { try {
val client = ss.accept() val client = ss.accept()
try { network.bindSocket(client) } catch (e: Exception) { Log.w(TAG, "Server bindSocket EPERM: \${e.message}") } try { network.bindSocket(client) } catch (e: Exception) { Log.w(TAG, "Server bindSocket EPERM: ${e.message}") }
client.keepAlive = true client.keepAlive = true
Log.d(TAG, "SERVER: accepted TCP from \${peerId.take(8)} addr=\${client.inetAddress?.hostAddress}") Log.d(TAG, "SERVER: accepted TCP from ${peerId.take(8)} addr=${client.inetAddress?.hostAddress}")
peerSockets[peerId] = client peerSockets[peerId] = client
try { peerManager.setDirectConnection(peerId, true) } catch (_: Exception) {} try { peerManager.setDirectConnection(peerId, true) } catch (_: Exception) {}
try { peerManager.addOrUpdatePeer(peerId, peerId) } catch (_: Exception) {} try { peerManager.addOrUpdatePeer(peerId, peerId) } catch (_: Exception) {}
@@ -615,22 +621,22 @@ class WifiAwareMeshService(private val context: Context) {
// Kick off Noise handshake for this logical peer // Kick off Noise handshake for this logical peer
if (myPeerID < peerId) { if (myPeerID < peerId) {
messageHandler.delegate?.initiateNoiseHandshake(peerId) messageHandler.delegate?.initiateNoiseHandshake(peerId)
Log.d(TAG, "SERVER: initiating Noise handshake to ${peerId.take(8)} (lower ID)") Log.d(TAG, "SERVER: initiating Noise handshake to ${peerId.take(8)} (lower ID)")
} }
// Ensure fast presence even before handshake settles // Ensure fast presence even before handshake settles
serviceScope.launch { delay(150); sendBroadcastAnnounce() } serviceScope.launch { delay(150); sendBroadcastAnnounce() }
} catch (ioe: IOException) { } catch (ioe: IOException) {
Log.e(TAG, "SERVER: accept failed for ${peerId.take(8)}", ioe) Log.e(TAG, "SERVER: accept failed for ${peerId.take(8)}", ioe)
} }
} }
override fun onLost(network: Network) { override fun onLost(network: Network) {
networkCallbacks.remove(peerId) networkCallbacks.remove(peerId)
Log.d(TAG, "SERVER: network lost for \${peerId.take(8)}") Log.d(TAG, "SERVER: network lost for ${peerId.take(8)}")
} }
} }
networkCallbacks[peerId] = cb networkCallbacks[peerId] = cb
Log.d(TAG, "SERVER: requesting Aware network for ${peerId.take(8)}") Log.d(TAG, "SERVER: requesting Aware network for ${peerId.take(8)}")
cm.requestNetwork(req, cb) cm.requestNetwork(req, cb)
val readyId = (System.nanoTime() and 0x7fffffff).toInt() val readyId = (System.nanoTime() and 0x7fffffff).toInt()
@@ -701,14 +707,13 @@ class WifiAwareMeshService(private val context: Context) {
} }
val port = ByteBuffer.wrap(payload).order(ByteOrder.BIG_ENDIAN).int val port = ByteBuffer.wrap(payload).order(ByteOrder.BIG_ENDIAN).int
Log.d(TAG, "CLIENT: connecting to ${peerId.take(8)} port=$port") Log.d(TAG, "CLIENT: connecting to ${peerId.take(8)} port=$port")
val spec = WifiAwareNetworkSpecifier.Builder(subscribeSession!!, peerHandle) val spec = WifiAwareNetworkSpecifier.Builder(subscribeSession!!, peerHandle)
.setPskPassphrase(PSK) .setPskPassphrase(PSK)
.build() .build()
val req = NetworkRequest.Builder() val req = NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE) .addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.setNetworkSpecifier(spec) .setNetworkSpecifier(spec)
.build() .build()
@@ -726,7 +731,7 @@ class WifiAwareMeshService(private val context: Context) {
try { try {
val sock = Socket() val sock = Socket()
try { network.bindSocket(sock) } catch (e: Exception) { Log.w(TAG, "Client bindSocket EPERM: \${e.message}") } try { network.bindSocket(sock) } catch (e: Exception) { Log.w(TAG, "Client bindSocket EPERM: ${e.message}") }
sock.tcpNoDelay = true sock.tcpNoDelay = true
sock.keepAlive = true sock.keepAlive = true
@@ -742,7 +747,7 @@ class WifiAwareMeshService(private val context: Context) {
} }
sock.connect(java.net.InetSocketAddress(scopedAddr, port), 7000) sock.connect(java.net.InetSocketAddress(scopedAddr, port), 7000)
Log.d(TAG, "CLIENT: TCP connected to ${peerId.take(8)} addr=$scopedAddr:$port (iface=$iface)") Log.d(TAG, "CLIENT: TCP connected to ${peerId.take(8)} addr=$scopedAddr:$port (iface=$iface)")
peerSockets[peerId] = sock peerSockets[peerId] = sock
try { peerManager.setDirectConnection(peerId, true) } catch (_: Exception) {} try { peerManager.setDirectConnection(peerId, true) } catch (_: Exception) {}
@@ -752,22 +757,22 @@ class WifiAwareMeshService(private val context: Context) {
// Kick off Noise handshake for this logical peer // Kick off Noise handshake for this logical peer
if (myPeerID < peerId) { if (myPeerID < peerId) {
messageHandler.delegate?.initiateNoiseHandshake(peerId) messageHandler.delegate?.initiateNoiseHandshake(peerId)
Log.d(TAG, "CLIENT: initiating Noise handshake to ${peerId.take(8)} (lower ID)") Log.d(TAG, "CLIENT: initiating Noise handshake to ${peerId.take(8)} (lower ID)")
} }
// Ensure fast presence even before handshake settles // Ensure fast presence even before handshake settles
serviceScope.launch { delay(150); sendBroadcastAnnounce() } serviceScope.launch { delay(150); sendBroadcastAnnounce() }
} catch (ioe: IOException) { } catch (ioe: IOException) {
Log.e(TAG, "CLIENT: socket connect failed to ${peerId.take(8)}", ioe) Log.e(TAG, "CLIENT: socket connect failed to ${peerId.take(8)}", ioe)
} }
} }
override fun onLost(network: Network) { override fun onLost(network: Network) {
networkCallbacks.remove(peerId) networkCallbacks.remove(peerId)
Log.d(TAG, "CLIENT: network lost for ${peerId.take(8)}") Log.d(TAG, "CLIENT: network lost for ${peerId.take(8)}")
} }
} }
networkCallbacks[peerId] = cb networkCallbacks[peerId] = cb
Log.d(TAG, "CLIENT: requesting Aware network for ${peerId.take(8)}") Log.d(TAG, "CLIENT: requesting Aware network for ${peerId.take(8)}")
cm.requestNetwork(req, cb) cm.requestNetwork(req, cb)
} }
@@ -836,26 +841,33 @@ class WifiAwareMeshService(private val context: Context) {
peerSockets[routedPeerId] = socket peerSockets[routedPeerId] = socket
} }
Log.d(TAG, "RX: packet type=${pkt.type} from ${senderPeerHex.take(8)} (bytes=${raw.size})") Log.d(TAG, "RX: packet type=${pkt.type} from ${senderPeerHex.take(8)} (bytes=${raw.size})")
packetProcessor.processPacket(RoutedPacket(pkt, routedPeerId)) packetProcessor.processPacket(RoutedPacket(pkt, routedPeerId))
} }
// Breaking out of the loop means the socket is dead or service is stopping.
// We MUST notify the mesh layer so it removes the logical peer immediately to allow reconnection.
Log.i(TAG, "Socket loop terminated for ${initialLogicalPeerId.take(8)} removing peer.")
handlePeerDisconnection(initialLogicalPeerId, routedPeerId) handlePeerDisconnection(initialLogicalPeerId, routedPeerId)
socket.closeQuietly() socket.closeQuietly()
} }
/**
* Internal helper to ensure peer is cleaned up from all managers when a socket dies.
*/
private fun handlePeerDisconnection(initialId: String, routedId: String?) { private fun handlePeerDisconnection(initialId: String, routedId: String?) {
serviceScope.launch { serviceScope.launch {
Log.w(TAG, "Cleaning up peer: $initialId / $routedId") Log.d(TAG, "Cleaning up peer: $initialId / $routedId")
val possible = setOfNotNull(initialId, routedId).filter { it.isNotEmpty() && it != myPeerID }
possible.forEach { id -> peerSockets.remove(initialId)?.closeQuietly()
peerSockets.remove(id)?.closeQuietly() serverSockets.remove(initialId)?.closeQuietly()
serverSockets.remove(id)?.closeQuietly() networkCallbacks.remove(initialId)?.let { runCatching { cm.unregisterNetworkCallback(it) } }
networkCallbacks.remove(id)?.let { runCatching { cm.unregisterNetworkCallback(it) } } peerManager.removePeer(initialId)
peerManager.removePeer(id)
routedId?.let { id ->
if (id != initialId) {
peerSockets.remove(id)?.closeQuietly()
serverSockets.remove(id)?.closeQuietly()
networkCallbacks.remove(id)?.let { runCatching { cm.unregisterNetworkCallback(it) } }
peerManager.removePeer(id)
}
} }
} }
} }