Find your friends at festivals, marches, and crowded events - no signal required
CrowdLink is an offline peer-to-peer Android app for finding and staying in touch with friends at crowded events. It works entirely over Bluetooth Low Energy - no internet, no mobile network, no server infrastructure of any kind.
It was built to solve a specific problem: cellular networks fail in large crowds. A survey of 42 people aged 18-35 found that 85.7% had lost contact with friends at events, and 64.3% were separated for over 10 minutes. CrowdLink works around network failure entirely by communicating directly between devices.
Designed for festivals, concerts, protests, large hikes, or anywhere people gather and signal becomes unreliable.
Each device acts as both a sender and a relay. When you send a message, it is encrypted and written to nearby devices via BLE GATT. Each receiving device checks if it is the intended recipient — if so, it decrypts and delivers it. If not, it forwards the message with a decremented TTL.
A 75% probabilistic relay prevents the network from flooding while still achieving good delivery across multiple hops. Messages are identified by UUID and tracked in a SeenMessageCache so duplicates are always dropped. Users can opt out of relaying entirely via the mesh relay toggle in settings.
GPS coordinates are also relayed through the mesh — a friend can appear on your map even when they are outside your direct BLE range.
All communication is end-to-end encrypted using AES-256-GCM via Google Tink. A 32-byte shared symmetric key is generated during QR pairing and stored locally against each paired friend. Every outgoing payload — messages, location updates, SOS alerts — is encrypted before it leaves the device.
Encrypted payloads are prefixed with 0xFF. Relay nodes forward ciphertext unchanged without decrypting it. On the receiving end, if MAC address randomisation makes sender identification unreliable, the app tries each paired friend’s key until decryption succeeds.
Pairing happens once, in person, before an event — similar in concept to AirDrop. One person generates a QR code containing their device ID, display name, and shared encryption key. The other scans it. Both devices confirm over BLE and store each other in the local Room database.
Physical proximity is required by design. It prevents remote impersonation and ensures both parties deliberately consent to the connection.
Clean Architecture with three layers:
Presentation → Jetpack Compose + ViewModels
Domain → Use cases + repository interfaces
Data → BLE, Room DB, mesh engine, encryption
The mesh transport and encryption sit entirely in the data layer. MeshRoutingEngine is pure Kotlin with no Android dependencies, making it straightforward to unit test.
Stack: Kotlin, Jetpack Compose, Material 3, Hilt, Room, Coroutines + Flow, MapLibre, Google Tink, JUnit 4, MockK, Turbine
You need at least two physical Android devices — BLE does not work on emulators.
git clone https://github.com/fionan313/CrowdLink.git
cd CrowdLink
./gradlew installDebug
Requires Android Studio Hedgehog or later, JDK 17, Android SDK API 28+.
Permissions used: Bluetooth scan/advertise, fine location (required for BLE on Android 12+), camera (QR scanning).
./gradlew testDebugUnitTest
open app/build/reports/tests/testDebugUnitTest/index.html
69 unit tests covering mesh routing, encryption, pairing, and message delivery. Integration tests are in androidTest/ for Room and Context-dependent components.
⚠️ These figures are from controlled testing on two devices during development. A broader user evaluation study is currently in progress — results will be updated here when complete.
| Metric | Target | Result |
|---|---|---|
| Discovery time | <10s | 4.5s avg |
| Distance accuracy | ±1.5m | ±0.6m |
| Battery drain | <5%/hr | 4.2%/hr |
| Mesh Delivery | >90% | 94% (3-hop) |
| SOS Latency | <2s | 0.8s avg |
Tested on Nothing Phone 2a (API 35) and Samsung Note 10+ (API 31).
app/src/main/java/com/fyp/crowdlink/
├── data/
│ ├── ble/ # BleAdvertiser, BleScanner, DeviceRepositoryImpl, RelayNodeConnection
│ ├── mesh/ # MeshRoutingEngine, MeshMessageSerialiser, SeenMessageCache
│ ├── notifications/ # MeshNotificationManager
│ ├── local/
│ │ ├── dao/ # Room DAOs
│ │ └── entity/ # Room entities
│ └── repository/ # FriendRepositoryImpl, MessageRepositoryImpl, LocationRepositoryImpl
├── domain/
│ ├── model/ # Friend, Message, MeshMessage, NearbyFriend, DeviceLocation
│ ├── repository/ # Repository interfaces
│ └── usecase/ # EstimateDistance, SendMessage, PairFriend, ShareLocation
├── presentation/
│ ├── chat/ # Messaging UI
│ ├── compass/ # Directional friend-finding
│ ├── discovery/ # Nearby device list
│ ├── friends/ # Paired friends list
│ ├── map/ # Offline map with friend pins
│ ├── onboarding/ # First-run onboarding flow
│ ├── pairing/ # QR scan and generate
│ ├── relay/ # ESP32 relay node discovery
│ ├── settings/ # Settings, profile, privacy controls
│ ├── sos/ # SOS alert
│ └── MainActivity.kt
├── ui/
│ └── theme/ # Material theme, colours, typography
├── di/ # Hilt AppModule
└── CrowdLinkApplication.kt
Final Year Project — TU Dublin, B.Sc. Computer Science (TU856), 2025/2026.
Student: Fionán Ó Ceallaigh (C22337521). Supervisor: Bryan Duggan.
This project is not open for external contributions during the assessment period. Submissions open after April 2026.
AI tools (Claude, Gemini) were used for debugging assistance and code review/clean-up.