You block an app in your Android VPN firewall, the app stops working, and its usage row keeps climbing. The tempting conclusion is that the block leaked, or that the counter is broken. Neither follows. A usage record says bytes were attributed to that identity at some observation point; it says nothing about whether a remote service accepted a request. Once you know which boundary was measured and which identity it was charged to, the two observations stop contradicting each other.

The counter measures bytes, not delivery

Android attributes traffic to a UID and counts it in both directions, and it does so at an interface boundary rather than at the moment an application's request succeeds.

A connection attempt that is refused still moves packets: the SYN, the reset or the retransmissions that follow, plus the DNS query that preceded both. Those bytes exist even though nothing was delivered, and on some paths they are counted before any filter decides what to do with them.

That is why a blocked app's row can grow while every request fails. The counter answers "how many bytes were attributed to this identity", not "did this identity reach the internet". A chart cannot distinguish an accepted stream from a politely refused handshake, and neither can a total that adds both directions together.

Attribution follows the UID, not the icon

The row is per UID, and one UID can be shared by more than one package, while a visible app can hand work to a separate component that runs under its own identity. A browser tab rendered by a shared WebView process, a background sync service, or a companion package installed alongside the main app all produce traffic that the usage screen may display under a name you did not expect, or under the app you did block. A blocked package and a helper that carries its requests are two different subjects, and the rule covered only one of them.

When the numbers look wrong, compare the UID rather than the label. The usage API is queried by UID, and adb shell dumpsys package <package> | grep userId= prints the number to compare against, so two entries with the same UID are the same identity from the counter's point of view.

The time window may predate your rule

Usage data is collected in buckets and polled, not streamed live. A daily total can include the minutes before you enabled the block, a background sync from the previous hour, or traffic that was already in flight. Comparing a cumulative total against one refused request and calling the difference a leak is a measurement error, not a firewall finding.

Use a shorter window and a fresh start. Note the timestamp when you change the rule, wait for one collection interval, then look only at data after it. If the total keeps rising over a window that begins after the rule, you have something to investigate; if it stops rising, the earlier growth was history being displayed.

The VPN service may not be the one you configured

A VPN firewall only sees traffic that its own service carries. If another VPN app starts and takes the active connection, your filter is no longer in the path while its interface still exists in settings. Per-app bypass lists do the same thing deliberately, and a profile that excludes an app is not a leak when the app then works.

IPv4, IPv6 and DNS can also diverge. A filter that handles IPv4 may leave IPv6 or a private DNS resolver outside it, so part of the app's traffic follows a path your rules never described. Check which app currently owns the VPN and what the exclusion list says before you rewrite anything:

adb shell settings get secure always_on_vpn_app
adb shell dumpsys connectivity
adb shell dumpsys netstats detail

Test one fresh request and record four facts

Pick a harmless request, make it once, and write down four facts: the rule in force, the app's UID, the network state and the time. Then check whether your filter logged the flow, and whether the destination or gateway saw the same connection. One flow identified on both sides is evidence; an aggregate total is not.

A defensible conclusion names what was denied and which observation confirmed it. "The counter rose" is not a finding, and "all usage counters are fake" is not one either. An OSI-layer slogan does not settle this: the honest answer is that the counter and the block can both be correct while describing different things. Android's NetworkStatsManager reference documents how those buckets are queried and attributed.

Allakkat attuumassuteqartut