Covers prerequisites, build commands, output verification, version updates, architecture decisions, Cargo features, and troubleshooting. https://claude.ai/code/session_01BApgDd5udqBzMqysSRMpZu
5.7 KiB
Arti Android Build Tools
Custom-built Arti (Tor in Rust) native libraries
for Amethyst Android. This replaces the Guardian Project's arti-mobile-ex AAR with a minimal
JNI wrapper built directly from Arti source.
Why custom build?
| Guardian Project AAR | Custom build | |
|---|---|---|
| Size | ~140MB | ~11MB |
| 16KB pages | No | Yes (NDK 25+) |
| Stop/restart | Broken (state file lock) | Works (TorClient persists, only SOCKS proxy stops) |
| Version | Behind | Pinned to latest (currently 1.9.0) |
Quick start
Pre-built .so files should be committed to amethyst/src/main/jniLibs/. You only need to
rebuild if you want to verify binaries, update the Arti version, or modify the JNI wrapper.
Prerequisites
-
Rust toolchain
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -
Android targets
rustup target add aarch64-linux-android x86_64-linux-android -
cargo-ndk
cargo install cargo-ndk -
Android NDK 25+ (required for 16KB page size support)
# Via Android Studio: SDK Manager → SDK Tools → NDK (Side by side) # Or via command line: sdkmanager "ndk;27.0.12077973" # Set environment variable export ANDROID_NDK_HOME="$HOME/Android/Sdk/ndk/27.0.12077973"
Building
cd tools/arti-build
# Build for all targets (arm64 + x86_64)
./build-arti.sh
# Build arm64 only (for release APKs)
./build-arti.sh --release
# Clean rebuild from scratch
./build-arti.sh --clean
The script will:
- Clone official Arti source from
gitlab.torproject.org - Check out the version pinned in
ARTI_VERSION - Copy the JNI wrapper into the source tree
- Compile with
cargo-ndkfor each target architecture - Output
.sofiles toamethyst/src/main/jniLibs/{arm64-v8a,x86_64}/ - Verify JNI symbols are exported correctly
Output
amethyst/src/main/jniLibs/
├── arm64-v8a/
│ └── libarti_android.so (~5-6 MB)
└── x86_64/
└── libarti_android.so (~6-7 MB, emulator support)
Verifying 16KB page alignment
Google Play requires 16KB page-aligned native libraries. Verify with:
readelf -l amethyst/src/main/jniLibs/arm64-v8a/libarti_android.so | grep LOAD
The first LOAD segment alignment should be 0x4000 (16384 bytes).
Directory structure
tools/arti-build/
├── README.md # This file
├── ARTI_VERSION # Pinned Arti git tag (e.g., arti-v1.9.0)
├── Cargo.toml # Rust dependencies and build profile
├── build-arti.sh # Build script
├── src/
│ └── lib.rs # JNI bridge (Rust → Kotlin)
└── .arti-source/ # [gitignored] Cloned Arti repository
Updating Arti version
-
Check available versions:
git ls-remote --tags https://gitlab.torproject.org/tpo/core/arti.git | grep 'arti-v' | tail -10 -
Update the version file:
echo "arti-v1.10.0" > ARTI_VERSION -
Update crate versions in
Cargo.tomlto match the new release. Check the crate versions at:https://gitlab.torproject.org/tpo/core/arti/-/raw/arti-v1.10.0/crates/arti-client/Cargo.toml -
Rebuild and test:
./build-arti.sh --clean
Architecture: JNI bridge
The Rust wrapper (src/lib.rs) exposes these JNI functions to Kotlin:
| JNI function | Kotlin | Purpose |
|---|---|---|
initialize(dataDir) |
ArtiNative.initialize() |
Create TorClient, bootstrap Tor network |
startSocksProxy(port) |
ArtiNative.startSocksProxy() |
Bind SOCKS5 listener on localhost |
stopSocksProxy() |
ArtiNative.stopSocksProxy() |
Abort listener, release port |
getVersion() |
ArtiNative.getVersion() |
Return Arti version string |
setLogCallback(cb) |
ArtiNative.setLogCallback() |
Register log callback |
Key design decisions
- TorClient is created once via
initialize()and persists for the app's lifetime. Its state file lock is tied to the object's lifetime and released only on GC/process exit. stopSocksProxy()only stops the TCP listener — it does NOT destroy the TorClient. This allows clean stop/start cycles without state file lock conflicts.- SOCKS5 is implemented in Rust using
tokio::net::TcpListener, not delegated to Arti's built-in proxy. This gives us full control over the listener lifecycle. - Bidirectional forwarding uses
tokio::io::copywithtokio::select!for efficiency.
Cargo.toml features
| Feature | Purpose |
|---|---|
tokio |
Async runtime |
rustls |
TLS without OpenSSL (smaller, no system dependency) |
compression |
Tor relay compression support |
bridge-client |
Connect via Tor bridges |
onion-service-client |
Access .onion addresses |
static-sqlite |
Bundled SQLite for state storage |
Release profile
[profile.release]
opt-level = "z" # Optimize for size
lto = true # Link-time optimization
codegen-units = 1 # Single codegen unit (smaller binary)
strip = true # Strip debug symbols
panic = "abort" # No unwinding (smaller binary)
Troubleshooting
cargo-ndk not found
cargo install cargo-ndk
NDK not found
export ANDROID_NDK_HOME="$HOME/Android/Sdk/ndk/<version>"
Rust targets not installed
rustup target add aarch64-linux-android x86_64-linux-android
Build fails with dependency errors
Try a clean build:
./build-arti.sh --clean
JNI symbols missing after build
The build script verifies symbols automatically. If verification fails, check that
src/lib.rs function names match the Kotlin package path:
Java_com_vitorpamplona_amethyst_ui_tor_ArtiNative_<methodName>