anubis
Android app manager with VPN integration. Manages groups of apps by freezing/unfreezing them based on VPN connection state.
Unlike sandbox-based solutions (Island, Insular, Shelter), which only isolate apps in a work profile where they can still detect VPN through the shared network stack, Anubis uses pm disable-user to completely disable apps at the system level. A disabled app cannot run any code, detect any network interfaces, or send any data.
Features
- App groups with different network policies:
- Local — apps frozen when VPN is active, launched without VPN
- VPN Only — apps frozen when VPN is inactive, launched through VPN
- Launch with VPN — never frozen, but launching triggers VPN activation
- Home screen launcher — tap app icons to launch with correct VPN state
- Grayscale icons for frozen/disabled apps
- Long press for freeze/unfreeze, shortcut creation, group management
- VPN client orchestration — auto start/stop for supported clients, any client works in manual mode
- Custom VPN client — select any installed app as VPN client from the app list
- Active VPN client detection — identifies which app owns the VPN via
dumpsys connectivityowner UID - Pinned shortcuts — home screen shortcuts that orchestrate freeze/VPN/launch in one tap
- Network check — ping, country, city (IP hidden by default for privacy)
- Quick Settings tile — toggle from notification shade
- Auto-freeze on boot and on app launch if VPN is already active
- VPN disconnect — multi-step: API → dummy VPN takeover → force-stop → kill
How It Works
Freeze Mechanism
Uses Shizuku to execute pm disable-user --user 0 <package> which completely disables an app at the OS level. The app cannot:
- Run any background services
- Receive broadcasts
- Access network interfaces
- Detect VPN or proxy
This is fundamentally different from sandboxing, which still allows the app to run and inspect the network stack.
VPN Start
For clients with known API, sends shell commands via Shizuku:
- SEPARATE (NekoBox):
am startto exported QuickEnable/QuickDisable activities - TOGGLE (v2rayNG, Happ, v2rayTun, V2Box):
am broadcastto widget receiver - MANUAL (any other client): opens the app, user connects manually
VPN Stop
Toggle commands are unreliable for stopping (can re-enable immediately), so Anubis uses a multi-step approach:
- API stop — only for SEPARATE clients with explicit stop command
- Dummy VPN — establish our own VPN to revoke theirs, then close ours
am force-stop— kill the detected VPN app process
Apps are never unfrozen while VPN is still active.
VPN Detection
Extracts the VPN network owner UID from dumpsys connectivity by matching type: VPN[ pattern, then resolves UID → package name via pm list packages --uid. Works with any VPN client, including unknown/custom ones.
Supported VPN Clients
| Client | Package | Control | Method |
|---|---|---|---|
| v2rayNG | com.v2ray.ang |
Auto (toggle) | Widget broadcast |
| NekoBox | moe.nb4a |
Full (start/stop) | Exported activities |
| Happ | com.happproxy |
Auto (toggle) | Widget broadcast |
| v2rayTun | com.v2raytun.android |
Auto (toggle) | Widget broadcast |
| V2Box | dev.hexasoftware.v2box |
Auto (toggle) | Widget broadcast |
| Any app | — | Manual | Select in Settings → "Other client" |
Discovering VPN Client APIs
For closed-source clients, broadcast actions can be discovered via APK analysis using jadx:
- Resources → find
app_widget_nameinstrings.xml(resources are not obfuscated) - Manifest → find
<receiver>withandroid.appwidget.providermetadata → get class name - Receiver code → find
setAction("...")→ this is the broadcast action - Verify → check
onReceive()for theisRunning ? stop : starttoggle pattern
All v2ray/xray forks share the same pattern:
Action: <package>.action.widget.click
Receiver: <package>.receiver.WidgetProvider
Shell command to toggle:
am broadcast -a <package>.action.widget.click -n <package>/.receiver.WidgetProvider
This is standard Android IPC discovery, not reverse engineering of protected code. Broadcast actions are public interfaces by design.
Requirements
- Android 10+ (API 29)
- Shizuku installed and running
- At least one VPN client installed
Setup
- Install and start Shizuku (via ADB or Wireless Debugging)
- Install Anubis
- Grant Shizuku permission when prompted
- Grant VPN permission when prompted (needed for dummy VPN disconnect)
- Go to Apps tab, assign apps to groups
- Select your VPN client in Settings (known or custom)
- Toggle stealth mode on the Home screen
Building
./gradlew assembleDebug
./gradlew assembleRelease # requires signing config
Create signing.properties in project root:
storeFile=release.keystore
storePassword=your_password
keyAlias=your_alias
keyPassword=your_password
Tech Stack
- Kotlin + Jetpack Compose (Material 3)
- Shizuku API 13.1.5 (AIDL UserService pattern)
- Room database with TypeConverters (app groups)
- ConnectivityManager NetworkCallback (VPN state monitoring)
- ShortcutManager (pinned shortcuts)
Roadmap
- Background VPN monitoring service (optional, for auto-freeze when VPN is toggled outside Anubis)
- Self-hosted
app_processdaemon to remove Shizuku dependency - Export/import app group configuration
- Additional VPN clients (WireGuard, sing-box, etc.)
License
MIT
