This commit is contained in:
callebtc
2025-10-22 04:58:05 +02:00
parent 13d4c5145e
commit 1296e117ba
6 changed files with 79 additions and 32 deletions
@@ -73,6 +73,8 @@ class MainActivity : OrientationAwareActivity() {
permissionManager = PermissionManager(this) permissionManager = PermissionManager(this)
// Initialize core mesh service first // Initialize core mesh service first
meshService = BluetoothMeshService(this) meshService = BluetoothMeshService(this)
// Expose BLE mesh to WiFi Aware controller for cross-transport relays
try { com.bitchat.android.wifiaware.WifiAwareController.setBleMeshService(meshService) } catch (_: Exception) { }
bluetoothStatusManager = BluetoothStatusManager( bluetoothStatusManager = BluetoothStatusManager(
activity = this, activity = this,
context = this, context = this,
@@ -347,6 +349,12 @@ class MainActivity : OrientationAwareActivity() {
bluetoothStatusManager.logBluetoothStatus() bluetoothStatusManager.logBluetoothStatus()
mainViewModel.updateBluetoothStatus(bluetoothStatusManager.checkBluetoothStatus()) mainViewModel.updateBluetoothStatus(bluetoothStatusManager.checkBluetoothStatus())
val bleRequired = try { com.bitchat.android.ui.debug.DebugPreferenceManager.getBleEnabled(true) } catch (_: Exception) { true }
if (!bleRequired) {
// Skip BLE checks entirely when BLE is disabled in debug settings
checkLocationAndProceed()
return
}
when (mainViewModel.bluetoothStatus.value) { when (mainViewModel.bluetoothStatus.value) {
BluetoothStatus.ENABLED -> { BluetoothStatus.ENABLED -> {
// Bluetooth is enabled, check location services next // Bluetooth is enabled, check location services next
@@ -513,8 +521,9 @@ class MainActivity : OrientationAwareActivity() {
else -> BatteryOptimizationStatus.ENABLED else -> BatteryOptimizationStatus.ENABLED
} }
val bleRequired2 = try { com.bitchat.android.ui.debug.DebugPreferenceManager.getBleEnabled(true) } catch (_: Exception) { true }
when { when {
currentBluetoothStatus != BluetoothStatus.ENABLED -> { bleRequired2 && currentBluetoothStatus != BluetoothStatus.ENABLED -> {
// Bluetooth still disabled, but now we have permissions to enable it // Bluetooth still disabled, but now we have permissions to enable it
Log.d("MainActivity", "Permissions granted, but Bluetooth still disabled. Showing Bluetooth enable screen.") Log.d("MainActivity", "Permissions granted, but Bluetooth still disabled. Showing Bluetooth enable screen.")
mainViewModel.updateBluetoothStatus(currentBluetoothStatus) mainViewModel.updateBluetoothStatus(currentBluetoothStatus)
@@ -249,11 +249,16 @@ class BluetoothMeshService(private val context: Context) {
override fun sendPacket(packet: BitchatPacket) { override fun sendPacket(packet: BitchatPacket) {
// Sign the packet before broadcasting // Sign the packet before broadcasting
val signedPacket = signPacketBeforeBroadcast(packet) val signedPacket = signPacketBeforeBroadcast(packet)
connectionManager.broadcastPacket(RoutedPacket(signedPacket)) val routed = RoutedPacket(signedPacket)
connectionManager.broadcastPacket(routed)
// Cross-transport relay to WiFi Aware if available
try { com.bitchat.android.wifiaware.WifiAwareController.getService()?.broadcastRoutedPacket(routed) } catch (_: Exception) { }
} }
override fun relayPacket(routed: RoutedPacket) { override fun relayPacket(routed: RoutedPacket) {
connectionManager.broadcastPacket(routed) connectionManager.broadcastPacket(routed)
// Cross-transport relay to WiFi Aware if available
try { com.bitchat.android.wifiaware.WifiAwareController.getService()?.broadcastRoutedPacket(routed) } catch (_: Exception) { }
} }
override fun getBroadcastRecipient(): ByteArray { override fun getBroadcastRecipient(): ByteArray {
@@ -210,6 +210,7 @@ class MediaSendingManager(
Log.d(TAG, "📤 Calling meshService.sendFilePrivate to $toPeerID") Log.d(TAG, "📤 Calling meshService.sendFilePrivate to $toPeerID")
meshService.sendFilePrivate(toPeerID, filePacket) meshService.sendFilePrivate(toPeerID, filePacket)
try { com.bitchat.android.wifiaware.WifiAwareController.getService()?.sendFilePrivate(toPeerID, filePacket) } catch (_: Exception) {}
Log.d(TAG, "✅ File send completed successfully") Log.d(TAG, "✅ File send completed successfully")
} }
@@ -264,6 +265,7 @@ class MediaSendingManager(
Log.d(TAG, "📤 Calling meshService.sendFileBroadcast") Log.d(TAG, "📤 Calling meshService.sendFileBroadcast")
meshService.sendFileBroadcast(filePacket) meshService.sendFileBroadcast(filePacket)
try { com.bitchat.android.wifiaware.WifiAwareController.getService()?.sendFileBroadcast(filePacket) } catch (_: Exception) {}
Log.d(TAG, "✅ File broadcast completed successfully") Log.d(TAG, "✅ File broadcast completed successfully")
} }
@@ -576,16 +576,21 @@ private fun PeerItem(
tint = Color.Gray tint = Color.Gray
) )
} else { } else {
val wifiAwarePeers by com.bitchat.android.wifiaware.WifiAwareController.connectedPeers.collectAsState() val awareConnected by com.bitchat.android.wifiaware.WifiAwareController.connectedPeers.collectAsState()
val isWifiAware = wifiAwarePeers.containsKey(peerID) val awareDiscovered by com.bitchat.android.wifiaware.WifiAwareController.discoveredPeers.collectAsState()
val isWifiDirect = awareConnected.containsKey(peerID)
val isBleDirect = isDirect
val icon = when { val icon = when {
isWifiAware -> Icons.Filled.Wifi isWifiDirect -> Icons.Filled.Wifi
isDirect -> Icons.Outlined.SettingsInputAntenna isBleDirect -> Icons.Outlined.SettingsInputAntenna
// Routed: show Route icon; optionally prefer WiFi Aware if discovered there
awareDiscovered.contains(peerID) -> Icons.Filled.WifiTethering
else -> Icons.Filled.Route else -> Icons.Filled.Route
} }
val cd = when { val cd = when {
isWifiAware -> "Direct WiFi Aware" isWifiDirect -> "Direct WiFi Aware"
isDirect -> "Direct Bluetooth" isBleDirect -> "Direct Bluetooth"
awareDiscovered.contains(peerID) -> "Routed over WiFi"
else -> "Routed" else -> "Routed"
} }
Icon( Icon(
@@ -51,7 +51,7 @@ object WifiAwareController {
try { try {
val s = service val s = service
if (s != null) { if (s != null) {
_connectedPeers.value = s.getDeviceAddressToPeerMapping().entries.associate { (ip, peer) -> peer to ip } _connectedPeers.value = s.getDeviceAddressToPeerMapping() // peerID -> ip
_knownPeers.value = s.getPeerNicknames() _knownPeers.value = s.getPeerNicknames()
_discoveredPeers.value = s.getDiscoveredPeerIds() _discoveredPeers.value = s.getDiscoveredPeerIds()
} else { } else {
@@ -108,4 +108,9 @@ object WifiAwareController {
} }
fun getService(): WifiAwareMeshService? = service fun getService(): WifiAwareMeshService? = service
// Optional bridge to BLE mesh for cross-transport relaying
@Volatile private var bleMesh: com.bitchat.android.mesh.BluetoothMeshService? = null
fun setBleMeshService(svc: com.bitchat.android.mesh.BluetoothMeshService) { bleMesh = svc }
fun getBleMeshService(): com.bitchat.android.mesh.BluetoothMeshService? = bleMesh
} }
@@ -172,24 +172,39 @@ class WifiAwareMeshService(private val context: Context) {
* Broadcasts routed packet to currently connected peers. * Broadcasts routed packet to currently connected peers.
*/ */
private fun broadcastPacket(routed: RoutedPacket) { private fun broadcastPacket(routed: RoutedPacket) {
routed.packet.toBinaryData()?.let { routed.packet.toBinaryData()?.let { data ->
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})")
broadcastRaw(it) serviceScope.launch { broadcastRaw(data) }
// Cross-transport relay (optional): also broadcast via BLE if available
try { com.bitchat.android.wifiaware.WifiAwareController.getBleMeshService()?.connectionManager?.broadcastPacket(routed) } catch (_: Exception) { }
} }
} }
// Expose a public method so BLE can forward relays to WiFi Aware
fun broadcastRoutedPacket(routed: RoutedPacket) {
broadcastPacket(routed)
}
/** /**
* Send packet to connected peer. * Send packet to connected peer.
*/ */
private fun sendPacketToPeer(peerID: String, packet: BitchatPacket) { private fun sendPacketToPeer(peerID: String, packet: BitchatPacket) {
val sock = peerSockets[peerID] ?: return val data = packet.toBinaryData() ?: return
try { serviceScope.launch {
val data = packet.toBinaryData() ?: return val sock = peerSockets[peerID]
sock.getOutputStream().write(data) if (sock == null) {
Log.d(TAG, "TX: packet type=${packet.type}${peerID.take(8)}… (bytes=${data.size})") Log.w(TAG, "TX: no socket for ${peerID.take(8)}")
} catch (e: IOException) { return@launch
Log.e(TAG, "TX: write to ${peerID.take(8)}… failed: ${e.message}") }
try {
sock.getOutputStream().write(data)
Log.d(TAG, "TX: packet type=${packet.type}${peerID.take(8)}… (bytes=${data.size})")
} catch (e: IOException) {
Log.e(TAG, "TX: write to ${peerID.take(8)}… failed: ${e.message}")
}
} }
// Cross-transport relay to BLE when unicast target is connected there too
try { com.bitchat.android.wifiaware.WifiAwareController.getBleMeshService()?.connectionManager?.sendPacketToPeer(peerID, packet) } catch (_: Exception) { }
} }
/** /**
@@ -279,17 +294,19 @@ class WifiAwareMeshService(private val context: Context) {
override fun hasNoiseSession(peerID: String) = override fun hasNoiseSession(peerID: String) =
encryptionService.hasEstablishedSession(peerID) encryptionService.hasEstablishedSession(peerID)
override fun initiateNoiseHandshake(peerID: String) { override fun initiateNoiseHandshake(peerID: String) {
val hs = encryptionService.initiateHandshake(peerID) ?: return serviceScope.launch {
val packet = BitchatPacket( val hs = encryptionService.initiateHandshake(peerID) ?: return@launch
version = 1u, val packet = BitchatPacket(
type = MessageType.NOISE_HANDSHAKE.value, version = 1u,
senderID = hexStringToByteArray(myPeerID), type = MessageType.NOISE_HANDSHAKE.value,
recipientID = hexStringToByteArray(peerID), senderID = hexStringToByteArray(myPeerID),
timestamp = System.currentTimeMillis().toULong(), recipientID = hexStringToByteArray(peerID),
payload = hs, timestamp = System.currentTimeMillis().toULong(),
ttl = MAX_TTL payload = hs,
) ttl = MAX_TTL
broadcastPacket(RoutedPacket(signPacketBeforeBroadcast(packet))) )
broadcastPacket(RoutedPacket(signPacketBeforeBroadcast(packet)))
}
} }
override fun processNoiseHandshakeMessage(payload: ByteArray, peerID: String): ByteArray? = override fun processNoiseHandshakeMessage(payload: ByteArray, peerID: String): ByteArray? =
try { encryptionService.processHandshakeMessage(payload, peerID) } catch (_: Exception) { null } try { encryptionService.processHandshakeMessage(payload, peerID) } catch (_: Exception) { null }
@@ -593,8 +610,10 @@ 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)}") Log.d(TAG, "SERVER: initiating Noise handshake to ${peerId.take(8)} (lower ID)")
} }
// Ensure fast presence even before handshake settles
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)
} }
@@ -707,10 +726,12 @@ class WifiAwareMeshService(private val context: Context) {
listenerExec.execute { listenToPeer(sock, peerId) } listenerExec.execute { listenToPeer(sock, peerId) }
handleServerKeepAlive(sock, peerId, peerHandle) handleServerKeepAlive(sock, peerId, peerHandle)
// 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)}") Log.d(TAG, "CLIENT: initiating Noise handshake to ${peerId.take(8)} (lower ID)")
} }
// Ensure fast presence even before handshake settles
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)
} }