# How Bitchat Android Handles Fragmentation of Large Messages: A Deep Dive into the Bluetooth Mesh Protocol

> Discover how Bitchat Android manages Bluetooth mesh message fragmentation with its FragmentManager, FragmentPayload, and FragmentingPacketSender system, ensuring iOS compatibility.

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

---

**Bitchat Android splits oversized packets into smaller fragment packets using a three-component system—`FragmentManager`, `FragmentPayload`, and `FragmentingPacketSender`—that ensures compatibility with the iOS implementation of Bitchat's Bluetooth mesh protocol.**

Large message fragmentation is essential for any mesh networking application constrained by MTU limits. In Bitchat Android, this capability is implemented through a unified pipeline shared across Wi-Fi Aware and Bluetooth transports. According to the [permissionlesstech/bitchat-android](https://github.com/permissionlesstech/bitchat-android) source code, the system transparently handles packets exceeding 512 bytes while maintaining cross-platform parity with the iOS reference implementation.

## Core Components of the Fragmentation System

The fragmentation architecture centers on three tightly integrated classes:

| Component | Role | Source File |
|-----------|------|-------------|
| **FragmentManager** | Generates fragment plans, stores incoming fragments, reassembles complete messages | [`FragmentManager.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/FragmentManager.kt) |
| **FragmentPayload** | Defines the 13-byte fragment header and handles payload encoding/decoding | [`FragmentPayload.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/FragmentPayload.kt) |
| **FragmentingPacketSender** | Wraps transport sends, streams fragments with progress tracking | [`FragmentingPacketSender.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/FragmentingPacketSender.kt) |

These components work together to ensure **fragmentation of large messages** happens automatically whenever payload size exceeds protocol limits.

## When Fragmentation Is Triggered

The decision to fragment occurs in `FragmentManager.createFragments`, located 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 84-86:

```kotlin
if (fullData.size <= FRAGMENT_SIZE_THRESHOLD) {
    return listOf(packet) // No fragmentation needed
}

```

The `FRAGMENT_SIZE_THRESHOLD` constant is defined in `AppConstants.Fragmentation` at approximately 512 bytes. This threshold accommodates the maximum transmission unit (MTU) for Bluetooth Low Energy while reserving headroom for protocol headers and padding.

## How the Fragment Plan Gets Built

When fragmentation is required, `FragmentManager` constructs a deterministic splitting strategy through four sequential steps:

1. **Fragment ID generation** — `FragmentPayload.generateFragmentID()` creates a random 8-byte identifier (lines 74-78 in [`FragmentPayload.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/FragmentPayload.kt))

2. **Dynamic fragment size calculation** — The system computes available payload space after accounting for headers, route information, and fixed padding overhead (lines 106-108 in [`FragmentManager.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/FragmentManager.kt))

3. **Payload chunking** — The `stride` helper function (lines 24-31) slices the original payload into equal-sized chunks, each wrapped in a `FragmentPayload` with proper sequencing metadata (lines 33-41)

4. **Fragment packet assembly** — Each `FragmentPayload` is encoded and placed into a new `BitchatPacket` with version-appropriate formatting (v1 or v2) and type `MessageType.FRAGMENT` (lines 44-54)

The resulting list of `BitchatPacket` objects represents the complete fragment sequence ready for transmission.

## Sending Fragments with Progress Tracking

The [[`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) class handles the actual transmission orchestration. Its `send` method performs the following operations:

```kotlin
// Obtains fragment list from FragmentManager (lines 21-53)
val fragments = fragmentManager.createFragments(routed.packet, maxFragments)

// Starts progress tracking for multi-fragment transfers (line 67)
if (fragments.size > 1) {
    progressManager.start(description, fragments.size)
}

// Serial transmission with configurable inter-fragment delay (lines 70-99)
fragments.forEachIndexed { index, fragment ->
    delay(interFragmentDelayMs) // Default: 20ms
    val success = sendSingle(routed.copy(packet = fragment))
    if (!success) {
        progressManager.fail(description)
        return@send false
    }
    progressManager.progress(description, index + 1)
}

```

Key transmission behaviors include:

- **Serial sending** — Fragments transmit sequentially, not in parallel, to prevent receiver buffer overflow
- **Configurable pacing** — `interFragmentDelayMs` defaults to 20 milliseconds between fragments
- **Atomic failure** — Any single fragment failure aborts the entire transfer
- **Progress callbacks** — `TransferProgressManager` updates UI components with real-time completion status

## Reassembly on the Receiving Side

Incoming fragment handling occurs in `FragmentManager.handleFragment` (lines 70-88). The reassembly process follows a strict state machine:

### Validation and Storage

```kotlin
// Decode and validate fragment header (lines 71-86)
val fragmentPayload = FragmentPayload.decode(packet.payload) ?: return null
if (!fragmentPayload.isValid) return null

// Enforce resource limits (lines 91-48)
if (incomingFragments[fragmentId]?.size ?: 0 >= MAX_FRAGMENTS_PER_ID) {
    return null // Reject excessive fragments for this ID
}

```

### Completion Detection and Reconstruction

When the stored fragment count equals `fragmentPayload.total`, reassembly triggers:

```kotlin
// Concatenate fragments in correct order (lines 62-68)
val orderedData = (0 until total).flatMap { index ->
    fragmentsMap[index] ?: return null
}.toByteArray()

// Decode original packet and clear state (lines 71-76)
val originalPacket = BitchatPacket.fromBinaryData(orderedData)
incomingFragments.remove(fragmentId)
fragmentMetadata.remove(fragmentId)

// Return with TTL=0 for fresh processing
return PacketWithTtl(originalPacket, ttl = 0)

```

### Automated Cleanup

A background coroutine (`startPeriodicCleanup`) runs every 10 seconds to purge stale fragments older than 30 seconds, preventing memory exhaustion from incomplete transfers.

## Integration with Transport Layers

The **fragmentation of large messages** operates transparently across both supported transports:

### Wi-Fi Aware

`WifiAwareMeshService` instantiates `FragmentingPacketSender` at line 91, injecting the shared `FragmentManager` from `MeshCore`:

```kotlin
val fragmentingSender = FragmentingPacketSender(
    scope = coroutineScope,
    fragmentManager = meshCore.fragmentManager,
    logTag = "WifiAwareMesh"
)

```

### Bluetooth

`BluetoothPacketBroadcaster` follows the identical pattern at line 127, ensuring BLE broadcasts benefit from identical fragmentation semantics.

This unified approach guarantees that **iOS ↔ Android message compatibility** is preserved regardless of underlying transport technology.

## Direct API Usage Examples

Developers can interact with the fragmentation system programmatically:

### Manual Fragment Generation

```kotlin
val fragmentManager = FragmentManager()
val fragments: List<BitchatPacket> = fragmentManager.createFragments(
    packet = oversizedPacket,
    maxFragments = 0xFFFF  // Allow full UInt16 range
)
// fragments ready for custom transport injection

```

### Custom Sender Configuration

```kotlin
val sender = FragmentingPacketSender(
    scope = CoroutineScope(Dispatchers.IO),
    fragmentManager = meshCore.fragmentManager,
    logTag = "CustomModule"
)

sender.send(
    routed = myRoutedPacket,
    description = "Large file broadcast",
    sendSingle = { routedPacket ->
        myCustomTransport.send(routedPacket.packet.bytes)
    }
)

```

## Summary

- **Threshold-based triggering** — Fragmentation activates automatically when un-padded payload exceeds ~512 bytes
- **Three-layer architecture** — `FragmentManager` plans and reassembles, `FragmentPayload` structures headers, `FragmentingPacketSender` orchestrates transmission
- **Cross-platform compatibility** — Fragment format matches iOS Bitchat implementation exactly
- **Robust resource management** — Per-ID limits, global byte caps, and periodic cleanup prevent DoS via fragment flooding
- **Transport-agnostic design** — Wi-Fi Aware and Bluetooth layers share identical fragmentation pipelines through dependency injection

## Frequently Asked Questions

### What is the maximum message size Bitchat Android can fragment?

The system supports up to 65,535 fragments (`0xFFFF`) per message ID. With approximately 500 bytes of payload per fragment after headers, this yields a theoretical maximum around 32 MB. However, practical limits are lower due to memory constraints and the 30-second reassembly timeout.

### How does Bitchat ensure fragments arrive in order?

Reassembly uses the `index` field in the 13-byte `FragmentPayload` header to place chunks correctly regardless of arrival order. The `FragmentManager` stores fragments in a `ConcurrentHashMap` indexed by `(fragmentId, index)` tuples.

### What happens if a fragment is lost in transmission?

The `FragmentingPacketSender` detects send failures and aborts the entire transfer, marking it failed through `TransferProgressManager`. The receiving side's 30-second timeout eventually cleans up partial fragment sets. There is no automatic retransmission—higher layers must detect failure and retry the complete message.

### Is the fragmentation protocol compatible with other mesh networks?

No. The 13-byte `FragmentPayload` header structure—including 8-byte random ID, 2-byte index, 2-byte total count, and 1-byte original type—is specific to Bitchat's protocol. While designed for iOS ↔ Android compatibility, it does not interoperate with standard Bluetooth mesh or Thread fragmentation schemes.