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:
@@ -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>
|
||||
```
|
||||
Reference in New Issue
Block a user