Exported Android activities, services and receivers: a checklist
· 6 min read
An Android app is not one program with one entry point. It is a set of components: activities, services, broadcast receivers and content providers. Each one can be private to the app or exported, which means other apps on the same phone can start it, bind to it, send it a broadcast or query it. Exported components are how deep links, share targets and widgets work. They are also one of the most common findings in Android security reviews, because it is easy to export something by accident.
This checklist shows how to find what your built APK exports, decide what should stay open, and protect the rest.
What "exported" means
The attribute android:exported on a component in the manifest decides whether code outside your app can reach it. The default has changed over time, which is the root of many surprises:
- A component without an intent filter is not exported by default.
- Before Android 12, a component with an intent filter was exported by default.
- Since Android 12 (API level 31), apps that target API 31 or higher must set
android:exportedexplicitly on every activity, service and receiver with an intent filter, or the install fails. - Content providers were exported by default when the app's minimum or target API level was 16 or lower; since then the default is not exported.
The Android 12 rule forced a decision, which helped. But many teams answered the build error by adding android:exported="true" everywhere, and that is how a debug screen or an internal service ends up reachable from any app on the phone.
Why it matters
An exported component that trusts its input can be misused by another app on the device. Typical cases:
- An activity that skips a check. A settings or debug screen that assumes the user already signed in. Another app starts it directly and lands behind the login.
- A service or receiver that acts on commands. A receiver that clears data, starts a sync, or changes an endpoint when it gets a broadcast with the right action.
- A content provider that returns data. A provider that answers queries for messages or files to anyone who asks.
- Intent redirection. An exported activity that takes an intent from its extras and starts it, which lets another app reach your private components through it.
In the OWASP MASVS, this belongs to MASVS-PLATFORM: the app uses inter process communication mechanisms in a safe way.
Step 1: list what the built APK exports
Check the merged manifest, not the one in your source. Libraries add their own components, and some of them are exported. Two ways to see it:
- In Android Studio, open the manifest and pick the Merged Manifest tab.
- On the built file, run
aapt2 dump xmltree app-release.apk --file AndroidManifest.xmlor open the APK in apktool and readAndroidManifest.xml.
Make a list of every component with exported="true", plus any older component with an intent filter and no explicit value. For each one, note whether it has an android:permission.
Step 2: decide which ones must stay open
| Component | Usually exported on purpose | Usually a mistake |
|---|---|---|
| Activity | Launcher activity, deep link and app link handlers, share targets | Debug menus, settings screens, screens behind login |
| Service | Services bound by the system with a system permission (for example notification listeners, accessibility, autofill) | Sync and upload services, internal workers |
| Receiver | Receivers for system broadcasts that require an exported receiver, widgets | Receivers for your own app's events |
| Provider | A FileProvider is not exported and grants access per URI | Any provider that returns app data without a permission |
Anything only your own app uses should get android:exported="false".
Step 3: protect what has to stay open
- Require a permission. For components that only your own apps should reach, declare a custom permission with
android:protectionLevel="signature"and set it asandroid:permissionon the component. Only apps signed with the same key can then use it. - Use the system's permission. Services the system binds to should require the permission the system holds, for example
android.permission.BIND_NOTIFICATION_LISTENER_SERVICE. - Treat input as untrusted. An exported activity should check every extra and every URI it gets. Never start an intent taken from another intent's extras without checking where it points.
- Prefer explicit intents and PendingIntent with FLAG_IMMUTABLE for your own communication, so other apps cannot change what you send.
Step 4: test it
From a computer with the phone connected, try to reach each exported component as another app would:
# Start an activity directly
adb shell am start -n com.example.app/.DebugSettingsActivity
# Send a broadcast to a receiver
adb shell am broadcast -n com.example.app/.SyncReceiver -a com.example.app.SYNC
# Query a content provider
adb shell content query --uri content://com.example.app.provider/items
If a screen opens that should need a sign in, or a query returns data, you found the problem. The open source tool drozer goes further and automates this kind of testing.
Checking every build automatically
Exported components change when a library is updated or a teammate adds an intent filter, so a one time review goes stale. AppForShare reads the manifest of every uploaded APK, including APKs made from an .aab. The security scan shows a warning that lists activities, activity aliases, services, receivers and providers exported without a permission, up to 20 by name, with the launcher activity left out. The finding carries the MASVS-PLATFORM category and a fix.
It is a hint for review, not a verdict. Some exported components are meant to be open, and the scan cannot read your code to see whether an activity checks its input. Use the list as Step 1 of the checklist above, every time.
Free accounts see the counts on each build. Pro and Team show the list and the fix, and you can fail a pipeline on warnings:
npx appforshare security <build id or slug> --fail-on warn
On Team, the findings history shows, build by build, when the finding appeared and when it was fixed. More in the security scan docs.
Common questions
Is the launcher activity a problem?
No. It has to be exported so the home screen can start it. The scan leaves it out for that reason.
A library added an exported receiver. Can I change it?
Yes, with a manifest merge rule in your own manifest: declare the same component with tools:node="merge" and android:exported="false", after checking the library does not need it to be reachable.
Does this apply to React Native and Flutter apps?
Yes. They are Android apps with a manifest like any other, and their plugins add components too.
Related reading: what allowBackup really backs up, hardcoded API keys in an APK or IPA and security checks before you share a build. To see the list for your own app, create a free account and upload an APK.


