Native Android VPN client built with Kotlin and Jetpack Compose. Routes device traffic through VLESS proxy servers using bundled Xray-core and tun2socks binaries.
┌──────────────┐ ┌──────────┐ ┌───────────┐ ┌──────────────┐
│ Android TUN │────▶│ tun2socks │────▶│ Xray-core │────▶│ VLESS Server │
│ (fd://0) │◀────│ SOCKS5 │◀────│ :10808 │◀────│ │
└──────────────┘ └──────────┘ └───────────┘ └──────────────┘
All device traffic is captured by the TUN interface, converted to SOCKS5 by tun2socks, and forwarded through Xray-core's VLESS outbound to a remote proxy server.
| Component | Version |
|---|---|
| Kotlin | 1.9.24 |
| Compose BOM | 2024.06.00 |
| AGP | 8.5.2 |
| Room | 2.6.1 |
| WorkManager | 2.9.0 |
| OkHttp | 4.12.0 |
| DataStore | 1.1.1 |
| Xray-core | 26.2.6 |
| tun2socks | 2.6.0 |
Target: compileSdk 34, minSdk 26, targetSdk 34, arm64-v8a only.
com.kasumi.vpn/
├── data/
│ ├── VlessServer.kt # Room entity — all VLESS fields + check results
│ ├── AppDatabase.kt # Room DB + ServerDao (sorted queries, deleteDead)
│ └── ServerRepository.kt # DAO + DataStore preferences wrapper
├── network/
│ ├── VlessParser.kt # vless:// URL parser (TCP, WS, gRPC, H2, REALITY)
│ ├── XrayConfigBuilder.kt # Generates Xray JSON config from VlessServer
│ ├── ServerFetcher.kt # Downloads + parses VLESS links from URL
│ ├── ServerChecker.kt # Parallel server ping checker (20 concurrent)
│ └── GeoIpResolver.kt # Country detection via ip-api.com through proxy
├── vpn/
│ ├── KasumiVpnService.kt # VpnService — Xray + TUN + tun2socks orchestration
│ └── XrayManager.kt # Xray binary lifecycle (start/stop/logging)
├── ui/
│ ├── MainScreen.kt # Main Compose screen layout
│ ├── MainViewModel.kt # State management, fetch/check/connect logic
│ ├── theme/Theme.kt # Dark monochrome palette + typography
│ └── components/
│ ├── ConnectButton.kt # Animated circular button with fog particles
│ ├── ServerList.kt # LazyColumn with ping badges + country flags
│ ├── ActionButtons.kt # FETCH, CHECK, FILTER, SCHEDULE buttons
│ ├── Dialogs.kt # Country filter + schedule config dialogs
│ └── ProgressOverlay.kt # Check progress bar
├── worker/
│ ├── ScheduleManager.kt # AlarmManager-based scheduling
│ ├── AlarmReceiver.kt # Alarm broadcast → WorkManager task
│ ├── FetchWorker.kt # Background server fetch
│ ├── CheckWorker.kt # Background server check
│ └── BootReceiver.kt # Restores schedules after reboot
├── notification/
│ └── NotificationHelper.kt # Notification channels + task notifications
├── KasumiApp.kt # Application init, schedule restoration
└── MainActivity.kt # Entry point, VPN permission handling
Located in app/src/main/jniLibs/arm64-v8a/:
- libxray.so (~35 MB) — Xray-core v26.2.6. VLESS proxy engine supporting TCP, WebSocket, gRPC, HTTP/2, KCP transports with TLS and REALITY.
- libtun2socks.so (~10 MB) — tun2socks v2.6.0. Reads raw IP packets from TUN device and forwards them through a SOCKS5 proxy.
Packaged as .so in jniLibs to bypass Android's W^X (Write XOR Execute) restriction. Android extracts them to nativeLibraryDir with execute permission automatically. Accessed via context.applicationInfo.nativeLibraryDir.
- Xray starts on SOCKS5 port 10808 with a generated JSON config matching the selected VLESS server.
- Wait for port — polls
127.0.0.1:10808with TCP connect (up to 15s timeout). - TUN interface created via
VpnService.Builder(address10.1.10.1/32, route0.0.0.0/0, MTU 8500, DNS8.8.8.8+1.1.1.1). Own app excluded viaaddDisallowedApplication. - fd dup trick — Java's
ProcessBuildercloses all fds > 2 in child processes. To pass the TUN fd to tun2socks, weOs.dup2(tunFd, 0)(duplicate to stdin), start tun2socks withProcessBuilder.Redirect.INHERITfor stdin, then restore fd 0 to/dev/nullin the parent. - tun2socks launched with
-device fd://0 -proxy socks5://127.0.0.1:10808. - Verification — after 500ms, check that tun2socks process is still alive.
All blocking operations run on a background thread (kasumi-vpn-setup). Only startForeground() runs on the main thread (Android requirement).
ServerChecker runs up to 20 parallel checks using a Semaphore. Each check:
- Starts a temporary Xray instance on an ephemeral port (20000+).
- Performs 2 warmup + 3 counted HTTP pings to
http://cp.cloudflare.com/generate_204through the SOCKS5 proxy. - Resolves the exit IP country via
ip-api.comthrough the same proxy. - Returns alive status, average ping, country name and code.
Accepts HTTP status 200, 204, 301, 302 as successful pings.
Uses AlarmManager (not WorkManager periodic tasks) for reliable execution on Xiaomi/MIUI and other aggressive OEM skins:
ScheduleManagersets alarms viasetExactAndAllowWhileIdle()(orsetAndAllowWhileIdle()fallback if exact alarm permission isn't granted).AlarmReceiverreceives the alarm broadcast and enqueues an expeditedOneTimeWorkRequestwith a network connectivity constraint.- After the worker completes, it reads the saved schedule from DataStore and sets the next alarm.
BootReceiverandKasumiApp.onCreate()restore active schedules from preferences.
Time alignment: given start time HH:mm and interval N hours, the next trigger is computed on an N-hour grid starting from the specified time.
Dark monochrome palette inspired by fog/mist aesthetics:
| Color | Hex | Role |
|---|---|---|
| Black | #000000 |
Background |
| Onyx | #0A0A0A |
Surfaces |
| Charcoal | #1A1A1A |
Dividers |
| Smoke | #666666 |
Secondary text |
| WarmGrey | #C8C0B8 |
Primary accent |
| White | #FFFFFF |
Headings |
The connect button features 6 animated fog particles orbiting a circular ring with sine-wave distance modulation and alpha breathing. A loading spinner arc appears during fetch/check operations.
| Permission | Purpose |
|---|---|
INTERNET |
Network access |
FOREGROUND_SERVICE |
VPN foreground notification |
FOREGROUND_SERVICE_SPECIAL_USE |
VPN service type |
POST_NOTIFICATIONS |
Task completion notifications |
RECEIVE_BOOT_COMPLETED |
Restore schedules after reboot |
ACCESS_NETWORK_STATE |
WorkManager network constraint |
SCHEDULE_EXACT_ALARM |
Precise alarm scheduling (optional) |
Requires JDK 21 (Android Studio JBR recommended):
export JAVA_HOME="/path/to/android-studio/jbr"
./gradlew assembleDebug
adb install -r app/build/outputs/apk/debug/app-debug.apkDefault fetch URL: vpn-configs-for-russia — community-maintained list of VLESS proxy configs. Configurable in-app (stored in DataStore).