mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-26 22:45:21 +00:00
werk
This commit is contained in:
@@ -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 Wi‑Fi 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 Wi‑Fi 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 Wi‑Fi 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 Wi‑Fi 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 Wi‑Fi Aware"
|
isWifiDirect -> "Direct Wi‑Fi Aware"
|
||||||
isDirect -> "Direct Bluetooth"
|
isBleDirect -> "Direct Bluetooth"
|
||||||
|
awareDiscovered.contains(peerID) -> "Routed over Wi‑Fi"
|
||||||
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 Wi‑Fi 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)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user