The short answer
A VPN app on Android is not a mechanism for controlling what leaves the device. It is a network policy the operating system applies to application identifiers, and that policy by design does not extend to system processes, to tethering, or to a range of service connections.
The “Block connections without VPN” toggle — what is commonly called a kill switch — blocks applications specifically. In the source of Android’s networking subsystem, sockets belonging to identifiers below FIRST_APPLICATION_UID — that is, system components such as system_server, radio and network_stack — are tagged PERMISSION_SYSTEM and skip the network access check altogether. They are permitted any network, including the physical Wi-Fi interface, regardless of the state of the tunnel.
This is not a bug or an oversight. Without that exemption the operating system could not perform some of its own functions. But the consequence for the user is direct: no setting on the handset will move all outbound traffic into the tunnel, because the enforcement mechanism applies to applications rather than to the device.
The only configuration in which every packet the phone sends is guaranteed to be encapsulated looks like this: the cellular radio is off, the only connection is Wi-Fi, and the tunnel runs on the router. At that point the phone is not the thing making routing decisions.
How traffic enforcement actually works in Android
Every socket on Android carries an SO_MARK (the fwmark) telling the kernel which network it belongs to. When a VPN comes up, routing tables and the per-application rules in netd are rewritten so that for locked identifiers the only reachable network is the tunnel. If an application tries to bind a socket directly to the physical Wi-Fi interface, the call goes through the fwmark server, which calls checkUserNetworkAccess(), sees a locked identifier, and returns EPERM.
For applications the mechanism works. The exemption is stated in system/netd/server/NetworkController.cpp in the two lines that determine everything that follows:
return uid < FIRST_APPLICATION_UID ? PERMISSION_SYSTEM : PERMISSION_NONE;
if ((userPermission & PERMISSION_SYSTEM) == PERMISSION_SYSTEM) {
return 0; // ALLOWS access to ANY network, including physical Wi-Fi
}
A system component sending a packet is not checked against whether a tunnel is currently up or whether network access outside it has been blocked. The check simply does not run.
Two things follow. First, any traffic physically sent by a system process leaves the device outside the tunnel. Second, if an application can induce a system process to send a packet on its behalf, the tunnel is bypassed as well — which is exactly what the publicly demonstrated bypass of April 2026, described below, turned out to be.
What is officially exempt from the tunnel
The most precise statement on record belongs to a Google engineer responding to a report on the Android issue tracker. Connectivity checks, in his words, are far from the only thing exempt from the VPN: privileged applications can also bypass the tunnel, and for many of them this is necessary in order to function. The examples given were IWLAN and tethering traffic.
Take those categories in turn.
Internet connectivity checks. Android performs one on every connection to a Wi-Fi network in order to detect a captive portal. This was documented publicly in 2022: the checks go out around the tunnel even with “Block connections without VPN” enabled, and DNS queries, HTTP and HTTPS traffic, and probably NTP go out along with them. Google closed the report on the grounds that the behaviour was intended. The disclosure argued separately that the name of the toggle and the documentation around it mislead the user, because they create the impression that the device sends nothing outside the tunnel.
Here GrapheneOS offers a real advantage over stock Android: connectivity checks can be switched to a GrapheneOS server or disabled entirely under “Network & internet”. That was independently tested and confirmed: with the checks disabled, the leak described above was not observed.
IWLAN, meaning calls over Wi-Fi. VoLTE, VoNR, VoWi-Fi, RCS, MMS, SMS over LTE and visual voicemail are implemented largely by the operating system over TCP/IP rather than by the cellular layer. That means connections made by the operating system itself to carrier servers through the IMS subsystem and the 3gppnetwork.org domains. Those connections belong to system identifiers, and an application tunnel does not cover them. GrapheneOS modifies carrier configuration so that the toggles for disabling VoLTE, VoNR and VoWi-Fi are always available — but that disables the service rather than moving it into the tunnel.
Tethering. Covered in its own section below.
Privileged applications generally. Google’s phrasing is not limited to a list. This is a category rather than an enumeration, and it can change between versions.
Tethered traffic never enters the tunnel
This is the most common practical surprise. A phone with an active tunnel shares Wi-Fi or USB, the connected laptop checks its external address, and sees the carrier address rather than the VPN exit node. Drop the tunnel on the phone and its own browser stops loading pages while the laptop carries on as though nothing had happened.
The cause is not a limitation of the Linux kernel, as is sometimes assumed, but the architecture of the AOSP networking subsystem. A VPN on Android operates within a user profile: the tunnel belongs to a profile and extends to the applications of that profile. A connected laptop, tablet or television belongs to no profile on the phone, so no tunnel on the phone covers them.
Tethered traffic is forwarded by system components, and those, as shown above, are tagged PERMISSION_SYSTEM and do not go through the network access check. That is precisely why tethering appears in the list of exemptions alongside IWLAN: it is exempt at the same architectural level.
AOSP contains no mechanism that would route tethered traffic into a profile’s tunnel. This is not a setting hidden somewhere and not a property of a particular build: the VPN application interface does not provide for it at all, which is why no AOSP-based operating system can offer it through that interface. An implementation would require a tunnel built into the operating system itself, capable of issuing each connected device a separate route.
Workarounds exist but require root access: they insert their own routing rules into the gap between the local network and tethering priorities. Root on a Pixel requires an unlocked bootloader, which disables verified boot. We do not do this on the devices we supply and we do not recommend it.
One further point is worth knowing: the fact that tethering is in use remains visible to the carrier even when the connected device runs its own tunnel, in particular through differences in the TTL values of packets that have passed through an additional hop.
DNS leaks
Name resolution is a separate subsystem with a history of its own.
Independent researchers documented several scenarios in 2024 in which Android sends DNS queries around the tunnel: when a VPN is active but has no DNS server configured, and for a brief interval while the app is reconfiguring the tunnel, crashing, or being force stopped. The leaks involve direct calls to the getaddrinfo function, and they were reproduced on the WireGuard app specifically, which is widely treated as the reference VPN implementation for Android. The critical detail: this happens regardless of the state of “Always-on VPN” and “Block connections without VPN”.
GrapheneOS closes part of these scenarios. The project states plainly that Android allows queries from the system resolver to leak to the network-provided DNS servers through a race condition when a VPN app goes down, and likewise allows connections to the tunnel’s own DNS servers to occur outside the tunnel; both cases are prevented in GrapheneOS by extending leak blocking to that part of the system resolver.
There is also a configuration trap. Private DNS takes precedence over the DNS servers supplied by the tunnel, because from the system’s point of view those are network servers. GrapheneOS therefore recommends not keeping Private DNS configured alongside a VPN, and for secondary profiles insists on disabling Private DNS entirely until that subsystem is reworked. A combination the user enables for privacy reasons works against them in this case.
Implementation flaws appear regularly
Beyond the categories exempted deliberately, there are bugs. A recent and instructive example was published on 30 April 2026.
Android 16 introduced a mechanism for graceful teardown of QUIC connections: an application can pre-register a CONNECTION_CLOSE frame that the operating system sends on its behalf once the socket is closed. The registerQuicConnectionClosePayload method on ConnectivityManager accepts an arbitrary byte array and checks neither the permissions of the caller, nor the contents of the data passed in, nor whether the caller is currently barred from network access outside the tunnel. The sender is system_server, which, as shown above, is exempt from those checks.
The result: an ordinary application holding only two automatically granted permissions sends arbitrary bytes to an arbitrary address around an active tunnel and discloses the device’s real address. Verified on a Pixel 8 running Android 16 with “Always-on VPN” and “Block connections without VPN” both enabled. The Android security team closed the report as “Won’t Fix (Infeasible)” and did not change that decision on appeal. GrapheneOS fixed it in its own codebase.
This is not an isolated case. GrapheneOS lists several further classes of bypass on its features page that it has closed itself: sending multicast packets either directly or through the system calls that manage multicast group membership, which allowed applications to bypass the tunnel whether it was up or down; the ability to send multicast packets through a tunnel belonging to a different profile; and a hole in the eBPF-based filtering that allowed the tunnel to be bypassed by naming a specific interface through a particular system call.
The most important item on that list is the project’s own assessment. GrapheneOS states that finding and resolving all forms of VPN leaks is one of its current priorities and that it does not presently consider the feature complete, owing to further issues it has discovered. That is a candid position from the team that has done more in this area than anyone else, which is exactly why it should be read literally.
The WireGuard app on a Pixel specifically
Now to the particular scenario in the question.
The official WireGuard app for Android uses the kernel implementation where one is available and falls back to the userspace implementation where it is not. Reaching the kernel module requires root. A Pixel running GrapheneOS has no root, so the app always runs in userspace — that is, through the Go implementation on top of the same VpnService mechanism, with every limitation described above.
Then comes a detail specific to GrapheneOS. The project recommends a very short list of VPN apps, and notes that hardware memory tagging detects invalid memory accesses in the official WireGuard app — unlike the other app on that list, which has been tested on GrapheneOS with memory tagging enabled. Separately, GrapheneOS has reported that its users have repeatedly found memory corruption bugs in WireGuard-based apps on Android, and suggested that most of them originate in the Go runtime.
Memory tagging can be enabled for user-installed applications on eighth-generation Pixels and later. Enabling one of the flagship GrapheneOS protections therefore raises the likelihood of crashing the very app that holds the tunnel. And a crashing VPN app is precisely the condition under which the DNS leaks above were recorded.
None of this is an argument against WireGuard as a protocol. It is an argument against an arrangement in which the integrity of the entire network defence of a device depends on one userspace application continuing to run.
What a VPN on the phone does not do at all
Even if every leak listed above were closed, there is a boundary a VPN does not cross by definition. A tunnel operates at the network layer. Everything that happens at the radio layer lies outside it.
With a SIM inserted and the cellular radio on, the handset registers with the carrier network regardless of the state of the tunnel. The carrier sees the IMSI, the IMEI, the time of registration and the location to within a cell. No VPN affects any of that. We covered the adjacent subject in our article on the IMEI on a Google Pixel.
IMS services add their own connections to carrier servers on top of that, and as noted above, those go around the tunnel.
So a VPN on the phone answers the question of who can see the content and the destination of your IP traffic. It does not answer the question of who knows that this handset is switched on and where it is.
The leak-free configuration: Wi-Fi and a router running the tunnel
The configuration we recommend, when the objective really is that no packet leaves the device outside the tunnel, has two conditions.
First: the cellular radio is off. Airplane mode disables cellular transmit and receive, after which Wi-Fi is switched back on separately. That removes registration with the carrier network, IMS traffic, and the very channel through which leaks would reach the outside. If the SIM stays in the handset and calls over Wi-Fi are enabled, the phone will continue to register with the carrier over Wi-Fi, so calling over Wi-Fi should be disabled or the SIM removed.
Second: the tunnel runs on the router. The router encapsulates everything leaving through its external interface. There is no policy tied to application identifiers here, no exemption for system processes and no user profiles, because routing decisions are made by a device that has none of those concepts. The kill switch is a firewall rule rather than an operating system policy: if the tunnel is not up, packets simply have no route out.
What this configuration does not do, and this should be said plainly:
- It does not hide the phone from whoever operates the router and the access point. The access point sees the device MAC address, the DHCP requests and the volume of traffic.
- It does not stop application telemetry. A tunnel changes the route, not the content.
- Internet connectivity checks do not go away. They simply travel inside the tunnel along with everything else.
In other words, this solves the problem of zero leakage outside the tunnel, not the problem of anonymity.
Conclusion
A VPN app on a GrapheneOS Pixel is not a mechanism for controlling what leaves the device, because enforcement in Android applies to application identifiers and system identifiers are excluded from it at the level of the networking subsystem. This is not a fault in a particular build and not a property of a particular app.
The categories exempt from the tunnel by design are confirmed by Google itself: internet connectivity checks, privileged applications, IWLAN and tethering. Tethered traffic never enters the tunnel, and AOSP contains no mechanism that would change that. DNS leaks during reconfiguration and crash scenarios are documented on the official WireGuard app. Implementation flaws that permit the tunnel to be bypassed appear regularly, and GrapheneOS itself does not consider this area finished.
There is one leak-free configuration: cellular off, Wi-Fi only, tunnel on the router. In it the phone makes no routing decisions at all, so there is nothing to bypass.
A VPN app on the handset is not useless in this picture. It is appropriate as a second layer inside the router tunnel, and for the cases where no controlled router is within reach. It simply is not the thing people take it for.
Pixel 8, Pixel 9 and Pixel 10 with GrapheneOS. We match the model to your threat model, install GrapheneOS, lock the bootloader, and hand over the device with a complete operating handbook. GrapheneOS details →
Related: The IMEI on a Google Pixel · Pixel with GrapheneOS.
Sources
Enforcement architecture and the system exemption
- “The Tiny UDP Cannon: An Android VPN Bypass”, 30 April 2026 — analysis of registerQuicConnectionClosePayload, the relevant source in NetworkController.cpp and QuicConnectionCloser.java, and the disclosure timeline. — lowlevel.fun
- Android Issue Tracker, discussion 234170605 — the Google engineer’s statement that connectivity checks are far from the only thing exempt from the VPN, with IWLAN and tethering given as examples. Viewing the tracker may require a Google account. — issuetracker.google.com
- Android Developers, “VPN” — description of VpnService, always-on mode and the behaviour when connections outside the tunnel are blocked. — developer.android.com
Internet connectivity checks
- Android Issue Tracker, reports 250529027 (the leak) and 249990229 (the documentation).
Tethering
- Report 2145, “Hotspot traffic is not routed through (Wireguard) VPN” — reproduction of the behaviour and an explanation that a VPN on Android operates within a user profile. — github.com
DNS
- Public disclosure of Android DNS leak scenarios, 2024 — the leaks reproduced on the official WireGuard app.
- GrapheneOS, FAQ, sections on DNS, Private DNS and VPN support. — grapheneos.org
GrapheneOS measures and its own assessment of the state of play
- GrapheneOS, Features, “Improved VPN leak blocking” — the classes of bypass closed and the note that the feature is not considered complete. — grapheneos.org
- GrapheneOS, FAQ, “What kind of VPN and Tor support is available?” — the recommended apps and the note about memory tagging. — grapheneos.org
- GrapheneOS, post of 28 September 2024 on memory corruption bugs in WireGuard-based apps. — grapheneos.social
The WireGuard implementation for Android
- wireguard-android, project description: uses the kernel implementation where available, otherwise the non-root userspace implementation. — git.zx2c4.com
The cellular layer and IMS
- GrapheneOS, FAQ, “What are the default connections?” — MMS, RCS, SMS over LTE, VVM, VoLTE, VoNR and VoWi-Fi are implemented by the operating system over TCP/IP through IMS. — grapheneos.org
- GrapheneOS, FAQ, “Is the baseband isolated?” — grapheneos.org