mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-25 05:25:20 +00:00
Fix delivery acks and read receipts (#186)
* delivery ack and read receipt * handle deliveryack * fix uppercase id * fix read receipts too
This commit is contained in:
@@ -66,7 +66,7 @@ class BluetoothMeshService(private val context: Context) {
|
||||
// Wire up PacketProcessor reference for recursive handling in MessageHandler
|
||||
messageHandler.packetProcessor = packetProcessor
|
||||
sendPeriodicBroadcastAnnounce()
|
||||
startPeriodicDebugLogging()
|
||||
// startPeriodicDebugLogging()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,13 +89,13 @@ class BluetoothMeshService(private val context: Context) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Send broadcast announcement every 10 seconds
|
||||
* Send broadcast announcement every 30 seconds
|
||||
*/
|
||||
private fun sendPeriodicBroadcastAnnounce() {
|
||||
serviceScope.launch {
|
||||
while (isActive) {
|
||||
try {
|
||||
delay(10000) // 10 seconds
|
||||
delay(30000) // 30 seconds
|
||||
sendBroadcastAnnounce()
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error in periodic broadcast announce: ${e.message}")
|
||||
@@ -356,9 +356,9 @@ class BluetoothMeshService(private val context: Context) {
|
||||
return fragmentManager.handleFragment(packet)
|
||||
}
|
||||
|
||||
override fun handleDeliveryAck(routed: RoutedPacket) {
|
||||
serviceScope.launch { messageHandler.handleDeliveryAck(routed) }
|
||||
}
|
||||
// override fun handleDeliveryAck(routed: RoutedPacket) {
|
||||
// serviceScope.launch { messageHandler.handleDeliveryAck(routed) }
|
||||
// }
|
||||
|
||||
override fun handleReadReceipt(routed: RoutedPacket) {
|
||||
serviceScope.launch { messageHandler.handleReadReceipt(routed) }
|
||||
@@ -552,7 +552,7 @@ class BluetoothMeshService(private val context: Context) {
|
||||
originalMessageID = message.id,
|
||||
recipientID = myPeerID,
|
||||
recipientNickname = nickname,
|
||||
hopCount = 0u // Will be calculated during relay
|
||||
hopCount = 0u.toUByte() // Will be calculated during relay
|
||||
)
|
||||
|
||||
try {
|
||||
@@ -586,6 +586,47 @@ class BluetoothMeshService(private val context: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send read receipt for a received private message
|
||||
*/
|
||||
fun sendReadReceipt(messageID: String, recipientPeerID: String, readerNickname: String) {
|
||||
serviceScope.launch {
|
||||
try {
|
||||
Log.d(TAG, "Sending read receipt for message $messageID to $recipientPeerID")
|
||||
|
||||
// Create the read receipt
|
||||
val receipt = ReadReceipt(
|
||||
originalMessageID = messageID,
|
||||
readerID = myPeerID,
|
||||
readerNickname = readerNickname
|
||||
)
|
||||
|
||||
// Encode the receipt
|
||||
val receiptData = receipt.encode()
|
||||
|
||||
// Create inner read receipt packet
|
||||
val innerPacket = BitchatPacket(
|
||||
version = 1u,
|
||||
type = MessageType.READ_RECEIPT.value,
|
||||
senderID = hexStringToByteArray(myPeerID),
|
||||
recipientID = hexStringToByteArray(recipientPeerID),
|
||||
timestamp = System.currentTimeMillis().toULong(),
|
||||
payload = receiptData,
|
||||
signature = null,
|
||||
ttl = 3u
|
||||
)
|
||||
|
||||
// Encrypt the entire inner packet and send as NOISE_ENCRYPTED
|
||||
encryptAndBroadcastNoisePacket(innerPacket, recipientPeerID)
|
||||
|
||||
Log.d(TAG, "Sent read receipt for message $messageID to $recipientPeerID")
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to send read receipt for message $messageID: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt a BitchatPacket and broadcast it as a NOISE_ENCRYPTED message
|
||||
* This is the correct protocol implementation - encrypt the entire packet, not just the payload
|
||||
@@ -644,14 +685,6 @@ class BluetoothMeshService(private val context: Context) {
|
||||
payload = nickname.toByteArray()
|
||||
)
|
||||
|
||||
// Send multiple times for reliability
|
||||
delay(Random.nextLong(0, 500))
|
||||
connectionManager.broadcastPacket(RoutedPacket(announcePacket))
|
||||
|
||||
delay(500 + Random.nextLong(0, 500))
|
||||
connectionManager.broadcastPacket(RoutedPacket(announcePacket))
|
||||
|
||||
delay(1000 + Random.nextLong(0, 500))
|
||||
connectionManager.broadcastPacket(RoutedPacket(announcePacket))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,19 +58,12 @@ class MessageHandler(private val myPeerID: String) {
|
||||
|
||||
// Check if this is a delivery ACK with the new format
|
||||
if (typeMarker == MessageType.DELIVERY_ACK.value) {
|
||||
// Extract the ACK JSON data (skip the type marker)
|
||||
val ackData = decryptedData.sliceArray(1 until decryptedData.size)
|
||||
|
||||
// Decode the delivery ACK
|
||||
val ack = DeliveryAck.decode(ackData)
|
||||
if (ack != null) {
|
||||
delegate?.onDeliveryAckReceived(ack)
|
||||
Log.d(TAG, "Processed delivery ACK from $peerID")
|
||||
return
|
||||
}
|
||||
handleDeliveryAck(decryptedData)
|
||||
Log.d(TAG, "Processed delivery ACK from $peerID")
|
||||
}
|
||||
|
||||
// Check for read receipt with type marker
|
||||
// NOTE: THIS DOESN'T WORK WITH IOS, IT SENDS AN INNER PACKET INSTEAD
|
||||
if (typeMarker == MessageType.READ_RECEIPT.value) {
|
||||
val receiptData = decryptedData.sliceArray(1 until decryptedData.size)
|
||||
val receipt = ReadReceipt.decode(receiptData)
|
||||
@@ -268,7 +261,7 @@ class MessageHandler(private val myPeerID: String) {
|
||||
delegate?.onMessageReceived(message)
|
||||
|
||||
// Send delivery ACK
|
||||
// delegate?.sendDeliveryAck(message, peerID)
|
||||
delegate?.sendDeliveryAck(message, peerID)
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
@@ -302,23 +295,13 @@ class MessageHandler(private val myPeerID: String) {
|
||||
/**
|
||||
* Handle delivery acknowledgment
|
||||
*/
|
||||
suspend fun handleDeliveryAck(routed: RoutedPacket) {
|
||||
val packet = routed.packet
|
||||
val peerID = routed.peerID ?: "unknown"
|
||||
if (packet.recipientID != null && String(packet.recipientID).replace("\u0000", "") == myPeerID) {
|
||||
try {
|
||||
val decryptedData = delegate?.decryptFromPeer(packet.payload, peerID)
|
||||
if (decryptedData != null) {
|
||||
val ack = DeliveryAck.decode(decryptedData)
|
||||
if (ack != null) {
|
||||
delegate?.onDeliveryAckReceived(ack)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to decrypt delivery ACK: ${e.message}")
|
||||
}
|
||||
suspend fun handleDeliveryAck(decryptedData: ByteArray) {
|
||||
val ackData = decryptedData.sliceArray(1 until decryptedData.size)
|
||||
val ack = DeliveryAck.decode(ackData)
|
||||
if (ack != null) {
|
||||
delegate?.onDeliveryAckReceived(ack)
|
||||
}
|
||||
// Delivery ACK relay is now handled by centralized PacketRelayManager
|
||||
return
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -327,20 +310,11 @@ class MessageHandler(private val myPeerID: String) {
|
||||
suspend fun handleReadReceipt(routed: RoutedPacket) {
|
||||
val packet = routed.packet
|
||||
val peerID = routed.peerID ?: "unknown"
|
||||
if (packet.recipientID != null && String(packet.recipientID).replace("\u0000", "") == myPeerID) {
|
||||
try {
|
||||
val decryptedData = delegate?.decryptFromPeer(packet.payload, peerID)
|
||||
if (decryptedData != null) {
|
||||
val receipt = ReadReceipt.decode(decryptedData)
|
||||
if (receipt != null) {
|
||||
delegate?.onReadReceiptReceived(receipt)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to decrypt read receipt: ${e.message}")
|
||||
}
|
||||
val receipt = ReadReceipt.decode(routed.packet.payload)
|
||||
if (receipt != null) {
|
||||
delegate?.onReadReceiptReceived(receipt)
|
||||
}
|
||||
// Read receipt relay is now handled by centralized PacketRelayManager
|
||||
return
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -139,7 +139,7 @@ class PacketProcessor(private val myPeerID: String) {
|
||||
MessageType.NOISE_HANDSHAKE_INIT -> handleNoiseHandshake(routed, 1)
|
||||
MessageType.NOISE_HANDSHAKE_RESP -> handleNoiseHandshake(routed, 2)
|
||||
MessageType.NOISE_ENCRYPTED -> handleNoiseEncrypted(routed)
|
||||
MessageType.DELIVERY_ACK -> handleDeliveryAck(routed)
|
||||
//MessageType.DELIVERY_ACK -> handleDeliveryAck(routed) // custom packet type...
|
||||
MessageType.READ_RECEIPT -> handleReadReceipt(routed)
|
||||
else -> {
|
||||
validPacket = false
|
||||
@@ -234,11 +234,11 @@ class PacketProcessor(private val myPeerID: String) {
|
||||
/**
|
||||
* Handle delivery acknowledgment
|
||||
*/
|
||||
private suspend fun handleDeliveryAck(routed: RoutedPacket) {
|
||||
val peerID = routed.peerID ?: "unknown"
|
||||
Log.d(TAG, "Processing delivery ACK from ${formatPeerForLog(peerID)}")
|
||||
delegate?.handleDeliveryAck(routed)
|
||||
}
|
||||
// private suspend fun handleDeliveryAck(routed: RoutedPacket) {
|
||||
// val peerID = routed.peerID ?: "unknown"
|
||||
// Log.d(TAG, "Processing delivery ACK from ${formatPeerForLog(peerID)}")
|
||||
// delegate?.handleDeliveryAck(routed)
|
||||
// }
|
||||
|
||||
/**
|
||||
* Handle read receipt
|
||||
@@ -313,7 +313,7 @@ interface PacketProcessorDelegate {
|
||||
fun handleMessage(routed: RoutedPacket)
|
||||
fun handleLeave(routed: RoutedPacket)
|
||||
fun handleFragment(packet: BitchatPacket): BitchatPacket?
|
||||
fun handleDeliveryAck(routed: RoutedPacket)
|
||||
// fun handleDeliveryAck(routed: RoutedPacket)
|
||||
fun handleReadReceipt(routed: RoutedPacket)
|
||||
|
||||
// Communication
|
||||
|
||||
Reference in New Issue
Block a user