TL;DR: An APK’s manifest lists everything the app declares: its package name and version, every permission it can request, its components, and the Android versions it targets. You can read all of it before installing with
aapt2, which ships with the Android SDK build-tools. Three commands —dump badging,dump permissions, anddump xmltree— tell you what an app is, what it wants access to, and what it can do in the background.
A download page says an APK is version 2.4.1 and “100% clean.” That is a claim, not a fact. The manifest inside the APK is the closest thing to a fact you get before installing: it is the file Android itself reads to decide what the app is allowed to do. Learning to read it takes about five minutes and three commands, and it works on any APK, from any source, without a device attached.
This pairs with two earlier posts on this site: how to verify an APK’s SHA-256 hash and read a VirusTotal report (is this file what it claims to be?) and how APK signature schemes work (who signed it?). This post answers the third question: what can it do once installed?
What the manifest actually tells you
Every APK contains AndroidManifest.xml in a binary format. Decoded, it gives you five things:
- Identity: package name (the app’s unique ID on Android),
versionCode(the integer Android uses to decide which build is newer), andversionName(the label shown to users). - Permissions: every permission the app declares, from harmless ones like
VIBRATEto sensitive ones likeREAD_SMS. - Components: activities, services, broadcast receivers, and content providers — including which ones are exported, meaning other apps can talk to them.
- SDK levels: the minimum Android version it runs on and the version its behavior was last adapted for.
- Features: hardware it requires, like a camera or telephony.
Two caveats before you start. First, declared permissions are the maximum an app can request, not what it actually uses at runtime — an app can declare CAMERA and never open it. Second, a manifest tells you nothing about native code behavior or code downloaded after install. It is the first filter, not the whole audit.
Get the tools: aapt2
The tool for this job is aapt2, the Android Asset Packaging Tool, which ships with the Android SDK build-tools. If you have Android Studio or the command-line SDK installed, it is already on your machine at:
$ANDROID_HOME/build-tools/<version>/aapt2
If you do not have the SDK, install just the build-tools package via sdkmanager (“sdkmanager "build-tools;36.0.0"” — adjust the version to whatever is current). No device, no emulator, and no root are needed. Everything here runs against the APK file on your computer.
Step 1: Verify the app’s identity
Start with dump badging, which prints a one-line summary of the package plus its permissions and features:
aapt2 dump badging app.apk | head -20
Typical output:
package: name='com.example.notes' versionCode='41' versionName='2.4.1' platformBuildVersionName='14'
sdkVersion:'24'
targetSdkVersion:'34'
uses-permission: name='android.permission.INTERNET'
uses-permission: name='android.permission.POST_NOTIFICATIONS'
uses-permission: name='android.permission.READ_EXTERNAL_STORAGE'
application-label:'Notes'
launchable-activity: name='com.example.notes.MainActivity'
uses-feature: name='android.hardware.touchscreen'
Three fields deserve your attention. The package name is the app’s real identity — two APKs with different package names are different apps, no matter what the download page says. The versionCode is what Android compares to decide whether an update is newer; versionName is just a label and can say anything. The targetSdkVersion tells you how recently the app was adapted to Android’s behavior rules: an app targeting API 34 was built against Android 14’s permission model, while one still targeting API 24 is running under decade-old rules with broader defaults. A sideloaded “latest version” with a suspiciously old target SDK is worth a second look.
Step 2: List every declared permission
dump permissions prints exactly what it says — the full permission list, nothing else:
aapt2 dump permissions app.apk
package: com.example.notes
uses-permission: name='android.permission.INTERNET'
uses-permission: name='android.permission.POST_NOTIFICATIONS'
uses-permission: name='android.permission.READ_EXTERNAL_STORAGE'
uses-permission: name='android.permission.ACCESS_FINE_LOCATION'
uses-permission: name='android.permission.RECORD_AUDIO'
Read this list against what the app claims to do. A notes app declaring ACCESS_FINE_LOCATION and RECORD_AUDIO is not automatically malicious, but it owes you an explanation, and “no explanation” is itself an answer. Android groups permissions by how they are granted, which determines how much friction stands between the app and the capability:
| Grant type | How the app gets it | Examples |
|---|---|---|
| Normal | Granted automatically at install, no prompt | INTERNET, VIBRATE, POST_NOTIFICATIONS (before Android 13) |
| Dangerous (runtime) | System prompt at first use; user can grant, deny, or grant once | CAMERA, RECORD_AUDIO, READ_CONTACTS, ACCESS_FINE_LOCATION, READ_SMS |
| Special | User flips a toggle in Settings; no standard prompt | SYSTEM_ALERT_WINDOW, REQUEST_INSTALL_PACKAGES, SCHEDULE_EXACT_ALARM, MANAGE_EXTERNAL_STORAGE |
| Signature | Only granted to apps signed with the same key | Mostly system and OEM permissions |
The special category is the one people underestimate. These permissions never show a runtime dialog — the app sends the user to a Settings page to flip a switch, which social-engineering-heavy malware walks victims through. On Android 16, the system now blocks enabling sideloading and accessibility access during an active phone call, precisely because scammers were talking victims through those screens.
Step 3: Read the full manifest tree
dump xmltree decodes the entire binary manifest into a readable tree. This is where you find what the app can do in the background:
aapt2 dump xmltree app.apk AndroidManifest.xml | grep -i -A2 "service\|receiver\|provider" | head -40
Look for four things:
- Exported components. An activity, service, or receiver with
android:exported="true"can be invoked by other apps. Since Android 12 (API 31), any component with an intent filter must declare this explicitly, so a modern manifest makes it easy to spot. Exported components are entry points — each one is worth a glance. - Background components. Services and broadcast receivers with actions like
BOOT_COMPLETEDtell you the app starts itself and runs without being opened. A flashlight app that registers a boot receiver and a background service is doing more than toggling an LED. - Deep links and intent filters.
VIEWactions withhttp/httpsdata schemes show which URLs the app claims. On Android 12+, verified app links require the domain to authorize the app, but unverified deep links still intercept URLs with a disambiguation dialog. - Meta-data blocks. SDKs (advertising, analytics, attribution) often stash API keys and app IDs in
<meta-data>in plain text. This is also where you spot which third-party SDKs are bundled.
If you prefer reading the manifest as plain XML rather than a tree dump, apktool d app.apk decodes it into a readable AndroidManifest.xml (covered in this site’s apktool guide). For programmatic analysis across many APKs, AndroGuard’s androguard analyze parses manifests into queryable objects. The table below compares the options.
Which command shows what
| Command | Shows | Best for |
|---|---|---|
aapt2 dump badging |
Package, version, SDK levels, permissions, features, launcher activity | 30-second identity and sanity check |
aapt2 dump permissions |
Declared permissions only | Auditing what an app can request |
aapt2 dump xmltree … AndroidManifest.xml |
Full manifest: components, intent filters, meta-data | Finding background behavior and exported entry points |
apktool d |
Readable AndroidManifest.xml plus resources and smali |
Deep reading and editing |
apksigner verify --print-certs |
Signing schemes and certificate fingerprints | Confirming who signed the APK (see the signature-schemes post) |
adb shell dumpsys package <pkg> |
Install-time and runtime permission grants on a device | Checking what you actually granted after installing |
Permissions that deserve a second look
Not every sensitive permission is a red flag — a maps app needs location, a messenger needs contacts. The question is always fit: does this permission match what the app claims to do? These are the ones where a mismatch matters most:
| Permission | Grant type | Why it matters |
|---|---|---|
READ_SMS / SEND_SMS / RECEIVE_SMS |
Runtime | Reads or sends text messages; classic toll-fraud and OTP-theft vector. A non-messaging app has no business here. |
READ_CALL_LOG |
Runtime | Exposes who you called and when. Rarely justified outside dialer apps. |
ACCESS_BACKGROUND_LOCATION |
Runtime (separate since Android 10) | Tracks location when the app is not open. Foreground location for a maps app is normal; background is not. |
RECORD_AUDIO / CAMERA |
Runtime | Microphone and camera access. Check whether the app’s features plausibly need them. |
SYSTEM_ALERT_WINDOW |
Special (Settings toggle) | Draws over other apps — the mechanism behind overlay-based phishing and ad injection. |
REQUEST_INSTALL_PACKAGES |
Special (per-app toggle since Android 8) | Lets the app install other APKs. Legitimate for app stores; alarming in a wallpaper app. |
QUERY_ALL_PACKAGES |
Declared; no user prompt | Lets the app list every installed app — used for fingerprinting and competitor detection. |
BIND_DEVICE_ADMIN |
Device-admin activation flow | Can lock the device or wipe data once activated. Ransomware’s favorite permission. |
MANAGE_EXTERNAL_STORAGE |
Special (“All files access”) | Bypasses scoped storage and reads every file. Very few app categories justify it. |
One more check costs nothing: compare the permission list against the previous version of the same app. A routine “bug fix” update that suddenly adds READ_SMS or QUERY_ALL_PACKAGES is the single most suspicious thing a manifest can tell you.
Why old guides mislead: the permission model keeps changing
Much of the advice about Android permissions online predates the runtime model entirely. Since Android 6.0, Google has steadily moved permissions from install-time lists to runtime prompts and added new controls almost every release. If a guide does not account for these changes, its conclusions are unreliable:
| Version | Year (API) | What changed |
|---|---|---|
| Android 6.0 | 2015 (API 23) | Runtime permission prompts replace the install-time permission list |
| Android 8.0 | 2017 (API 26) | “Install unknown apps” becomes a per-app toggle (REQUEST_INSTALL_PACKAGES) |
| Android 10 | 2019 (API 29) | Background location becomes a separate runtime permission |
| Android 11 | 2020 (API 30) | One-time permissions; auto-reset for unused apps |
| Android 12 | 2021 (API 31) | Privacy Dashboard (24-hour history); approximate-location option; exported components must be explicit |
| Android 13 | 2022 (API 33) | POST_NOTIFICATIONS becomes runtime; READ_MEDIA_* replaces broad storage access |
| Android 14 | 2023 (API 34) | Partial photo/video access; stricter foreground-service type declarations |
| Android 16 | 2025 (API 36) | 7-day Privacy Dashboard history; blue-dot location indicator; OTP redaction in notifications; risky grants blocked during calls |
The practical upshot: a manifest from an app targeting a recent SDK is far more informative than the manifest alone suggests, because the platform constrains what declared permissions can actually do. Conversely, an app deliberately targeting an old SDK to keep old defaults is telling you something about its priorities.
The 60-second pre-install routine
- Identity: run
aapt2 dump badging— does the package name, versionCode, and targetSdkVersion match what the download page claims? - Permissions: run
aapt2 dump permissions— does every sensitive permission fit the app’s stated purpose? - Background behavior: run
aapt2 dump xmltreeand check services, receivers, and exported components — does anything run that the app never mentions? - Provenance: verify the SHA-256 hash and the signer certificate against the developer’s published values before trusting any of the above.
- After install: grant the minimum, then check
adb shell dumpsys package <package>or the Privacy Dashboard to see what was actually granted.
Key takeaways
- The manifest is the source of truth about an APK; download pages are claims.
aapt2reads it without installing anything. dump badgingverifies identity,dump permissionsaudits access,dump xmltreeexposes background components.- Judge permissions by fit, not by presence: a maps app with location access is normal, a notes app with SMS access is not.
- Watch special-access permissions (
SYSTEM_ALERT_WINDOW,REQUEST_INSTALL_PACKAGES,MANAGE_EXTERNAL_STORAGE) — they bypass runtime prompts entirely. - Diff the manifest between updates: new invasive permissions in a minor update are the strongest warning sign.
References
- Android Developers — aapt2 documentation
- Android Developers — Permissions on Android
- Android Developers — Android 16 behavior changes
- Android Authority — Android 16 confirmed features (anti-scammer call safeguards, OTP redaction)
- Android Headlines — Android 16 blue-dot location indicator
- Android Police — Privacy Dashboard 7-day history in Android 16
Was this useful?
Thanks for the feedback.
One comment