# Anubis: an Android app manager that freezes apps outright when the VPN state changes

> Anubis manages groups of Android apps by disabling them at the OS level with pm disable-user through Shizuku, rather than isolating them in a work profile where they can still see the network stack. It drives more than a dozen VPN clients through their own start and stop APIs, and its most interesting problem is that a toggle command is an unreliable way to stop a VPN, so the app escalates through three steps to make sure the tunnel is really down.

**sogonov/anubis** — Android app manager with VPN integration. Manages groups of apps by freezing/unfreezing them based on VPN connection state.

- Repository: https://github.com/sogonov/anubis
- Stars: 1,242 · Forks: 38
- Language: Kotlin
- License: MIT
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/sogonov-anubis

## Disabling an app, not sandboxing it

Anubis is an Android app manager with VPN integration that manages groups of apps by freezing and unfreezing them based on VPN connection state.

The comparison the README opens with is the whole design. Unlike sandbox-based solutions, Island, Insular and Shelter, which only isolate apps in a work profile where they can still detect the 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.

That distinction is a security boundary rather than a preference. A work profile isolates the app's data and its view of the network, but the app is still running and the shared network stack is still there to be inspected. A disabled app is not running, so there is no process to query the system for interfaces, no broadcast receiver to observe connectivity changes, and no code path to leak data. The three claims in the README are each a different kind of exposure being closed.

The mechanism is Shizuku, which executes pm disable-user --user 0 <package> and completely disables the app at the OS level. The consequences are listed: the app cannot run any background services, cannot receive broadcasts, cannot access network interfaces, and cannot detect the VPN or a proxy.

There is a cost to that, and it is worth being clear about it. A disabled app is disabled in a way the user can see in system settings, and enabling it back is a visible state change, not a silent one. That is a design decision with a security upside, since an app that has been frozen cannot quietly resume itself, but it also means the workflow depends on the freeze and unfreeze actually happening, which is what most of the rest of the project is about.

The primary language is Kotlin, the licence is MIT, and the recent releases are 0.1.6-beta.1 on 2026-05-10, v0.1.5.1 on 2026-05-05 and 0.1.5-beta.2 on 2026-04-21. The version numbers are all in the 0.1 range, so this is an early project, and the repository is not archived with the last push on 2026-10-09. The layout is a standard Android project: an app/ module, a build.gradle.kts, a settings.gradle.kts, a gradle.properties, a gradle/ directory with the wrapper, a docs/ directory and an img/ directory for the screenshots.

## Three group policies, and a launcher that is the control surface

The feature list is organised around app groups with different network policies, and there are exactly three.

Local means the apps are frozen when the VPN is active and launched without the VPN. That is the interesting one, because it is the opposite of the usual intuition: the apps you would use through the tunnel are the ones that stay frozen while the tunnel is up, so nothing can notice that the tunnel exists. The second is VPN Only, where the apps are frozen when the VPN is inactive and launched through the VPN, which is the straightforward reading. The third is Launch with VPN, where the apps are never frozen but launching them triggers VPN activation.

That third policy is the one that shows the group model is about intent rather than about a binary state. Some apps you want to always be present, and you want the tunnel to come up when you open them. Some you want to be unable to run at all while the tunnel is up, so that the tunnel's presence is not observable. Some you want to be reachable only through the tunnel.

The home screen launcher is the control surface, and it is more than a shortcut. You tap app icons to launch with the correct VPN state, the icons for frozen or disabled apps are shown in grayscale, and a long press gives you freeze and unfreeze, shortcut creation, and group management. The grayscale is a small and effective touch, because a frozen app that looks identical to a live one is a security control that has quietly failed, and grayscale is a visual channel that does not depend on remembering which apps you configured.

There are also pinned shortcuts, which are home screen shortcuts that orchestrate freeze, VPN and launch in one tap, so the whole sequence is a single press rather than three steps. A Quick Settings tile lets you toggle from the notification shade. And the network check gives you ping, country and city, with the IP hidden by default for privacy, which is a sensible default on a tool whose whole purpose is not leaking where you are.

The automation behaviours are listed separately. Auto-freeze on boot, and on app launch if the VPN is already active. Background VPN monitoring, which auto-freezes when the VPN is toggled outside Anubis, and which is opt-in in Settings. That last one is the important one, because a user who toggles the VPN in their own client rather than in Anubis would otherwise leave a gap, and the opt-in nature of the setting suggests the author is aware it has a cost.

## How the VPN is started, and the three control modes

For clients with a known API, Anubis sends shell commands via Shizuku, and the clients fall into three control modes.

SEPARATE is independent start and stop commands. The examples are NekoBox with the QuickEnableShortcut and QuickDisableShortcut activities, Incy with CONNECT and DISCONNECT broadcasts, and Clash Meta with the START_CLASH and STOP_CLASH activity. FlClash and FlClashX are in this group too, using action.START and action.STOP on a TempActivity.

TOGGLE is a single command that toggles on or off. The examples are the v2rayNG family, v2rayNG, Happ, v2rayTun, V2Box and olcng, all using a widget broadcast, and Exclave and husi using a QuickToggleShortcut activity. The distinction that makes sense of the naming is that a toggle needs no state, so it works whether the client is on or off, but it also cannot be aimed.

MANUAL is no external start or stop API at all. Anubis opens the app and the user taps connect. The manual list explains why for each entry. AmneziaVPN has no exported start or stop API because it is a Qt app. AmneziaWG and WireGuard use the same mechanism, and the SET_TUNNEL_UP and SET_TUNNEL_DOWN approach requires a tunnel-name extra, which is planned for a later release. WG Tunnel uses the same tunnel mechanism, but its RemoteControlReceiver is gated by a user-defined secret key. TeapodStream exports only MainActivity, and Karing exports MainActivity plus a Quick Settings tile. And the last row is Any app, selectable in Settings under Other client.

The supported client list is long and specific, and the table structure makes the shape of the problem clear. There are around twenty clients across three modes, and for each of them the README names the package or packages and the exact mechanism. It also notes that variants of the same brand, such as v2rayNG Play and F-Droid, are grouped together in the Settings picker, which is a detail that matters in practice because the F-Droid build of v2rayNG has a different package name from the Play build and a script that assumed one would break on the other.

Tunguska gets its own section because it integrates differently. Instead of a single broadcast toggle, it exposes explicit AUTOMATION_START and AUTOMATION_STOP actions through a dedicated exported activity, guarded by a per-client automation token. Anubis stores that token in encrypted preferences and uses it for separate start and stop orchestration, and the quick setup is five steps: install Tunguska and import a working profile, start it once manually and grant Android VPN permission, open Anubis Integration in Tunguska, enable automation and copy the generated token, and paste it into the VPN client settings in Anubis.

The discovery section is the part a contributor would read. For closed-source clients, broadcast actions can be discovered via APK analysis using jadx: find app_widget_name in strings.xml, since resources are not obfuscated; find the receiver with android.appwidget.provider metadata to get the class name; find the setAction call in the receiver code to get the broadcast action; and verify in onReceive for the isRunning ? stop : start toggle pattern.

The two patterns discovered so far are the v2ray and xray forks, which use a widget broadcast, and the rest. That is a documented method for extending the supported client list, and it is a genuinely useful piece of documentation for anyone who wants to add a client to Anubis or to a similar tool.

## Stopping a VPN, which is harder than starting one

The VPN stop logic is the most interesting engineering in the project, and the reason is stated plainly: toggle commands are unreliable for stopping, since they can re-enable immediately, so Anubis uses a multi-step approach.

The three steps are in order. First, API stop, and this only applies to SEPARATE clients with an explicit stop command. Second, a dummy VPN, establishing Anubis's own VPN to revoke theirs, and then closing Anubis's. Third, am force-stop to kill the detected VPN app process.

The reasoning behind step two is the interesting part, and it is a piece of Android networking knowledge rather than a trick. A VPN client on Android is not the only route to the tunnel, the system's VPN service is, and the way to make the system drop a client's tunnel is for another client to take the VPN slot. So Anubis briefly becomes the VPN itself, which causes the previous client to lose the tunnel, and then closes its own. The step that follows, force-stopping the process, is the belt to that pair of braces, because a client that immediately reconnects will not be stopped by losing the slot.

And the rule that falls out of all this: apps are never unfrozen while the VPN is still active. That is the invariant the whole design exists to maintain. The three-step stop is what makes the invariant enforceable, because a toggle that re-enables immediately would unfreeze the apps while the tunnel was still up, which is exactly the leak the tool is designed to prevent.

The VPN detection is the other half and it is a different kind of solution. Anubis extracts the VPN network owner UID from dumpsys connectivity by matching the type: VPN[ pattern, then resolves the UID to a package name via pm list packages --uid. Because it works from the system's own view of which UID owns the VPN, it works with any VPN client, including unknown and custom ones. That is a much more robust approach than a hardcoded list of package names, and it is why the manual mode can still detect that a VPN is up even when Anubis cannot start or stop it.

The gap in that approach is the ordering problem. The system can tell you a VPN is up, but a tunnel that is up is not necessarily carrying your traffic, and a client that is running is not necessarily connected. So the detection is necessary but not sufficient, and it is combined with the client-specific knowledge in the tables rather than used alone.

The features list rounds this out with a VPN disconnect entry describing the multi-step sequence as API, then dummy VPN takeover, then force-stop, then kill, which is the same sequence with the final kill of the process spelled out.

## Conclusion

Anubis fits a privacy-minded Android user who runs a VPN client and wants specific apps provably unable to run when the tunnel is down, rather than merely unable to reach the network. It is the wrong tool if you need per-app traffic routing, since Anubis freezes whole apps rather than assigning them to a tunnel, and it is the wrong tool if you cannot grant Shizuku, because the freeze mechanism depends on it entirely. Before adopting, confirm your VPN client appears in the supported list with the right control mode, and read the stop sequence carefully, because a VPN that reports itself off while the tunnel is still up is the failure mode this project spends most of its effort on.

## FAQ

### How does Anubis freeze apps, and how is that different from a work profile?

It uses Shizuku to execute pm disable-user --user 0 <package>, which completely disables the app at the OS level. Unlike sandbox solutions such as Island, Insular and Shelter, which isolate apps in a work profile where they can still detect the VPN through the shared network stack, a disabled app cannot run any code, reach any network interface, or send any data.

### Which VPN clients does Anubis support?

Around twenty, in three control modes. SEPARATE clients with independent start and stop include NekoBox, Incy, Clash Meta, FlClash and FlClashX. TOGGLE clients using a single command include the v2rayNG family, Happ, v2rayTun, V2Box, olcng, Exclave and husi. MANUAL clients such as AmneziaVPN, AmneziaWG, WireGuard, WG Tunnel, TeapodStream and Karing are opened for the user to connect, and any other app can be selected in Settings.

### How does Anubis stop a VPN when a toggle is unreliable?

It escalates through three steps: an API stop for clients with an explicit stop command, then a dummy VPN that Anubis establishes itself to revoke the other client's tunnel before closing its own, and finally am force-stop to kill the VPN app process. Apps are never unfrozen while the VPN is still active.

### How does Anubis detect which app owns the VPN?

It extracts the VPN network owner UID from dumpsys connectivity by matching the type: VPN[ pattern and resolves the UID to a package name with pm list packages --uid. Because it reads the system's own view of the VPN slot it works with any client, including unknown or custom ones.

## Sources

- [Issues](https://github.com/sogonov/anubis/issues)
- [License: MIT](https://github.com/sogonov/anubis/blob/main/LICENSE)
- [README](https://github.com/sogonov/anubis/blob/main/README.md)
- [Releases](https://github.com/sogonov/anubis/releases)
- [sogonov/anubis on GitHub](https://github.com/sogonov/anubis)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/sogonov-anubis
