All posts

Which app asked? Attributing DNS queries to apps on Android

ยท by Sk Masum Ali

A list of domains is only half useful. What makes Vigil interesting is the other half: this tracker was contacted by that app. A DNS packet doesn't say who sent it, so Vigil has to work that out afterwards.

Ask the system who owns the socket

Since Android 10, ConnectivityManager has getConnectionOwnerUid. Give it the protocol, the source address and port, and the destination address and port, and it returns the UID of the app that owns that connection. The parsed packet already holds all four values, so the lookup is one call. That is also why Vigil's minimum version is Android 10.

fun getUid(query: ParsedDnsQuery): Int = try {
    cm.getConnectionOwnerUid(
        OsConstants.IPPROTO_UDP,
        InetSocketAddress(intToAddress(query.sourceIp), query.sourcePort),
        InetSocketAddress(intToAddress(query.destIp), 53),
    )
} catch (_: Exception) {
    Process.INVALID_UID
}

From a UID to something a person can read

A number isn't useful on screen. The UID is turned into a package name through PackageManager, and then into the label the user knows, such as Chrome instead of com.android.chrome. Those lookups aren't free, and a chatty app can send hundreds of queries, so results are kept in a 512-entry LruCache keyed by UID. Repeat queries from the same app skip PackageManager completely.

When it can't tell

Sometimes the system can't name an owner, or the UID has no package. Vigil falls back to an explicit AppInfo.UNKNOWN rather than guessing, so the feed shows an unknown source instead of a wrong name.

Command menu

Jump to a page, open a profile, or run an action