# How Bitchat Android Reassembles Multi-Packet Messages: A Complete Technical Guide

> Discover how Bitchat Android reassembles multi-packet messages by buffering fragments, verifying metadata, and reconstructing payloads. Get the complete technical guide.

- Repository: [permissionlesstech/bitchat-android](https://github.com/permissionlesstech/bitchat-android)
- Tags: deep-dive
- Published: 2026-08-04

---

**Bitchat Android uses a `FragmentManager` class to split oversized packets into smaller fragments on the sending side and reassemble them on the receiving side by buffering fragments in memory, verifying metadata consistency, and reconstructing the original payload once all fragments arrive.**

Multi-packet message reassembly is essential for any mesh messaging app that exchanges data larger than the Bluetooth MTU limit. In the [permissionlesstech/bitchat-android](https://github.com/permissionlesstech/bitchat-android) repository, this capability is implemented through a fragmentation system that mirrors the iOS version for cross-platform compatibility. This article breaks down exactly how fragmented messages are reassembled in Bitchat Android, from outbound splitting to inbound reconstruction and cleanup.

## Outbound Fragmentation in Bitchat Android

When a message exceeds the **MTU threshold** (approximately 512 bytes), Bitchat Android automatically fragments it before transmission.

### The FragmentingPacketSender Entry Point

`MeshCore` creates a `FragmentingPacketSender` (located in [[`FragmentingPacketSender.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/FragmentingPacketSender.kt)](https://github.com/permissionlesstech/bitchat-android/blob/main/app/src/main/java/com/bitchat/android/mesh/FragmentingPacketSender.kt)) to handle large packets. The sender checks against `AppConstants.Fragmentation.FRAGMENT_SIZE_THRESHOLD` and invokes `fragmentManager.createFragments(packet, maxFragments)`.

The `FragmentManager` instance lives in `MeshCore` and is shared across the mesh layer.

### Creating Fragment Payloads

`FragmentManager.createFragments` performs three critical operations:

1. Generates an **8-byte random fragment ID** to identify the fragment set
2. Splits the unpadded payload into chunks that fit within MTU-overhead limits
3. Wraps each chunk in a `FragmentPayload` (defined in [[`FragmentPayload.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/FragmentPayload.kt)](https://github.com/permissionlesstech/bitchat-android/blob/main/app/src/main/java/com/bitchat/android/model/FragmentPayload.kt))

Each resulting `BitchatPacket` has:
- `type = MessageType.FRAGMENT`
- `payload = FragmentPayload.encode()`
- Original route and TTL preserved

The `FragmentingPacketSender` then transmits these fragments sequentially, with optional delays between packets.

## Inbound Reassembly: The Core Process

All incoming packets flow through `PacketProcessor.processPacket` → `PacketProcessor.handleFragment` in [[`PacketProcessor.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/PacketProcessor.kt)](https://github.com/permissionlesstech/bitchat-android/blob/main/app/src/main/java/com/bitchat/android/mesh/PacketProcessor.kt). Fragment packets are delegated back to `MeshCore`:

```kotlin
override fun handleFragment(packet: BitchatPacket): BitchatPacket? {
    return fragmentManager.handleFragment(packet)
}

```

### FragmentManager.handleFragment Implementation

The reassembly routine in [[`FragmentManager.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/FragmentManager.kt)](https://github.com/permissionlesstech/bitchat-android/blob/main/app/src/main/java/com/bitchat/android/mesh/FragmentManager.kt) (lines 70-140) executes the following steps:

**Validity checks** — Verifies payload contains at least the 13-byte `FragmentPayload` header.

**Decoding** — `FragmentPayload.decode(packet.payload)` extracts `(fragmentID, index, total, originalType, data)`.

**Metadata verification** — Confirms all fragments in a set share identical `originalType` and `total` values.

**Fragment storage** — Stores fragments in `incomingFragments[fragmentID]` as a map of `index → data`. Two safeguards prevent memory exhaustion:
- `MAX_FRAGMENT_TOTAL_BYTES` — per-set byte limit
- `MAX_GLOBAL_FRAGMENT_TOTAL_BYTES` — global buffer cap

**Completion detection** — When `fragmentMap.size == total`, reassembly proceeds:

```kotlin
val reassembledData = mutableListOf<Byte>()
for (i in 0 until total) {
    fragmentMap[i]?.let { reassembledData.addAll(it.asIterable()) }
}

```

**Packet reconstruction** — `BitchatPacket.fromBinaryData(reassembledData.toByteArray())` rebuilds the original. TTL is forced to zero (`copy(ttl = 0u)`) to prevent replay attacks.

**Cleanup** — The completed fragment set is immediately removed from internal maps.

The reassembled packet returns to `PacketProcessor.handleFragment`, which reinjects it into normal processing via `handleReceivedPacket`. From there, it routes to `handleMessage`, `handleAnnounce`, or other appropriate handlers.

## Reassembly Safeguards and Housekeeping

Bitchat Android implements multiple protective mechanisms to ensure reliable multi-packet message reassembly:

**Timeout cleanup** — A coroutine in `FragmentManager` periodically invokes `cleanupOldFragments`, removing sets older than `FRAGMENT_TIMEOUT` (30 seconds).

**Active-set limits** — `MAX_ACTIVE_FRAGMENT_SETS` caps concurrent fragment collections, while `MAX_GLOBAL_FRAGMENT_TOTAL_BYTES` bounds total buffered memory.

These limits make fragmentation deterministic and safe across Android and iOS peers.

## Practical Code Examples

### Sending Large Messages (Automatic Fragmentation)

```kotlin
// Inside a component with MeshCore instance `mesh`
val bigText = "A".repeat(2000)      // Exceeds 512-byte MTU
mesh.sendMessage(content = bigText) // Automatically fragmented

```

Behind the scenes, `FragmentingPacketSender` calls `fragmentManager.createFragments` and emits multiple `MessageType.FRAGMENT` packets.

### Manual Fragmentation and Reassembly

```kotlin
val fm = FragmentManager()
val original = BitchatPacket(
    version = 1u,
    type = MessageType.MESSAGE.value,
    senderID = MeshPacketUtils.hexStringToByteArray("deadbeef"),
    recipientID = SpecialRecipients.BROADCAST,
    timestamp = System.currentTimeMillis().toULong(),
    payload = ByteArray(1500) { it.toByte() },
    ttl = 5u
)

// 1. Create fragments
val fragments: List<BitchatPacket> = fm.createFragments(original)

// 2. Process fragments in any order
fragments.shuffled().forEach { fm.handleFragment(it) }

// 3. Final call returns reassembled packet

```

The repository's [[`FragmentManagerTest.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/FragmentManagerTest.kt)](https://github.com/permissionlesstech/bitchat-android/blob/main/app/src/test/java/com/bitchat/android/mesh/FragmentManagerTest.kt) validates this exact flow.

### Debugging Reassembly

Enable debug logging in `FragmentManager.handleFragment`. Successful reassembly produces:

```

D/FragmentManager: Reassembled packet type=0x01, payloadSize=1500

```

## Key Source Files for Multi-Packet Reassembly

| Component | Purpose | Location |
|-----------|---------|----------|
| [`FragmentManager.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/FragmentManager.kt) | Core fragmentation and reassembly logic, storage, timeout cleanup | [[`app/src/main/java/com/bitchat/android/mesh/FragmentManager.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/app/src/main/java/com/bitchat/android/mesh/FragmentManager.kt)](https://github.com/permissionlesstech/bitchat-android/blob/main/app/src/main/java/com/bitchat/android/mesh/FragmentManager.kt) |
| [`FragmentPayload.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/FragmentPayload.kt) | Binary layout: fragment ID, index, total, original type, data | [[`app/src/main/java/com/bitchat/android/model/FragmentPayload.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/app/src/main/java/com/bitchat/android/model/FragmentPayload.kt)](https://github.com/permissionlesstech/bitchat-android/blob/main/app/src/main/java/com/bitchat/android/model/FragmentPayload.kt) |
| [`PacketProcessor.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/PacketProcessor.kt) | Routes packets; delegates fragment handling to `FragmentManager` | [[`app/src/main/java/com/bitchat/android/mesh/PacketProcessor.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/app/src/main/java/com/bitchat/android/mesh/PacketProcessor.kt)](https://github.com/permissionlesstech/bitchat-android/blob/main/app/src/main/java/com/bitchat/android/mesh/PacketProcessor.kt) |
| [`MeshCore.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/MeshCore.kt) | Hosts shared `FragmentManager` instance; wires delegate chain | [[`app/src/main/java/com/bitchat/android/mesh/MeshCore.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/app/src/main/java/com/bitchat/android/mesh/MeshCore.kt)](https://github.com/permissionlesstech/bitchat-android/blob/main/app/src/main/java/com/bitchat/android/mesh/MeshCore.kt) |
| [`FragmentingPacketSender.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/FragmentingPacketSender.kt) | Emits fragments; handles delays and retry logic | [[`app/src/main/java/com/bitchat/android/mesh/FragmentingPacketSender.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/app/src/main/java/com/bitchat/android/mesh/FragmentingPacketSender.kt)](https://github.com/permissionlesstech/bitchat-android/blob/main/app/src/main/java/com/bitchat/android/mesh/FragmentingPacketSender.kt) |
| [`AppConstants.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/AppConstants.kt) | Defines MTU thresholds, fragment sizes, timeouts, limits | [[`app/src/main/java/com/bitchat/android/util/AppConstants.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/app/src/main/java/com/bitchat/android/util/AppConstants.kt)](https://github.com/permissionlesstech/bitchat-android/blob/main/app/src/main/java/com/bitchat/android/util/AppConstants.kt) |
| [`FragmentManagerTest.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/FragmentManagerTest.kt) | Unit tests for fragmentation and reassembly correctness | [[`app/src/test/java/com/bitchat/android/mesh/FragmentManagerTest.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/app/src/test/java/com/bitchat/android/mesh/FragmentManagerTest.kt)](https://github.com/permissionlesstech/bitchat-android/blob/main/app/src/test/java/com/bitchat/android/mesh/FragmentManagerTest.kt) |

## Summary

- **Bitchat Android** automatically fragments packets exceeding ~512 bytes using `FragmentManager.createFragments`, generating 8-byte random fragment IDs and wrapping chunks in `FragmentPayload` structures.
- **Inbound reassembly** occurs in `FragmentManager.handleFragment`, which validates headers, verifies metadata consistency, buffers fragments in ordered maps, and reconstructs the original packet once all pieces arrive.
- **Safety mechanisms** include per-set and global byte limits, maximum active fragment sets, 30-second timeouts, and TTL zeroing to prevent replay.
- **Cross-platform compatibility** is maintained by mirroring the iOS implementation's structure and behavior.
- **Key classes**: `FragmentManager` for logic, `FragmentPayload` for binary format, `PacketProcessor` for routing, `FragmentingPacketSender` for transmission.

## Frequently Asked Questions

### What triggers message fragmentation in Bitchat Android?

Fragmentation triggers when a `BitchatPacket` payload exceeds `AppConstants.Fragmentation.FRAGMENT_SIZE_THRESHOLD`, approximately 512 bytes to stay within Bluetooth MTU limits. The `MeshCore` automatically routes oversized messages through `FragmentingPacketSender`, which delegates to `FragmentManager.createFragments`.

### Can fragments arrive out of order and still reassemble correctly?

Yes. `FragmentManager.handleFragment` stores fragments in `incomingFragments[fragmentID]` as a map of `index → data`, then reconstructs by iterating `0 until total` in order. The unit test in [`FragmentManagerTest.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/FragmentManagerTest.kt) explicitly shuffles fragments before processing to verify this behavior.

### How does Bitchat Android prevent memory exhaustion from fragment attacks?

Three safeguards protect against malicious flooding: `MAX_FRAGMENT_TOTAL_BYTES` limits per-set buffering, `MAX_GLOBAL_FRAGMENT_TOTAL_BYTES` caps total memory across all sets, and `MAX_ACTIVE_FRAGMENT_SETS` restricts concurrent fragment collections. A 30-second timeout (`FRAGMENT_TIMEOUT`) additionally removes stale incomplete sets.

### Why is TTL set to zero on reassembled packets?

`FragmentManager` calls `copy(ttl = 0u)` during reconstruction to prevent replay attacks. A reassembled packet with its original TTL could be captured and retransmitted by an attacker to propagate indefinitely through the mesh; zeroing TTL ensures the packet is processed locally by the receiving node only.