Question
How to Fix android:exported Errors in Android 12 Manifest Files
Question
After upgrading an Android app to target Android 12, the project no longer compiles and fails during manifest merging with this error:
Manifest merger failed with multiple errors, see logs
The merged manifest reports:
android:exported needs to be explicitly specified. Apps targeting Android 12 and higher are required to specify an explicit value for android:exported when the corresponding component has an intent filter defined.
I already added android:exported="false" or android:exported="true" to many activities in the main manifest, but the error still appears.
The project uses:
- multiple manifest files
compileSdkVersion 31targetSdkVersion 31- activities, services, and receivers with
intent-filter
A second manifest file also defines activities, including this one:
<activity
android:name=".ui.EasyOnboardingInviteActivity"
android:label="@string/invite_to_app"
android:launchMode="singleTask" />
How do I correctly fix this Android 12 android:exported manifest merger error, especially when the app has multiple manifest files?
Short Answer
By the end of this page, you will understand why Android 12 requires android:exported, which components must define it, how manifest merging affects the final result, and how to find the real component causing the error when you already updated part of the manifest.
Concept
What android:exported means
android:exported tells Android whether a component can be launched by other apps.
It applies to components such as:
activityservicereceiver- sometimes other manifest-declared app entry points
Example meanings:
android:exported="true"→ other apps can start or interact with this component if allowedandroid:exported="false"→ only your own app can use it directly
Why Android 12 made this required
Starting with apps targeting Android 12 (API 31) and above, any component that has an intent-filter must explicitly declare android:exported.
This rule exists because components with intent-filter are possible entry points into your app. Android now requires developers to state clearly whether those entry points are public or private.
Why your app can still fail even after updating many activities
A common mistake is assuming the error refers only to the manifest file you are currently looking at.
In real Android projects, the final manifest is built by merging:
- your main manifest
- flavor manifests
- build type manifests
- library manifests
So if even one activity, service, or receiver with an intent-filter is missing android:exported, the build fails.
In the provided manifests, one clear example is:
<activity
android:name=".ui.EasyOnboardingInviteActivity"
android:label="@string/invite_to_app"
android:launchMode="singleTask" />
If this activity has or receives an intent-filter through merging, or if another component elsewhere is missing the attribute, the app will still fail.
More importantly, the main lesson is:
- updating only the main manifest is not enough
- you must inspect all merged manifests
- every component with an
intent-filtermust explicitly setandroid:exported
Rule of thumb
If a component has an intent-filter, always write android:exported explicitly.
- launcher activity → usually
true - deep link handler → usually
true - boot receiver listening for system broadcasts → usually
true - internal-only components → usually
falseif they do not need external access
Mental Model
Think of your app manifest as a building directory.
Each component is like a room:
- an
activityis a room users can enter - a
serviceis a room where background work happens - a
receiveris a room that reacts when something happens
An intent-filter is like putting a sign outside the building saying:
- "Open this room for launch"
- "Send files here"
- "Open links here"
In Android 12+, if you put up that sign, Android asks:
Should people outside the building be allowed in or not?
That is what android:exported answers.
true= yes, this room is visible from outsidefalse= no, this room is internal only
If you do not answer, Android refuses to build the app because the entry point is ambiguous.
Syntax and Examples
Basic syntax
Activity
<activity
android:name=".ui.SplashActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
Service
<service
android:name=".services.ContactChooserTargetService"
android:permission="android.permission.BIND_CHOOSER_TARGET_SERVICE"
android:exported="true">
<intent-filter>
<action android:name="android.service.chooser.ChooserTargetService" />
</intent-filter>
</service>
Broadcast receiver
<receiver
android:name=".services.EventReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
Example of the issue
This is invalid for Android 12+ if an intent-filter is present:
<activity
android:name=".ui.ImportBackupActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
It must become something like:
<activity
android:name=".ui.ImportBackupActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
In your case
Your second manifest contains an activity without android:exported:
<activity
android:name=".ui.EasyOnboardingInviteActivity"
android:label="@string/invite_to_app"
android:launchMode="singleTask" />
It should be updated to explicitly include the attribute, for example:
<activity
android:name=".ui.EasyOnboardingInviteActivity"
android:label="@string/invite_to_app"
android:launchMode="singleTask"
android:exported="false" />
Use false only if no other app should launch it. If it is intended to be opened externally, use true instead.
Beginner rule
If a component has an intent-filter, always choose one:
android:exported="true"
or
android:exported="false"
Step by Step Execution
Small example
Suppose you have two manifest files.
Main manifest
<application>
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
Flavor manifest
<application>
<activity
android:name=".ShareActivity">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
</application>
What happens during build
- Gradle starts building the selected variant.
- Android merges all manifest files for that variant.
- It sees
MainActivityhas anintent-filterand already hasandroid:exported="true". - It then sees
ShareActivityhas anintent-filterbut noandroid:exported. - Manifest merge fails.
- The whole app does not compile, even though the main manifest looked correct.
Fix
Update the second manifest:
<application>
<activity
android:name=".ShareActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
</application>
How to debug this in Android Studio
- Open the manifest file.
- Switch to the Merged Manifest view.
- Search for components with
intent-filter. - Check whether each one has
android:exported. - Also inspect library and flavor manifests.
This is the key idea: the real error may come from a different manifest than the one you edited.
Real World Use Cases
Where this matters in real apps
Launcher activities
Your app's launcher screen usually has:
MAINLAUNCHER
That activity must normally be:
android:exported="true"
Deep linking
If your app opens web links such as:
https://example.com/...- custom schemes like
myapp://...
the responsible activity needs an intent-filter, which usually means it must be exported.
Share targets
If your app appears in Android's Share menu, the receiving activity often uses SEND or SEND_MULTIPLE, so it is typically exported.
Boot receivers
A receiver that listens for BOOT_COMPLETED must be visible to the system, so it usually needs:
android:exported="true"
Internal screens
Activities opened only from inside your own app usually do not need an intent-filter, and often should be:
android:exported="false"
or omit the intent-filter entirely if external launching is unnecessary.
Real Codebase Usage
How developers handle this in real projects
1. Explicitly set android:exported on all entry points
Teams often make this a code review rule:
- every component with
intent-filtermust declareandroid:exported - avoid relying on old defaults
2. Keep internal components private
If an activity does not need to be launched from outside the app:
- remove unnecessary
intent-filter - set
android:exported="false"
This reduces accidental exposure.
3. Use merged manifest inspection during debugging
When build variants, product flavors, or libraries are involved, developers check:
src/main/AndroidManifest.xmlsrc/debug/...src/release/...- flavor manifests
- third-party library manifests
- Android Studio's Merged Manifest tab
4. Treat exported components as public API
If a component is exported, developers usually add:
- input validation
- permission checks
- safe intent parsing
- guard clauses against missing extras
For example, an exported deep-link activity should not assume all incoming data is valid.
5. Use guard clauses in code
If an exported activity reads extras from an incoming intent, developers often write defensive code such as:
val userId = intent?.getStringExtra("user_id")
if (userId.isNullOrBlank()) {
finish()
return
}
This matters because exported components may receive unexpected input from other apps or the system.
Common Mistakes
1. Updating only one manifest file
In multi-flavor Android projects, there may be several manifest files.
Broken assumption
"I already added android:exported everywhere."
But only the main manifest was updated.
Fix
Check all manifests used by the selected build variant.
2. Forgetting that services and receivers also count
Beginners often update only activities.
Broken code
<receiver android:name=".BootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
Fix
<receiver
android:name=".BootReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
3. Choosing true for everything
That may compile, but it is not always safe.
Problem
Exporting unnecessary components increases your app's public surface area.
Better approach
- use
trueonly when outside access is needed - use
falsefor internal components
4. Missing the actual component mentioned by the merged manifest
Sometimes the error message shown in the editor is short or unclear.
Fix
Open the Merged Manifest tab and inspect the exact component.
5. Assuming libraries cannot cause this
Some dependencies include manifest entries.
Fix
If the missing attribute comes from a library manifest, you may need to:
- update the library
- override the component in your own manifest
- inspect merge output carefully
6. Adding android:exported to components without understanding intent filters
The Android 12 requirement mainly matters when a component has an intent-filter.
That said, being explicit is still good practice.
7. Missing this activity in your second manifest
In your provided manifests, this entry stands out:
<activity
android:name=".ui.EasyOnboardingInviteActivity"
android:label="@string/invite_to_app"
android:launchMode="singleTask" />
If this activity is part of the final merged manifest and needs explicit export behavior, add the attribute:
<activity
android:name=".ui.EasyOnboardingInviteActivity"
android:label="@string/invite_to_app"
android:launchMode="singleTask"
android:exported="false" />
Comparisons
android:exported="true" vs android:exported="false"
| Setting | Meaning | Typical use case | Risk level |
|---|---|---|---|
true | Other apps or the system can launch/interact with the component | Launcher activity, deep links, share targets, boot receivers | Higher if input is not validated |
false | Only your app can use the component directly | Internal screens, internal services, private receivers | Lower |
Component with intent filter vs without intent filter
| Component type | Needs explicit android:exported on Android 12+? | Notes |
|---|---|---|
Component with intent-filter | Yes | Required when targeting API 31+ |
Component without intent-filter | Not required by this specific rule | Still okay to be explicit |
Main manifest vs merged manifest
| Manifest view | What it shows | Why it matters |
|---|---|---|
| Main manifest | Only one source file | May look correct while build still fails |
| Merged manifest | Final combined result from all sources | Best place to find the real problem |
Fixing your own code vs updating a dependency
| Source of problem | Typical fix |
|---|---|
| Your manifest file | Add android:exported directly |
| Flavor/build-type manifest | Update that specific manifest |
| Library manifest | Update dependency or override manifest entry in your app |
Cheat Sheet
Quick rules
- Targeting Android 12+ (
targetSdkVersion 31or higher)? - Any
activity,service, orreceiverwith anintent-filtermust explicitly declareandroid:exported.
Common values
android:exported="true"
android:exported="false"
Usually use true for
- launcher activities
- deep link handlers
- share targets
- broadcast receivers handling system intents
- services intended for system or external interaction
Usually use false for
- internal-only activities
- private services
- internal receivers
Debug checklist
- Open Merged Manifest.
- Search for
intent-filter. - For each matching component, confirm
android:exportedexists. - Check:
- main manifest
- flavor manifests
- debug/release manifests
- library manifests
- Rebuild project.
Example fix
<activity
android:name=".ui.SomeActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
Important edge case
If you already fixed the main manifest but the build still fails, the missing android:exported is probably in:
- another manifest file
- a product flavor manifest
- a library manifest
In the provided code
A likely missing entry is:
<activity
android:name=".ui.EasyOnboardingInviteActivity"
android:label="@string/invite_to_app"
android:launchMode="singleTask"
android:exported="false" />
FAQ
Why do I get android:exported errors only after upgrading to Android 12?
Because Android 12 introduced a stricter rule for apps targeting API 31+: components with intent-filter must explicitly declare android:exported.
Does every activity need android:exported?
Not necessarily. The Android 12 requirement applies specifically to components with intent-filter. Still, being explicit can improve clarity.
Should I set android:exported="true" on all activities?
No. Only components that should be reachable from outside your app should be exported. Internal components should usually be false.
Why does the build still fail even though I updated my main manifest?
Because Android merges multiple manifests. Another manifest file or a library manifest may still contain a component missing android:exported.
How can I find the exact component causing the error?
Use Android Studio's Merged Manifest view and search for components with intent-filter but no android:exported.
Do services and receivers also need this attribute?
Yes. Activities, services, and receivers with intent-filter must declare it when targeting Android 12+.
Can a third-party library cause this manifest error?
Yes. If a library contributes a component without the required attribute, manifest merge can fail.
What should I set for launcher activities?
Usually android:exported="true", because the system launcher must be able to open them.
Mini Project
Description
Create a small Android app manifest setup that demonstrates how android:exported works across multiple manifest files. The project will simulate a common real-world case: one launcher activity in the main manifest and one share activity in a secondary manifest. This helps you practice finding and fixing manifest merge issues before they appear in a larger codebase.
Goal
Build and fix a multi-manifest Android setup so that every component with an intent-filter explicitly declares the correct android:exported value.
Requirements
- Create a main manifest with a launcher activity that includes
MAINandLAUNCHER. - Create a second manifest file with an activity that handles
SENDintents. - Intentionally omit
android:exportedfirst, then identify the merge error. - Update the manifest so both activities compile correctly.
- Choose
trueorfalsebased on whether the component should be externally accessible.
Keep learning
Related questions
Accessing Kotlin Extension Functions from Java
Learn how Kotlin extension functions are compiled and how to call them correctly from Java with clear examples and common pitfalls.
Add a Maven Repository URL in Gradle Kotlin DSL (build.gradle.kts)
Learn how to add Maven Central and a custom Maven repository URL in Gradle Kotlin DSL using build.gradle.kts syntax.
Allow HTTP and HTTPS in Android 9 Pie with Network Security Configuration
Learn how Android 9 Pie handles cleartext HTTP traffic and how to allow HTTP and HTTPS safely using network security config.