adb from Android platform-tools, apkanalyzer (ships with the Android SDK command-line tools — check the documentation for the current package name), unzip/grep (or PowerShell equivalents on Windows), mitmproxy on your laptop, and a Markdown file for the report. Root is optional and only needed for on-device packet capture.
Settings > About phone > tap Build number seven times. The exact path differs by OEM (on some devices it is Settings > System > About phone), so check the documentation if you cannot find it.Settings > System > Developer options > USB debugging → ON.<your.package> (for example com.example.notes), <device-serial> (the value printed by adb devices), <proxy-host> (your laptop's LAN IP, for example 192.168.1.10).adb devices returns nothing, work through How to Connect Android to Windows via ADB: Commands and Fixes first — every later step depends on a working connection. - [ ] adb version prints a version string
- [ ] adb devices lists exactly one device with state device
- [ ] apkanalyzer --help prints usage
- [ ] mitmproxy --version prints a version string
Example: a correctly prepared workstation shows:
$ adb devices
List of devices attached
R58M12ABCDE device
The word device (not unauthorized, not offline) means the RSA key was accepted on the phone screen.
What to do: list installed third-party packages, copy the target APK to your laptop, and export the manifest permissions.
# 1. Third-party packages only (-3 skips system apps)
adb shell pm list packages -3
# 2. Where the APK lives on the device
adb shell pm path <your.package>
# 3. Pull it (use the path printed by the previous command)
adb pull /data/app/~~<hash>/<your.package>-<hash>/base.apk ./<your.package>.apk
# 4. Declared permissions, straight from the manifest
apkanalyzer manifest print ./<your.package>.apk | grep -i "uses-permission"
Why: the manifest lists what the app *asks for*. That is the input for everything else — a notes app requesting READ_SMS is a finding even before you look at traffic. Pulling the APK also lets you analyse it offline without touching the device again.
How to check: apkanalyzer prints one line per permission. Example:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="com.google.android.gms.permission.AD_ID"/>
Write each line into your report file as permission | declared | granted?. The granted? column is filled in Step 2 — declaration alone is not proof that the permission is active.
If you pulled the app from a third-party store rather than Google Play, verify the store itself first: How to Install Verified Third-Party App Stores on Android 17 (QPR2): Step-by-Step Guide.

What to do: separate *requested* from *actually granted*, then scan the DEX code for third-party SDK packages.
# Runtime (dangerous) permissions and their actual grant state
adb shell dumpsys package <your.package> | grep -A 40 "runtime permissions:"
# AppOps: per-operation allow/ignore, includes ops with no manifest permission
adb shell cmd appops get <your.package>
# Tracker SDKs compiled into the APK (vendor prefixes are examples — confirm
# each one against that vendor's own documentation)
apkanalyzer dex packages ./<your.package>.apk \
| grep -Ei 'com\.(google\.android\.gms\.ads|facebook\.appevents|adjust\.sdk|appsflyer)'
Why: dumpsys package shows the real switch position for each dangerous permission, and AppOps catches operations the manifest never mentions (clipboard reads, background starts). The DEX scan answers "which third-party code ships inside" — an advertising or attribution SDK is a data-sharing endpoint by design.
How to check: you should see a permission block with explicit granted= values and at least one AppOps line per operation.
Example:
runtime permissions:
android.permission.POST_NOTIFICATIONS: granted=true, flags=[ USER_SENSITIVE_WHILE_INVISIBLE ]
android.permission.ACCESS_FINE_LOCATION: granted=false
POST_NOTIFICATION: allow
READ_CLIPBOARD: allow
RUN_IN_BACKGROUND: allow
For the SDK scan, the last field of each output line is the package name; leading columns are size counters whose meaning depends on the tool version — check the documentation.
P d 142 142 com.adjust.sdk
P d 88 88 com.facebook.appevents
Record it as: SDK | package prefix | evidence (apkanalyzer line). If the grep returns nothing, the app may still load SDKs dynamically — note that as "not detected statically" rather than "absent".

What to do: route device traffic through mitmproxy on your laptop, run a scripted 5-minute session in the app (launch → browse one screen → background it → kill it), then reset the proxy.
# 1. Start the interceptor on the laptop
mitmproxy --listen-port 8080
# 2. Point the device at it (same Wi-Fi network as <proxy-host>)
adb shell settings put global http_proxy <proxy-host>:8080
# 3. Drive the app, then watch hostnames appear in the mitmproxy flow list
# 4. Clear the proxy when finished
adb shell settings put global http_proxy :0
Why: http_proxy is a global Android setting, so every app on the device obeys it without a VPN profile. The flow list gives you the real endpoints — the ones the app contacts at runtime, not the ones in the manifest.
How to check: the mitmproxy window lists flows with hostnames and status codes. Example:
GET https://app.example.com/v1/sync 200
POST https://analytics.example.net/collect 204
GET https://cdn.example.com/assets/1.json 200
If HTTPS flows appear as << SSL handshake failed >> or the app logs Trust anchor for certification path not found, that is certificate pinning or the Android 7+ rule that ignores user-installed CAs — see Troubleshooting.
For apps that hide traffic this way, a complementary signal is the log stream:
adb logcat -c && adb logcat | grep -Ei 'adjust|appsflyer|firebase|okhttp|retrofit'
Example: an SDK initialisation line such as D/Adjust ( 4211): Adjust SDK initialised tells you the SDK is active even if its traffic was blocked.
Final deliverable — one table, three columns:
| Finding | Evidence | Action |
|---|---|---|
| `READ_CLIPBOARD` allowed | `cmd appops get` line `READ_CLIPBOARD: allow` | report to vendor / restrict |
| Attribution SDK bundled | `apkanalyzer dex packages` line `com.adjust.sdk` | add to data-sharing list |
| Contact to `analytics.example.net` | mitmproxy flow `POST /collect 204` | document endpoint + purpose |
Success looks like a table where every row has a command output behind it. No row without evidence.
Problem. Device is listed but every command fails with an authorisation error.
R58M12ABCDE unauthorizedadb kill-server, replug, and accept the "Allow USB debugging?" dialog. If no dialog appears: Developer options > Revoke USB debugging authorizations, then replug and run adb devices again.Problem. grep fails on Windows.
'grep' is not recognized as an internal or external command, operable program or batch file.cmd.exe or PowerShell, where grep does not exist; the capture above is shown for bash.Select-String instead of piping to grep:adb shell dumpsys package <your.package> | Select-String -Pattern "runtime permissions" -Context 0,40
Problem. HTTPS traffic is unreadable after the proxy is set.
javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.tcpdump — check the documentation for your device image.You now have a permission table backed by dumpsys output, a list of tracker SDK packages extracted from the APK, and a set of endpoints observed in mitmproxy during a scripted session. Repeat the same three steps for each app on your list and keep one report file per package, so results stay comparable across releases.
Do I need root to audit an app's network traffic?
No for the proxy method — adb shell settings put global http_proxy works on a stock device. Root is only required for on-device packet capture with tcpdump, and for inspecting traffic from apps that pin certificates.
Why does adb devices show unauthorized instead of device?
The phone has not accepted your workstation's RSA key yet. Accept the on-screen prompt, or run Developer options > Revoke USB debugging authorizations and replug the cable.
How do I find which tracker SDKs an app ships?
Pull the APK with adb pull, then run apkanalyzer dex packages and filter the output for known vendor prefixes. Static detection only proves the code is bundled; confirm activity in adb logcat or in the captured flows.
Can I run this audit on an app installed from a third-party store?
Yes, but verify the store and the APK signature first — the procedure is identical once the package is installed. The store-side checks are described in the third-party app stores guide.