# How to Audit Android App Privacy via ADB: Permissions, Trackers and Network Endpoints

> AI Release · @ai_release1 · https://ai-release.org/guides/kak-provesti-audit-privatnosti-prilozheniy-na-android-c_en.html

## TL;DR

- **Time to complete:** about 45 minutes for one app (15 min for permissions, 15 min for tracker SDKs, 15 min for traffic capture).
- **Difficulty:** Intermediate — you need a terminal and a phone with USB debugging, no reverse-engineering skills.
- **You will need:** an Android device with Developer options, a USB data cable, `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.
- **Output:** a table of permissions actually granted, a list of tracker SDKs found in the APK, and a list of network hosts the app contacted during a scripted session.

## Requirements and setup
![Illustration: Requirements and setup](https://ai-release.org/guides/img/kak-provesti-audit-privatnosti-prilozheniy-na-android-c-en-1.jpg?v=1794041361)


- **Workstation OS:** Windows 10/11, macOS, or Linux with a free USB port. On Windows you also need the OEM USB driver — check the vendor documentation.
- **adb:** install Android platform-tools (Android Studio SDK Manager or the standalone archive). Version numbers change; check the documentation for the current release.
- **Phone:** Developer options enabled — `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.
- **USB debugging:** `Settings > System > Developer options > USB debugging` → ON.
- **A test app:** pick one app you own or are authorised to test. Placeholders in this guide: `<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`).
- **Disk space:** ~200 MB for the APK copies and mitmproxy flows.
- **Reference:** if `adb devices` returns nothing, work through [How to Connect Android to Windows via ADB: Commands and Fixes](https://ai-release.org/guides/kak-podklyuchit-android-k-windows-cherez-adb-komandy-sp.html?utm_source=tg&utm_medium=channel&utm_campaign=guide_inline&utm_content=guide_to_guide) first — every later step depends on a working connection.
- **Pre-flight checklist:**
  - [ ] `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:

```bash
$ 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.

## Step 1. Inventory the app and dump its declared permissions

**What to do:** list installed third-party packages, copy the target APK to your laptop, and export the manifest permissions.

```bash
# 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](https://ai-release.org/guides/poshagovoe-rukovodstvo-kak-ustanovit-storonnie-magaziny.html?utm_source=tg&utm_medium=channel&utm_campaign=guide_inline&utm_content=guide_to_guide).

## Step 2. Read runtime state, app-ops, and embedded tracker SDKs
![Illustration: Step 2. Read runtime state, app-ops, and embedded tracker SDKs](https://ai-release.org/guides/img/kak-provesti-audit-privatnosti-prilozheniy-na-android-c-en-2.jpg?v=1794041369)


**What to do:** separate *requested* from *actually granted*, then scan the DEX code for third-party SDK packages.

```bash
# 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".

## Step 3. Capture network endpoints and compile the report
![Illustration: Step 3. Capture network endpoints and compile the report](https://ai-release.org/guides/img/kak-provesti-audit-privatnosti-prilozheniy-na-android-c-en-3.jpg?v=1794041380)


**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.

```bash
# 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:

```bash
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.

## Troubleshooting

**Problem. Device is listed but every command fails with an authorisation error.**
- Error: `R58M12ABCDE	unauthorized`
- Cause: the RSA fingerprint prompt was never accepted on the phone, or the key was revoked after a USB mode change.
- Solution: unplug the cable, run `adb 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.**
- Error: `'grep' is not recognized as an internal or external command, operable program or batch file.`
- Cause: you are in `cmd.exe` or PowerShell, where `grep` does not exist; the capture above is shown for bash.
- Solution: use `Select-String` instead of piping to `grep`:

```powershell
adb shell dumpsys package <your.package> | Select-String -Pattern "runtime permissions" -Context 0,40
```

**Problem. HTTPS traffic is unreadable after the proxy is set.**
- Error: `javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.`
- Cause: the app does not trust the mitmproxy CA — either it pins certificates, or it targets an Android API level where user-installed CAs are ignored by default.
- Solution: confirm the proxy itself works by opening any plain HTTP endpoint in the device browser; if that page loads, the block is app-level. Record the hostname from the failed flow, mark the finding as "TLS-pinned, content not inspectable", and do not attempt to bypass pinning on a device you do not own. For on-device capture you would need root and `tcpdump` — check the documentation for your device image.

## Summary and next steps

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.

## FAQ

**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](https://ai-release.org/guides/poshagovoe-rukovodstvo-kak-ustanovit-storonnie-magaziny.html?utm_source=tg&utm_medium=channel&utm_campaign=guide_inline&utm_content=guide_to_guide).
