Files
b2febcee88 feat: add product flavors and TorProvider abstraction (#508)
* feat: add product flavors and TorProvider abstraction

Introduces build flavors to separate Tor functionality from the standard build, reducing APK size for users who don't need Tor.

- Creates `standard` and `tor` product flavors.
- The `standard` flavor is the default, lightweight build.
- The `tor` flavor includes the Arti (Tor) dependency and is identified by the `.tor` application ID suffix.
- Adds a `TorProvider` interface and a `TorProviderFactory` to abstract Tor implementation details between flavors.

Prepares architecture for optional Tor support to reduce APK size
from 142MB to ~4-5MB for standard builds

Related to #454

* Refactor: implement StandardTorProvider and RealTorProvider

This commit refactors the Tor integration by introducing a `TorProvider` interface and creating separate implementations for 'tor' and 'standard' product flavors.

- The original `TorManager` singleton has been moved into `RealTorProvider` for the 'tor' flavor.
- A no-op `StandardTorProvider` is introduced for the 'standard' flavor, which reports Tor as unavailable.
- A `TorProviderFactory` is used to create the appropriate provider at runtime based on the build variant.

* refactor: migrate to TorProvider abstraction

Replaced direct calls to the static `TorManager` with an instance obtained from `TorProviderFactory`.

This change allows for different Tor implementations based on build flavors, improving modularity and abstracting the Tor provider logic.

Updated `BitchatApplication`, `ChatHeader`, `OkHttpProvider`, and `AboutSheet` to use the new factory pattern for accessing Tor functionalities.

* build: add flavor-specific ProGuard rules and CI/CD

 - Split ProGuard rules: base, standard-specific, tor-specific
  - Move Arti/Guardian Project rules to proguard-tor.pro
  - Update CI/CD workflow to build both flavors
  - Add separate artifact uploads for each flavor
  - Add descriptive release notes template

  CI now builds both standard (~4-5MB) and tor (~140MB) APKs."

  resolves #454

* refactor: centralize network reset logic

Extracts the repeated network connection reset logic into a new private function `resetNetworkConnections()`.

This change also replaces direct `_statusFlow.value = ...` assignments with the safer `_statusFlow.update { ... }` function to prevent race conditions.

* ci: run separate build steps for flavors

* feat: disable Tor toggle if not available in build

* ci: parallelize builds and add conditional tor lint

Optimizes CI workflow:
- Parallel matrix builds (4 runners instead of sequential)
- Conditional tor lint only when app/src/tor/ changes in PRs
- Merged test+lint jobs to reduce setup overhead

Reduces CI time by ~50% (8-11 min vs 18-26 min)

* refactor: Unify Tor implementation and remove build flavors

This commit refactors the Tor integration by removing the `standard` and `tor` product flavors in favor of a single, unified build that always includes a custom-built Arti (Tor) library.

Key changes include:
*   **Removed Build Flavors:** Deleted the `standard` and `tor` product flavors from `build.gradle.kts`, simplifying the build process and CI configuration.
*   **Unified Tor Manager:** Replaced the `TorProvider` interface and flavor-specific implementations (`StandardTorProvider`, `RealTorProvider`) with a new singleton, `ArtiTorManager`. This class now manages the Arti lifecycle for all builds.
*   **Custom Arti Wrapper:** Introduced a new `ArtiProxy` class to provide a compatible API wrapper around the custom-built native Arti library (`libarti_android.so`). This replaces the dependency on the external `arti-mobile-ex` library.
*   **Updated Proguard:** Consolidated and updated Proguard rules into the main `proguard-rules.pro` file to keep the necessary `ArtiTorManager` and native library classes.
*   **CI/CD Simplification:** Updated GitHub Actions workflows (`release.yml`, `android-build.yml`) to build and release a single APK instead of separate ones for each flavor.

* build: Configure ABI filters for debug and release builds

For debug builds, include `x86_64` to support emulators.

For release builds, only include `arm64-v8a` to minimize the final APK size.

* feat: Ignore jniLibs directory

This change adds the `app/src/main/jniLibs/` directory to the `.gitignore` file to prevent native libraries from being committed to the repository.

* feat: Refine .gitignore for Arti build artifacts

Improves the `.gitignore` file by:
- Ignoring all `build/` directories except for `tools/arti-build/`.
- Adding specific ignores for Arti build artifacts, including the cloned source repository and the Rust build cache directory.

* feat: Update arti android native library

* feat: add build script and JNI wrapper for Arti

Adds a comprehensive build system for creating custom Arti (Tor in Rust) shared libraries for Android. This replaces the dependency on external, outdated AARs with a fully transparent and reproducible build process.

Key changes:
- Introduces `build-arti.sh`, a script to clone the official Arti repository, apply a JNI wrapper, and build `.so` files for Android.
- Adds `ARTI_VERSION` to pin the build to a specific Arti release (v1.7.0).
- Implements a new Rust JNI wrapper (`src/lib.rs`) that exposes core functions like `initialize`, `startSocksProxy`, and `stop` to the Android app.
- Includes a `Cargo.toml` with release profile optimizations for size (`lto`, `strip`, `opt-level = "z"`).
- Provides detailed documentation in `README.md` explaining the build process, prerequisites, and architecture.

* build script for mac

* consolidate both scripts

* improve script

---------

Co-authored-by: callebtc <93376500+callebtc@users.noreply.github.com>
2025-12-12 23:27:52 +07:00

6.2 KiB

Arti Android Build Tools

This directory contains the build scripts and source files for compiling the custom Arti (Tor in Rust) library for Android.

Overview

bitchat-android uses a custom-built Arti library instead of Guardian Project's outdated arti-mobile-ex AAR. This provides:

  • Smaller APK size: ~11MB total vs ~140MB with Guardian Project AAR (28x reduction)
  • Latest Arti version: Currently v1.7.0 with pure Rust TLS (rustls)
  • 16KB page size support: Required for Google Play (Nov 2025)
  • Full transparency: Build from official Arti source + our JNI wrapper

Quick Start

The pre-built .so files are committed to the repo, so you don't need to build unless you want to:

  1. Verify the binaries match the source
  2. Update to a new Arti version
  3. Modify the JNI wrapper

Directory Structure

tools/arti-build/
├── README.md           # This file
├── build-arti.sh       # Main build script (clones Arti, builds .so files)
├── ARTI_VERSION        # Pinned Arti version tag (e.g., arti-v1.7.0)
├── Cargo.toml          # Rust package configuration
├── src/
│   └── lib.rs          # JNI wrapper (Rust -> Kotlin/Java bridge)
└── .arti-source/       # [GITIGNORED] Cloned official Arti repo

app/src/main/jniLibs/   # [COMMITTED] Pre-built native libraries
├── arm64-v8a/
│   └── libarti_android.so  (~5.3MB)
└── x86_64/
    └── libarti_android.so  (~6.2MB)

Rebuilding from Source

Prerequisites

  1. Rust toolchain with Android targets:

    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
    rustup target add aarch64-linux-android x86_64-linux-android
    
  2. cargo-ndk for Android cross-compilation:

    cargo install cargo-ndk
    
  3. Android NDK 25+ (for 16KB page size support):

    # Via Android Studio SDK Manager, or:
    $ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager "ndk;27.0.12077973"
    
    # Set environment variable
    export ANDROID_NDK_HOME="$HOME/Library/Android/sdk/ndk/27.0.12077973"
    

Build Commands

cd tools/arti-build

# Build both architectures (arm64 + x86_64 for emulator)
./build-arti.sh

# Build ARM64 only (smaller, for production releases)
./build-arti.sh --release

# Clean rebuild (re-clone Arti source)
./build-arti.sh --clean

The script will:

  1. Clone official Arti from https://gitlab.torproject.org/tpo/core/arti
  2. Checkout the version specified in ARTI_VERSION
  3. Copy our JNI wrapper into the cloned repo
  4. Build with cargo ndk
  5. Copy .so files to app/src/main/jniLibs/

Verification

After building, verify the libraries:

# Check file sizes
ls -lh ../../app/src/main/jniLibs/*/libarti_android.so

# Verify JNI symbols are exported
nm -gU ../../app/src/main/jniLibs/arm64-v8a/libarti_android.so | grep Java_org_torproject

# Verify 16KB page alignment
readelf -l ../../app/src/main/jniLibs/arm64-v8a/libarti_android.so | grep LOAD
# Look for: Align 0x4000 (16KB)

Updating Arti Version

  1. Check available versions:

    git ls-remote --tags https://gitlab.torproject.org/tpo/core/arti.git | grep arti-v
    
  2. Update the version file:

    echo "arti-v1.8.0" > ARTI_VERSION
    
  3. Rebuild from scratch:

    ./build-arti.sh --clean
    
  4. Test the build:

    cd ../..
    ./gradlew clean assembleDebug
    ./gradlew installDebug
    # Enable Tor in app and verify it works
    
  5. Commit the new libraries:

    git add app/src/main/jniLibs/ tools/arti-build/ARTI_VERSION
    git commit -m "chore: update Arti to v1.8.0"
    

JNI Wrapper Architecture

The src/lib.rs file implements a JNI bridge between Kotlin and Rust:

Kotlin (ArtiNative.kt)
    ↓ JNI
Rust (lib.rs)
    ↓
Arti Client (TorClient)
    ↓
SOCKS5 Proxy (localhost:9060)

Exported JNI Functions:

  • getVersion() - Returns Arti version string
  • setLogCallback(callback) - Registers log listener for bootstrap progress
  • initialize(dataDir) - Creates Tokio runtime and TorClient
  • startSocksProxy(port) - Starts SOCKS5 proxy on specified port
  • stop() - Stops SOCKS proxy (TorClient is reused)

Key Design Decisions:

  • Global TorClient persists across stop/start cycles (fixes Nov 2024 toggle bug)
  • Tokio runtime created once and never destroyed
  • Log messages bridged to Java via GlobalRef callback

Feature Configuration

Edit Cargo.toml to customize Arti features:

[dependencies]
arti-client = {
    path = "../crates/arti-client",
    default-features = false,
    features = [
        "tokio",                  # Required: async runtime
        "rustls",                 # Required: pure Rust TLS (no OpenSSL)
        "compression",            # Optional: directory compression
        "bridge-client",          # Optional: Tor bridge support
        "onion-service-client",   # Optional: .onion site support
        "static-sqlite"           # Required: bundled SQLite
    ]
}

Size Comparison

Configuration arm64-v8a x86_64 Total APK Size
Guardian Project AAR - - ~140 MB ~150 MB
Custom (both arch) 5.3 MB 6.2 MB 11.5 MB ~15 MB
Custom (ARM-only) 5.3 MB - 5.3 MB ~10 MB

28x size reduction vs Guardian Project implementation.

Troubleshooting

"cargo-ndk not found"

cargo install cargo-ndk

"Android NDK not found"

export ANDROID_NDK_HOME="$HOME/Library/Android/sdk/ndk/27.0.12077973"

"Rust target not installed"

rustup target add aarch64-linux-android x86_64-linux-android

"Version not found"

Check available versions:

git ls-remote --tags https://gitlab.torproject.org/tpo/core/arti.git | grep arti-v | tail -10

Library too large

  1. Ensure building with --release flag
  2. Verify strip = true in Cargo.toml [profile.release]
  3. Consider removing optional features

References