docs: add README for Arti Android build tools

Covers prerequisites, build commands, output verification, version
updates, architecture decisions, Cargo features, and troubleshooting.

https://claude.ai/code/session_01BApgDd5udqBzMqysSRMpZu
This commit is contained in:
Claude
2026-04-01 17:03:47 +00:00
parent 035c570902
commit 35bf16b63e
+199
View File
@@ -0,0 +1,199 @@
# Arti Android Build Tools
Custom-built [Arti](https://gitlab.torproject.org/tpo/core/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
1. **Rust toolchain**
```bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
```
2. **Android targets**
```bash
rustup target add aarch64-linux-android x86_64-linux-android
```
3. **cargo-ndk**
```bash
cargo install cargo-ndk
```
4. **Android NDK 25+** (required for 16KB page size support)
```bash
# 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
```bash
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:
1. Clone official Arti source from `gitlab.torproject.org`
2. Check out the version pinned in `ARTI_VERSION`
3. Copy the JNI wrapper into the source tree
4. Compile with `cargo-ndk` for each target architecture
5. Output `.so` files to `amethyst/src/main/jniLibs/{arm64-v8a,x86_64}/`
6. 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:
```bash
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
1. Check available versions:
```bash
git ls-remote --tags https://gitlab.torproject.org/tpo/core/arti.git | grep 'arti-v' | tail -10
```
2. Update the version file:
```bash
echo "arti-v1.10.0" > ARTI_VERSION
```
3. Update crate versions in `Cargo.toml` to 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
```
4. Rebuild and test:
```bash
./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::copy` with `tokio::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
```toml
[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
```bash
cargo install cargo-ndk
```
### NDK not found
```bash
export ANDROID_NDK_HOME="$HOME/Android/Sdk/ndk/<version>"
```
### Rust targets not installed
```bash
rustup target add aarch64-linux-android x86_64-linux-android
```
### Build fails with dependency errors
Try a clean build:
```bash
./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>
```