How to check that an app cannot connect to the internet

A privacy policy is a promise. This is a check: one minute, with Android's official tools, against the file actually installed on your phone.

Why the internet permission settles it

On Android, an app that does not declare android.permission.INTERNET cannot open a network connection. It is not that it behaves well: the operating system refuses the attempt. It does not matter what the code does, what the company says, or who owns it tomorrow.

That is why this check is worth more than any promise. If an app that handles sensitive data does not declare that permission, there is no way for the data to leave the phone. And if it does declare it, that does not mean it is doing anything wrong — nearly every app needs it — but privacy goes back to depending on trust.

What does not work

Your phone's permission screen does not show it. Under Settings → Apps → Permissions, Android lists only dangerous permissions: the ones granted at runtime, like camera, location or contacts. INTERNET is a normal permission: it is granted at install time and never appears on that screen, for any app. Not seeing it there proves nothing whatsoever.

Neither does the privacy policy, nor the store listing, nor the app "working in airplane mode". All of that is equally consistent with an app that cannot connect and with one that can but did not happen to that day.

The quick way: ask the system

You need a computer with the Android Platform Tools — official and free — and USB debugging enabled on your phone, under developer options.

$ adb shell dumpsys package app.xtracto | grep -A 30 "requested permissions"

    requested permissions:
      app.xtracto.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION

That list is what the system has on record, and it includes normal permissions. If android.permission.INTERNET is not there, the app cannot reach the network.

The rigorous way: ask the file

The above tells you what the system believes. This tells you what the installed file itself declares, which is the source. Three commands:

# 1. Find where it is installed
adb shell pm path app.xtracto

# 2. Pull the file to your computer
adb pull /data/app/.../base.apk app.apk

# 3. Ask it which permissions it declares
aapt2 dump permissions app.apk

aapt2 ships with the Android SDK build-tools. If you have Android Studio installed, apkanalyzer manifest permissions app.apk does the same and is often closer to hand.

Watch out for split APKs. Google Play does not ship one file, it ships an app bundle: step 1 may return several paths — a base.apk and some split_config.*.apk. The manifest that counts is the base one, but if you want to be exhaustive, pull them all and check each.

How to read the output

android.permission.INTERNET

Can open network connections. This is the one that matters. Without it, any attempt to connect fails at the system level.

ACCESS_NETWORK_STATE

Only allows checking whether there is a connection. It does not allow using one. It usually accompanies INTERNET, but on its own it opens nothing.

Permissions ending in DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION

Generated automatically by AndroidX, Android's base library. They carry the package name as a prefix, are internal to the app and grant access to nothing.

BIND_NOTIFICATION_LISTENER_SERVICE

Not a permission the app requests, but one it requires of Android so that only the system can start its notification service. The actual access is something you grant by hand in settings, and can revoke whenever you want.

Without a computer

There are apps on Google Play that list the permissions of everything you have installed, reading them from the same system registry, and normal permissions do show up there. They work, but the irony is worth noticing: to avoid trusting one app you are installing another. If the check really matters to you, do it with adb.

Check it against Xtracto

Xtracto reads your bank alerts and records your spending, and its entire privacy claim rests on this. Once it is published, here is exactly what you should see:

$ aapt2 dump permissions xtracto.apk

package: app.xtracto
uses-permission: name='app.xtracto.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION'

That single permission is the internal AndroidX one. There is no INTERNET: not a policy, but something the system would not allow. If it ever shows up in that list, everything our homepage says would be a lie, and you can prove it in a minute.

What Xtracto is