Moe Hamade 61588db474
chore(deps): upgrade to AGP 9.3.1, Gradle 9.6.1, Kotlin 2.4.10, SDK 37 (#750)
* chore(deps): upgrade to AGP 9.3.1, Gradle 9.6.1, Kotlin 2.4.10, SDK 37

Bring the toolchain and every dependency to latest stable. No app source
changes were required.

Toolchain:
- AGP 8.10.1 -> 9.3.1, Gradle 8.13 -> 9.6.1, Kotlin 2.2.0 -> 2.4.10
- compileSdk 35 -> 37, targetSdk 35 -> 37 (Android 17, stable)
- Java 8 -> 11

AGP 9 migration (built-in Kotlin):
- Drop org.jetbrains.kotlin.android; AGP 9 provides Kotlin natively and the
  plugin is incompatible with the new DSL
- Migrate kotlinOptions.jvmTarget to kotlin.compilerOptions (the String
  setter is a hard error in Kotlin 2.4)
- Drop android.enableJetifier (deprecated, removed in AGP 10, no support
  library deps remain)

Libraries:
- Compose BOM 2025.06.01 -> 2026.06.01, activity-compose 1.10.1 -> 1.13.0
- core-ktx 1.16.0 -> 1.19.0, lifecycle 2.9.1 -> 2.11.0 (unified with
  lifecycle-process, which had drifted to 2.8.7)
- okhttp 4.12.0 -> 5.4.0, coroutines 1.10.2 -> 1.11.0, gson 2.13.1 -> 2.14.0
- BouncyCastle 1.70 -> 1.85, switching bcprov-jdk15on -> bcprov-jdk18on
  (jdk15on is abandoned; same org.bouncycastle packages)
- Tink 1.10.0 -> 1.23.0, CameraX 1.5.2 -> 1.6.1, gms-location 21.3.0 -> 21.4.0
- security-crypto 1.1.0-beta01 -> 1.1.0, navigation-compose 2.9.1 -> 2.9.8
- exifinterface 1.3.7 -> 1.4.2, moved from a hardcoded coordinate into the
  version catalog
- Tests: espresso 3.6.1 -> 3.7.0, test-ext 1.2.1 -> 1.3.0, mockito-kotlin
  4.1.0 -> 6.3.0; mockito-inline (deprecated) -> mockito-core 5.23.0;
  coroutines-test 1.6 -> 1.11.0, now sharing the coroutines version ref
  instead of drifting

Robolectric stays pinned at 4.15: 4.16+ breaks EncryptionServiceTest with
"AndroidKeyStore not found". Bisected away from security-crypto and shown not
to be SDK-level related. Unpinning needs an EncryptionService refactor, which
is deliberately left to a follow-up PR.

targetSdk behaviour changes for API 36 and 37 were audited against the source:
edge-to-edge and predictive back are already handled, ACCESS_LOCAL_NETWORK is
not needed (loopback only, for Arti's SOCKS proxy), the reflection in
ChatViewModel touches instance rather than static final fields, and there is
no RFCOMM or scheduleAtFixedRate usage.

Verified: compileDebugKotlin, testDebugUnitTest (96 tests, 0 failures),
bundleRelease with R8, gradlew help, and build --dry-run. The 6 R8 "cannot
parse kotlin metadata" warnings present under AGP 8.13.2 are gone under 9.3.1.

Not verified on hardware. BLE mesh, foreground services, Nostr relay
websockets, Tor, and the Noise handshake still need a device smoke test.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: declare ACCESS_LOCAL_NETWORK for Wi-Fi Aware on Android 17

Android 17 (API 37) makes local network protection mandatory for apps
targeting it. WifiAwareMeshService reaches peers over link-local IPv6 TCP
sockets (connectAwareClientSocket), which may be gated by the new
ACCESS_LOCAL_NETWORK runtime permission once targetSdk is raised to 37.

The official local network permission documentation frames the feature as
LAN access and does not explicitly state whether Wi-Fi Aware peer-to-peer
networks are in scope, so this is defensive rather than confirmed-necessary.
The sockets are bound to a dedicated Aware Network obtained via
requestNetwork, not the user's subnet.

Declaring it costs nothing: ACCESS_LOCAL_NETWORK shares the NEARBY_DEVICES
group with NEARBY_WIFI_DEVICES, so users who have already granted the latter
are not prompted again. The runtime request is gated on SDK_INT >= 37 so
older devices are unaffected.

Raised by automated review on #750.

Verified: compileDebugKotlin, testDebugUnitTest (124 tests, 0 failures),
bundleRelease, and ACCESS_LOCAL_NETWORK present in the merged manifest.
Not verified on an Android 17 device.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs: correct ACCESS_LOCAL_NETWORK permission-group claim

Device testing on Android 17 (API 37) disproved the earlier claim that
ACCESS_LOCAL_NETWORK is effectively free because it shares the NEARBY_DEVICES
group with NEARBY_WIFI_DEVICES.

Granting NEARBY_WIFI_DEVICES alone leaves ACCESS_LOCAL_NETWORK denied:

  pm grant ... NEARBY_WIFI_DEVICES
  -> NEARBY_WIFI_DEVICES:  granted=true
  -> ACCESS_LOCAL_NETWORK: granted=false

The two are tracked and granted independently, so ACCESS_LOCAL_NETWORK has to
be requested explicitly. That is exactly what the wifiAwarePermissions() list
already does, so no behavioural change is needed — only the comments were
wrong. Whether the runtime dialog bundles the two into a single prompt remains
unverified, since enabling Wi-Fi Aware from Debug Settings after onboarding
never triggers a permission request at all (pre-existing, unrelated to this
branch).

Comment-only change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: request Wi-Fi Aware permissions when enabling it from Debug Settings

Enabling Wi-Fi Aware from the Debug Settings sheet never requested the
permissions it needs. The permission flow is reachable only through
PermissionManager.getRequiredPermissions(), which gates the Wi-Fi Aware entries
behind shouldRequireWifiAwarePermission() — and that returns false unless the
debug toggle is already on. Since the toggle defaults to off, onboarding never
asks, and flipping it later starts WifiAwareController directly, which only
checks the permission and bails.

The result was a silent dead end: Wi-Fi Aware could never start, and the
controller logged "Missing NEARBY_WIFI_DEVICES permission" on a 5s retry loop
indefinitely. Reproduced on a Pixel 9a (Android 17) and a Samsung SM-A366E
(Android 16); both needed adb grants to get the transport running at all.

The toggle and the Start chip now request the permissions first and only enable
the transport once NEARBY_WIFI_DEVICES is granted. ACCESS_LOCAL_NETWORK is
treated as best-effort since it does not exist below API 37 — confirmed by
`pm grant` rejecting it as an unknown permission on the Android 16 device. The
list comes from PermissionManager.wifiAwarePermissions() so the API 37 gate has
a single definition.

Also corrects the permission-group comments now that both levels are verified
on Android 17: grants are tracked independently (granting NEARBY_WIFI_DEVICES
alone leaves ACCESS_LOCAL_NETWORK denied), but the two share the NEARBY_DEVICES
group so requesting them together produces a single "Nearby devices" prompt.

Verified on device: after a clean uninstall/reinstall, toggling Wi-Fi Aware
produced one prompt and left both permissions granted with the USER_SET flag.

Addresses the second automated review finding on #750.

Verified: compileDebugKotlin, testDebugUnitTest (124 tests, 0 failures).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: version-gate Wi-Fi Aware permissions, check live grant state

Two issues from automated review of ee1ee3ad.

wifiAwarePermissions() returned NEARBY_WIFI_DEVICES unconditionally, but that
permission only exists from API 33 while minSdk is 26 and Wi-Fi Aware is
available from API 26. On an API 26-32 device the new enable path would request
an unknown permission, receive a denial, and never enable a transport that
needs no runtime permission there at all — a regression introduced by the
previous commit. Both entries are now version-gated.

The result callback also inferred the Nearby grant from the result map, which
omits permissions that were already held and so filtered out before launching.
It now reads the live permission state instead.

A denied ACCESS_LOCAL_NETWORK still does not block enabling: the controller
starts fine without it (verified on Android 17), and its necessity for
link-local sockets remains unproven, so a denial should not disable a transport
that otherwise works.

Also trims the comments added in the last two commits down to the density of
the surrounding code.

Verified: compileDebugKotlin, testDebugUnitTest (124 tests, 0 failures).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 23:34:08 +02:00
2026-01-15 11:45:22 +07:00
2025-08-04 13:50:03 +02:00
2025-07-08 20:37:46 +02:00
2025-07-08 20:37:46 +02:00
2026-02-03 15:33:56 +01:00
2025-08-29 14:37:35 +02:00

Bitchat Android Logo

Warning

This software has not received external security review and may contain vulnerabilities and may not necessarily meet its stated security goals. Do not use it for sensitive use cases, and do not rely on its security until it has been reviewed. Work in progress.

bitchat for Android

A secure, decentralized, peer-to-peer messaging app that works over Bluetooth mesh networks. No internet required for mesh chats, no servers, no phone numbers - just pure encrypted communication. Bitchat also supports geohash channels, which use an internet connection to connect you with others in your geographic area.

This is the Android port of the original bitchat iOS app, maintaining 100% protocol compatibility for cross-platform communication.

Install bitchat

You can download the latest version of bitchat for Android from the GitHub Releases page.

Or you can:

Get it on Google Play

Instructions:

  1. Download the APK: On your Android device, navigate to the link above and download the latest .apk file. Open it.
  2. Allow Unknown Sources: On some devices, before you can install the APK, you may need to enable "Install from unknown sources" in your device's settings. This is typically found under Settings > Security or Settings > Apps & notifications > Special app access.
  3. Install: Open the downloaded .apk file to begin the installation.

License

This project is released into the public domain. See the LICENSE file for details.

Features

  • Cross-Platform Compatible: Full protocol compatibility with iOS bitchat
  • Decentralized Mesh Network: Automatic peer discovery and multi-hop message relay over Bluetooth LE
  • End-to-End Encryption: X25519 key exchange + AES-256-GCM for private messages
  • Channel-Based Chats: Topic-based group messaging with optional password protection
  • Store & Forward: Messages cached for offline peers and delivered when they reconnect
  • Privacy First: No accounts, no phone numbers, no persistent identifiers
  • IRC-Style Commands: Familiar /join, /msg, /who style interface
  • Message Retention: Optional channel-wide message saving controlled by channel owners
  • Emergency Wipe: Triple-tap logo to instantly clear all data
  • Modern Android UI: Jetpack Compose with Material Design 3
  • Dark/Light Themes: Terminal-inspired aesthetic matching iOS version
  • Battery Optimization: Adaptive scanning and power management

Android Setup

Prerequisites

  • Android Studio: Arctic Fox (2020.3.1) or newer
  • Android SDK: API level 26 (Android 8.0) or higher
  • Kotlin: 1.8.0 or newer
  • Gradle: 7.0 or newer

Build Instructions

  1. Clone the repository:

    git clone https://github.com/permissionlesstech/bitchat-android.git
    cd bitchat-android
    
  2. Open in Android Studio:

    # Open Android Studio and select "Open an Existing Project"
    # Navigate to the bitchat-android directory
    
  3. Build the project:

    ./gradlew build
    
  4. Install on device:

    ./gradlew installDebug
    

Development Build

For development builds with debugging enabled:

./gradlew assembleDebug
adb install -r app/build/outputs/apk/debug/app-debug.apk

Release Build

For production releases:

./gradlew assembleRelease

Android-Specific Requirements

Permissions

The app requires the following permissions (automatically requested):

  • Bluetooth: Core BLE functionality
  • Location: Required for BLE scanning on Android
  • Network: Expand your mesh through public internet relays
  • Notifications: Message alerts and background updates

Hardware Requirements

  • Bluetooth LE (BLE): Required for mesh networking
  • Android 8.0+: API level 26 minimum
  • RAM: 2GB recommended for optimal performance

Usage

Basic Commands

  • /j #channel - Join or create a channel
  • /m @name message - Send a private message
  • /w - List online users
  • /channels - Show all discovered channels
  • /block @name - Block a peer from messaging you
  • /block - List all blocked peers
  • /unblock @name - Unblock a peer
  • /clear - Clear chat messages
  • /pass [password] - Set/change channel password (owner only)
  • /transfer @name - Transfer channel ownership
  • /save - Toggle message retention for channel (owner only)

Getting Started

  1. Install the app on your Android device (requires Android 8.0+)
  2. Grant permissions for Bluetooth and location when prompted
  3. Launch bitchat - it will auto-start mesh networking
  4. Set your nickname or use the auto-generated one
  5. Connect automatically to nearby iOS and Android bitchat users
  6. Join a channel with /j #general or start chatting in public
  7. Messages relay through the mesh network to reach distant peers

Android UI Features

  • Jetpack Compose UI: Modern Material Design 3 interface
  • Dark/Light Themes: Terminal-inspired aesthetic matching iOS
  • Haptic Feedback: Vibrations for interactions and notifications
  • Adaptive Layout: Optimized for various Android screen sizes
  • Message Status: Real-time delivery and read receipts
  • RSSI Indicators: Signal strength colors for each peer

Channel Features

  • Password Protection: Channel owners can set passwords with /pass
  • Message Retention: Owners can enable mandatory message saving with /save
  • @ Mentions: Use @nickname to mention users (with autocomplete)
  • Ownership Transfer: Pass control to trusted users with /transfer

Security & Privacy

Encryption

  • Private Messages: X25519 key exchange + AES-256-GCM encryption
  • Channel Messages: Argon2id password derivation + AES-256-GCM
  • Digital Signatures: Ed25519 for message authenticity
  • Forward Secrecy: New key pairs generated each session

Privacy Features

  • No Registration: No accounts, emails, or phone numbers required
  • Ephemeral by Default: Messages exist only in device memory
  • Cover Traffic: Random delays and dummy messages prevent traffic analysis
  • Emergency Wipe: Triple-tap logo to instantly clear all data
  • Bundled Tor Support: Built-in Tor network integration for enhanced privacy when internet connectivity is available

Performance & Efficiency

Message Compression

  • LZ4 Compression: Automatic compression for messages >100 bytes
  • 30-70% bandwidth savings on typical text messages
  • Smart compression: Skips already-compressed data

Battery Optimization

  • Adaptive Power Modes: Automatically adjusts based on battery level
    • Performance mode: Full features when charging or >60% battery
    • Balanced mode: Default operation (30-60% battery)
    • Power saver: Reduced scanning when <30% battery
    • Ultra-low power: Emergency mode when <10% battery
  • Background efficiency: Automatic power saving when app backgrounded
  • Configurable scanning: Duty cycle adapts to battery state

Network Efficiency

  • Optimized Bloom filters: Faster duplicate detection with less memory
  • Message aggregation: Batches small messages to reduce transmissions
  • Adaptive connection limits: Adjusts peer connections based on power mode

Technical Architecture

Binary Protocol

bitchat uses an efficient binary protocol optimized for Bluetooth LE:

  • Compact packet format with 1-byte type field
  • TTL-based message routing (max 7 hops)
  • Automatic fragmentation for large messages
  • Message deduplication via unique IDs

Mesh Networking

  • Each device acts as both client and peripheral
  • Automatic peer discovery and connection management
  • Store-and-forward for offline message delivery
  • Adaptive duty cycling for battery optimization

Android-Specific Optimizations

  • Coroutine Architecture: Asynchronous operations for mesh networking
  • Kotlin Coroutines: Thread-safe concurrent mesh operations
  • EncryptedSharedPreferences: Secure storage for user settings
  • Lifecycle-Aware: Proper handling of Android app lifecycle
  • Battery Optimization: Foreground service and adaptive scanning

Android Technical Architecture

Core Components

  1. BitchatApplication.kt: Application-level initialization and dependency injection
  2. MainActivity.kt: Main activity handling permissions and UI hosting
  3. ChatViewModel.kt: MVVM pattern managing app state and business logic
  4. BluetoothMeshService.kt: Core BLE mesh networking (central + peripheral roles)
  5. EncryptionService.kt: Cryptographic operations using BouncyCastle
  6. BinaryProtocol.kt: Binary packet encoding/decoding matching iOS format
  7. ChatScreen.kt: Jetpack Compose UI with Material Design 3

Dependencies

  • Jetpack Compose: Modern declarative UI
  • BouncyCastle: Cryptographic operations (X25519, Ed25519, AES-GCM)
  • Nordic BLE Library: Reliable Bluetooth LE operations
  • Kotlin Coroutines: Asynchronous programming
  • LZ4: Message compression (when enabled)
  • EncryptedSharedPreferences: Secure local storage

Binary Protocol Compatibility

The Android implementation maintains 100% binary protocol compatibility with iOS:

  • Header Format: Identical 13-byte header structure
  • Packet Types: Same message types and routing logic
  • Encryption: Identical cryptographic algorithms and key exchange
  • UUIDs: Same Bluetooth service and characteristic identifiers
  • Fragmentation: Compatible message fragmentation for large content

Publishing to Google Play

Preparation

  1. Update version information:

    // In app/build.gradle.kts
    defaultConfig {
        versionCode = 2  // Increment for each release
        versionName = "1.1.0"  // User-visible version
    }
    
  2. Create a signed release build:

    ./gradlew assembleRelease
    
  3. Generate app bundle (recommended for Play Store):

    ./gradlew bundleRelease
    

Play Store Requirements

  • Target API: Latest Android API (currently 34)
  • Privacy Policy: Required for apps requesting sensitive permissions
  • App Permissions: Justify Bluetooth and location usage
  • Content Rating: Complete questionnaire for age-appropriate content

Distribution

  • Google Play Store: Main distribution channel
  • F-Droid: For open-source distribution
  • Direct APK: For testing and development

Cross-Platform Communication

This Android port enables seamless communication with the original iOS bitchat app:

  • iPhone ↔ Android: Full bidirectional messaging
  • Mixed Groups: iOS and Android users in same channels
  • Feature Parity: All commands and encryption work across platforms
  • Protocol Sync: Identical message format and routing behavior

iOS Version: For iPhone/iPad users, get the original bitchat at github.com/jackjackbits/bitchat

Contributing

Contributions are welcome! Key areas for enhancement:

  1. Performance: Battery optimization and connection reliability
  2. UI/UX: Additional Material Design 3 features
  3. Security: Enhanced cryptographic features
  4. Testing: Unit and integration test coverage
  5. Documentation: API documentation and development guides

Support & Issues

For iOS-specific issues, please refer to the original iOS bitchat repository.

Description
bluetooth mesh chat, IRC vibes
Readme
Languages
Kotlin 85.8%
Java 10.1%
Python 2.6%
Shell 1.1%
Rust 0.3%
Other 0.1%