# How Bitchat Android Implements Multi-Hop Relay for Bluetooth Mesh: A Deep Dive into the Source Code

> Explore how Bitchat Android implements multi-hop relay for Bluetooth mesh. Discover its source code architecture supporting up to 7 hops via adaptive broadcast relay.

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

---

**Bitchat Android implements multi-hop relay for Bluetooth mesh through a three-component architecture—`BluetoothMeshService`, `PacketProcessor`, and `PacketRelayManager`—that supports both source-route forwarding and TTL-based adaptive broadcast relay with up to 7 hops.**

The permissionlesstech/bitchat-android repository builds a decentralized messaging mesh over Bluetooth Low Energy (BLE). Multi-hop relay enables messages to traverse intermediate devices when sender and recipient are not in direct range. This article examines the actual Kotlin implementation, following packet flow from broadcast origination to final delivery.

## Core Architecture: Three Components for Mesh Relay

Bitchat's relay system delegates responsibility across three tightly-coupled classes. Understanding their interaction is essential to tracing how a packet propagates across multiple hops.

### BluetoothMeshService: The Mesh Orchestrator

`BluetoothMeshService` (app/src/main/java/com/bitchat/android/mesh/BluetoothMeshService.kt) serves as the top-level API and packet entry point:

- Exposes `sendMessage()` and `sendFileBroadcast()` for application-layer calls
- Provides the local peer ID (`myPeerID`) used in routing decisions
- Delegates all outbound broadcasts to `connectionManager.broadcastPacket()`
- Hands every inbound packet to `PacketProcessor` for routing decisions

```kotlin
// From BluetoothMeshService.kt
fun sendMessage(text: String) {
    val packet = createMessagePacket(text)
    val signed = signPacket(packet)
    connectionManager.broadcastPacket(RoutedPacket(signed))
}

```

### PacketProcessor: Security Validation and Routing Dispatch

`PacketProcessor` (app/src/main/java/com/bitchat/android/mesh/PacketProcessor.kt) decodes raw BLE frames and determines handler routing:

- Validates packet security via `delegate?.validatePacketSecurity`
- Handles packet types: **ANNOUNCE**, **MESSAGE**, **VOICE_FRAME**, **FRAGMENT**, and others
- Invokes `delegate?.relayPacket(routed)` for packets not destined for the local peer

This is the junction where relay eligibility is determined before `PacketRelayManager` executes the actual forwarding logic.

### PacketRelayManager: The Relay Decision Engine

`PacketRelayManager` (app/src/main/java/com/bitchat/android/mesh/PacketRelayManager.kt) contains the core multi-hop implementation. It manages two relay modes:

| Mode | Description | Use Case |
|------|-------------|----------|
| **Source-route relay** | Explicit hop list in `packet.route`; forwards to next specified peer | Guaranteed path, low latency |
| **TTL-based broadcast relay** | Probabilistic rebroadcast based on network size | Discovery, dynamic topology |

## Packet Flow Across Multiple Hops

Tracing a message from origin to destination reveals how these components coordinate:

1. **Origination**: `BluetoothMeshService.sendMessage()` creates and signs a `BitchatPacket`
2. **Initial broadcast**: `connectionManager.broadcastPacket()` transmits via BLE GATT
3. **Reception**: `BluetoothConnectionManager.onPacketReceived()` triggers `PacketProcessor.processPacket()`
4. **Validation**: Security checks pass; non-local packets trigger `delegate?.relayPacket(routed)`
5. **Relay decision**: `PacketRelayManager.handlePacketRelay()` executes:
   - Drops if `isPacketAddressedToMe` is true
   - Decrements `packet.ttl`; drops if zero
   - For source-routed packets: finds self in `packet.route`, forwards to next hop
   - For non-routed packets: evaluates `shouldRelayPacket()` for broadcast rebroadcast

The cycle repeats at each intermediate device until TTL expiration or final delivery.

## TTL Handling and Loop Prevention

`PacketRelayManager` enforces the BLE hop limit through strict TTL management:

```kotlin
// From PacketRelayManager.kt, lines 58-62
if (packet.ttl <= 0u) {
    log("Dropping packet: TTL expired")
    return
}
val decrementedTtl = packet.ttl - 1u

```

Each hop decrements TTL before forwarding. The maximum of **7 hops** aligns with BLE mesh specifications, preventing infinite propagation in cyclic topologies.

Duplicate detection complements TTL enforcement. The source-route implementation checks for routing loops by validating that the next hop hasn't already appeared earlier in the path (lines 79-84).

## Source-Route Implementation: Explicit Multi-Hop Paths

When `packet.route` is populated, `PacketRelayManager` implements deterministic forwarding:

```kotlin
// From PacketRelayManager.kt, lines 86-104 (conceptual)
val myIndex = route.indexOfFirst { it.contentEquals(myPeerId) }
if (myIndex == -1 || myIndex == route.lastIndex) return

val nextHopIdHex = route[myIndex + 1].toHex()
delegate?.sendToPeer(nextHopIdHex, packetWithDecrementedTtl) 
    ?: fallbackToBroadcast(packet)

```

Key behaviors:
- Finds local position in the byte-array route list
- Directs packet to `nextHopIdHex` via `delegate?.sendToPeer`
- Falls back to broadcast if the direct hop is disconnected

## Adaptive Broadcast Relay: Network-Aware Probabilistic Forwarding

When no source-route exists, `shouldRelayPacket()` (lines 42-70) implements controlled flooding:

```kotlin
// From PacketRelayManager.kt, lines 58-66
private fun shouldRelayPacket(): Boolean {
    if (!isRelayEnabled()) return false
    
    val networkSize = delegate?.getNetworkSize() ?: 0
    val probability = when {
        networkSize <= 10 -> 1.0
        networkSize <= 30 -> 0.85
        networkSize <= 50 -> 0.70
        networkSize <= 100 -> 0.55
        else -> 0.40
    }
    return Random.nextDouble() < probability
}

```

The probability table balances **reliability** in small meshes against **bandwidth conservation** in large deployments. Relay can be globally disabled via debug UI through `isRelayEnabled()`.

## Practical Implementation Examples

### Sending a Relay-Eligible Broadcast Message

```kotlin
val meshService = BluetoothMeshService(context)
meshService.startServices()

// Automatically relayed through mesh based on TTL and adaptive probability
meshService.sendMessage("Propagating across hops")

```

### Constructing an Explicit Source-Route Packet

```kotlin
// Define exact path: self -> intermediate -> destination
val route = listOf(
    hexStringToByteArray(myPeerId),          // hop 0 (origin)
    hexStringToByteArray("a1b2c3d4e5f6"),   // hop 1 (relay)
    hexStringToByteArray("f1e2d3c4b5a6")    // hop 2 (destination)
)

val packet = BitchatPacket(
    version = 1u,
    type = MessageType.MESSAGE.value,
    senderID = hexStringToByteArray(myPeerId),
    recipientID = null,               // null = follow route field
    timestamp = System.currentTimeMillis().toULong(),
    payload = "Routed delivery".toByteArray(),
    ttl = 7u,                         // maximum BLE hops
    route = route                     // explicit path
)

val signed = signPacketBeforeBroadcast(packet)
connectionManager.broadcastPacket(RoutedPacket(signed))

```

## Key Source Files for Multi-Hop Relay

| File | Multi-Hop Responsibility |
|------|-------------------------|
| [`BluetoothMeshService.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/BluetoothMeshService.kt) | API entry point, packet origination |
| [`PacketProcessor.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/PacketProcessor.kt) | Validation, routing dispatch |
| [`PacketRelayManager.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/PacketRelayManager.kt) | **TTL management, source-route forwarding, adaptive broadcast logic** |
| [`MessageHandler.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/MessageHandler.kt) | Implements `relayPacket()` delegate interface |
| [`BluetoothConnectionManager.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/BluetoothConnectionManager.kt) | Low-level BLE transmission (`broadcastPacket`, `sendPacketToPeer`) |

## Summary

- **Three-component architecture**: `BluetoothMeshService` orchestrates, `PacketProcessor` validates and dispatches, `PacketRelayManager` decides and executes forwarding
- **Two relay modes**: Source-route for explicit paths, TTL-probabilistic broadcast for dynamic discovery
- **TTL enforcement**: Strict decrement per hop with 7-hop BLE maximum; packets expire at zero
- **Adaptive relay probability**: Network-size-driven rebroadcast rates from 100% (≤10 peers) to 40% (>100 peers)
- **Loop prevention**: Duplicate-hop detection in source routes complements TTL expiration

## Frequently Asked Questions

### What is the maximum hop count in Bitchat's Bluetooth mesh?

Bitchat Android enforces a **maximum of 7 hops**, matching BLE mesh specifications. The `ttl` field in `BitchatPacket` decrements at each relay; packets with `ttl == 0` are dropped immediately. This limit appears in the TTL handling logic at lines 58-62 of [`PacketRelayManager.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/PacketRelayManager.kt).

### How does Bitchat prevent infinite relay loops?

Two mechanisms work together: **TTL expiration** terminates packets after 7 hops regardless of path, and **source-route duplicate detection** prevents revisiting nodes when explicit routes are used. The adaptive broadcast probability also reduces rebroadcast frequency in large networks, statistically limiting propagation scope.

### Can relay functionality be disabled for testing?

Yes. `PacketRelayManager` provides `isRelayEnabled()`, controllable through the debug UI. When disabled, `shouldRelayPacket()` returns false for all packets, making the device receive-only. This toggle is useful for isolating mesh segments during development or troubleshooting.

### When should I use source-route versus broadcast relay?

Use **source-route relay** when you know the topology and need guaranteed delivery with minimal latency—it directs packets through specific intermediaries without probabilistic flooding. Use **TTL-based broadcast relay** for peer discovery, dynamic networks, or when path information is unavailable; the adaptive probability automatically adjusts for network density.