ProxyDroid
May 12, 2026 · View on GitHub
A global proxy app for Android, modernised around a VPN-first architecture.
ProxyDroid forwards device traffic to an upstream SOCKS5 or HTTP proxy
without requiring root. It installs a VpnService, captures IP packets on
a TUN device, and converts TCP/UDP flows into proxy connections in
userspace.
ARCHITECTURE
- VPN-first. No iptables, no redsocks, no root. The app runs as a
standard Android
VpnService, which works on every supported Android release. - Rust tun2socks. The packet-to-socket bridge lives in
app/src/main/rust/proxydroid-tun2socks, built with netstack-smoltcp and invoked from Kotlin through JNI. Cargo builds are wired in viaorg.mozilla.rust-android-gradle. - Compose UI. The settings and status surfaces are written in Jetpack Compose with Material 3.
- Per-app bypass. Apps can be excluded from the tunnel via the
standard
VpnServiceaddDisallowedApplicationAPI.
SUPPORTED UPSTREAMS
- SOCKS5 (with optional username/password auth)
- HTTP
CONNECT(with optional Basic auth)
PREREQUISITES
- JDK 17 (Gradle 8.x does not accept JDK 21+)
- Android SDK with
compileSdk36 installed - Android NDK
25.1.8937393 - CMake
3.22.1 - Rust stable toolchain with the Android targets:
aarch64-linux-android,armv7-linux-androideabi,i686-linux-android,x86_64-linux-android
AGP is pinned to 8.1.2 and Kotlin to 1.9.10. See
gradle/libs.versions.tomlfor every version. Do not bump AGP without verifying therust-android-gradle0.9.6mergeJniLibFoldersduplicate-resources interaction (cf. commits0249f91,8875c74).
BUILD
Android Studio
- Open the project root.
- Let Gradle sync — Cargo runs as part of
cargoBuildand feeds the JNI libraries into the merged APK. Build > Make Project.
Command line
./gradlew assembleDebug # debug APK at app/build/outputs/apk/debug/
./gradlew assembleRelease # release APK; requires signing config (see below)
Signing for release is opt-in. Add a local.properties with:
KEYSTORE_PATH=/absolute/path/to/keystore.jks
KEYSTORE_PASSWORD=...
KEY_ALIAS=...
KEY_PASSWORD=...
If those keys are absent the release variant builds unsigned.
PROJECT STRUCTURE
app/
├── src/main/
│ ├── java/org/proxydroid/ # Kotlin sources (Compose UI + VpnService)
│ ├── res/ # Resources
│ ├── cpp/ # Native helpers built via CMake
│ │ └── exec/ # termExec — JNI native-process helper
│ ├── rust/
│ │ └── proxydroid-tun2socks/ # Rust tun2socks crate (netstack-smoltcp)
│ └── AndroidManifest.xml
├── build.gradle # App module build (consumes libs.versions.toml)
└── proguard-rules.pro
gradle/
├── libs.versions.toml # Single source of truth for dependency versions
└── wrapper/
build.gradle # Root build, repos + plugin classpath
gradle.properties # JVM args + AGP compileSdk suppression
settings.gradle
scripts/ # Test helpers (Python SOCKS5/HTTP servers, etc.)
Legacy
cpp/libeventandcpp/redsocksdirectories from the iptables era are scheduled for removal in the ongoing refactor. The redsocks path is no longer reachable from Kotlin.
INTEGRATION TEST (EMULATOR ↔ HOST SOCKS5)
HostSocks5ProxyIntegrationTest runs inside an Android emulator and
routes an HTTP request through a SOCKS5 proxy listening on the host. The
host proxy is a small stdlib-only Python server in
scripts/socks5_test_server.py.
The emulator reaches the host loopback via the alias 10.0.2.2, so a
host proxy bound to 0.0.0.0:1080 is seen by the device as
10.0.2.2:1080.
# Terminal 1 — start the SOCKS5 proxy on the host:
python3 scripts/socks5_test_server.py --host 0.0.0.0 --port 1080
# Terminal 2 — boot any AVD, then run the instrumentation test:
./gradlew connectedAndroidTest \
-Pandroid.testInstrumentationRunnerArguments.class=org.proxydroid.HostSocks5ProxyIntegrationTest
Override defaults with
-Pandroid.testInstrumentationRunnerArguments.socksHost=...,
socksPort, targetHost, targetPort.
SUPPORTED ABIS
armeabi-v7a, arm64-v8a, x86, x86_64.
REQUIREMENTS
minSdk24 (Android 7.0)targetSdk/compileSdk36 (Android 16) — required by Google Play 2025/2026 policy
LICENSE
GPLv3.