Resolve merge conflicts:

- BluetoothConnectionManager.kt: keep main version, rename sendToPeer -> sendPacketToPeer and add overload; remove conflict markers
- BluetoothMeshService.kt: keep main, ensure delegate uses sendPacketToPeer
- BinaryProtocol.kt: keep main version with v2 payload length support

Refactor: rename all sendToPeer usages to sendPacketToPeer across codebase
- PacketRelayManager + interface
- PacketProcessor + interface
- BluetoothPacketBroadcaster targeted send method remains sendPacketToPeer
- Update tests accordingly

Build fixes
This commit is contained in:
callebtc
2025-09-20 01:23:21 +02:00
95 changed files with 8004 additions and 1680 deletions
@@ -15,7 +15,9 @@ enum class MessageType(val value: UByte) {
LEAVE(0x03u),
NOISE_HANDSHAKE(0x10u), // Noise handshake
NOISE_ENCRYPTED(0x11u), // Noise encrypted transport message
FRAGMENT(0x20u); // Fragmentation for large packets
FRAGMENT(0x20u), // Fragmentation for large packets
REQUEST_SYNC(0x21u), // GCS-based sync request
FILE_TRANSFER(0x22u); // New: File transfer packet (BLE voice notes, etc.)
companion object {
fun fromValue(value: UByte): MessageType? {
@@ -32,15 +34,15 @@ object SpecialRecipients {
}
/**
* Binary packet format - 100% compatible with iOS version
*
* Header (Fixed 13 bytes):
* Binary packet format - 100% backward compatible with iOS version
*
* Header (13 bytes for v1, 15 bytes for v2):
* - Version: 1 byte
* - Type: 1 byte
* - Type: 1 byte
* - TTL: 1 byte
* - Timestamp: 8 bytes (UInt64, big-endian)
* - Flags: 1 byte (bit 0: hasRecipient, bit 1: hasSignature, bit 2: isCompressed)
* - PayloadLength: 2 bytes (UInt16, big-endian)
* - PayloadLength: 2 bytes (v1) / 4 bytes (v2) (big-endian)
*
* Variable sections:
* - SenderID: 8 bytes (fixed)
@@ -173,20 +175,28 @@ data class BitchatPacket(
}
/**
* Binary Protocol implementation - exact same format as iOS version
* Binary Protocol implementation - supports v1 and v2, backward compatible
*/
object BinaryProtocol {
private const val HEADER_SIZE = 13
private const val HEADER_SIZE_V1 = 13
private const val HEADER_SIZE_V2 = 15
private const val SENDER_ID_SIZE = 8
private const val RECIPIENT_ID_SIZE = 8
private const val SIGNATURE_SIZE = 64
object Flags {
const val HAS_RECIPIENT: UByte = 0x01u
const val HAS_SIGNATURE: UByte = 0x02u
const val IS_COMPRESSED: UByte = 0x04u
const val HAS_ROUTE: UByte = 0x08u
}
private fun getHeaderSize(version: UByte): Int {
return when (version) {
1u.toUByte() -> HEADER_SIZE_V1
else -> HEADER_SIZE_V2 // v2+ will use 4-byte payload length
}
}
fun encode(packet: BitchatPacket): ByteArray? {
try {
@@ -203,7 +213,13 @@ object BinaryProtocol {
}
}
val buffer = ByteBuffer.allocate(4096).apply { order(ByteOrder.BIG_ENDIAN) }
// Compute a safe capacity for the unpadded frame
val headerSize = getHeaderSize(packet.version)
val recipientBytes = if (packet.recipientID != null) RECIPIENT_ID_SIZE else 0
val signatureBytes = if (packet.signature != null) SIGNATURE_SIZE else 0
val payloadBytes = payload.size + if (isCompressed) 2 else 0
val capacity = headerSize + SENDER_ID_SIZE + recipientBytes + payloadBytes + signatureBytes + 16 // small slack
val buffer = ByteBuffer.allocate(capacity.coerceAtLeast(512)).apply { order(ByteOrder.BIG_ENDIAN) }
// Header
buffer.put(packet.version.toByte())
@@ -229,9 +245,13 @@ object BinaryProtocol {
}
buffer.put(flags.toByte())
// Payload length (2 bytes, big-endian) - includes original size if compressed
// Payload length (2 or 4 bytes, big-endian) - includes original size if compressed
val payloadDataSize = payload.size + if (isCompressed) 2 else 0
buffer.putShort(payloadDataSize.toShort())
if (packet.version >= 2u.toUByte()) {
buffer.putInt(payloadDataSize) // 4 bytes for v2+
} else {
buffer.putShort(payloadDataSize.toShort()) // 2 bytes for v1
}
// SenderID (exactly 8 bytes)
val senderBytes = packet.senderID.take(SENDER_ID_SIZE).toByteArray()
@@ -282,7 +302,7 @@ object BinaryProtocol {
return paddedData
} catch (e: Exception) {
Log.e("BinaryProtocol", "Error encoding packet type ${packet.type}: ${e.message}")
Log.e("BinaryProtocol", "Error encoding packet type ${'$'}{packet.type}: ${'$'}{e.message}")
return null
}
}
@@ -303,20 +323,22 @@ object BinaryProtocol {
*/
private fun decodeCore(raw: ByteArray): BitchatPacket? {
try {
if (raw.size < HEADER_SIZE + SENDER_ID_SIZE) return null
if (raw.size < HEADER_SIZE_V1 + SENDER_ID_SIZE) return null
val buffer = ByteBuffer.wrap(raw).apply { order(ByteOrder.BIG_ENDIAN) }
// Header
val version = buffer.get().toUByte()
if (version != 1u.toUByte()) return null
if (version.toUInt() != 1u && version.toUInt() != 2u) return null // Support v1 and v2
val headerSize = getHeaderSize(version)
val type = buffer.get().toUByte()
val ttl = buffer.get().toUByte()
// Timestamp
val timestamp = buffer.getLong().toULong()
// Flags
val flags = buffer.get().toUByte()
val hasRecipient = (flags and Flags.HAS_RECIPIENT) != 0u.toUByte()
@@ -324,11 +346,15 @@ object BinaryProtocol {
val isCompressed = (flags and Flags.IS_COMPRESSED) != 0u.toUByte()
val hasRoute = (flags and Flags.HAS_ROUTE) != 0u.toUByte()
// Payload length
val payloadLength = buffer.getShort().toUShort()
// Payload length - version-dependent (2 or 4 bytes)
val payloadLength = if (version >= 2u.toUByte()) {
buffer.getInt().toUInt() // 4 bytes for v2+
} else {
buffer.getShort().toUShort().toUInt() // 2 bytes for v1, convert to UInt
}
// Calculate expected total size
var expectedSize = HEADER_SIZE + SENDER_ID_SIZE + payloadLength.toInt()
var expectedSize = headerSize + SENDER_ID_SIZE + payloadLength.toInt()
if (hasRecipient) expectedSize += RECIPIENT_ID_SIZE
var routeCount = 0
if (hasRoute) {
@@ -340,7 +366,7 @@ object BinaryProtocol {
expectedSize += 1 + (routeCount * SENDER_ID_SIZE)
}
if (hasSignature) expectedSize += SIGNATURE_SIZE
if (raw.size < expectedSize) return null
// SenderID
@@ -404,7 +430,7 @@ object BinaryProtocol {
)
} catch (e: Exception) {
Log.e("BinaryProtocol", "Error decoding packet: ${e.message}")
Log.e("BinaryProtocol", "Error decoding packet: ${'$'}{e.message}")
return null
}
}