Question

How to Fix android:exported Errors in Android 12 Manifest Files

kotlinjavaandroidandroid-manifestandroid-12

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 31
  • targetSdkVersion 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:

  • activity
  • service
  • receiver
  • sometimes other manifest-declared app entry points

Example meanings:

  • android:exported="true" → other apps can start or interact with this component if allowed
  • android: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-filter must explicitly set android: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 false if they do not need external access

Mental Model

Think of your app manifest as a building directory.

Each component is like a room:

  • an activity is a room users can enter
  • a service is a room where background work happens
  • a receiver is 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 outside
  • false = no, this room is internal only

If you do not answer, Android refuses to build the app because the entry point is ambiguous.

Take Quiz

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

  1. Gradle starts building the selected variant.
  2. Android merges all manifest files for that variant.
  3. It sees MainActivity has an intent-filter and already has android:exported="true".
  4. It then sees ShareActivity has an intent-filter but no android:exported.
  5. Manifest merge fails.
  6. 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

  1. Open the manifest file.
  2. Switch to the Merged Manifest view.
  3. Search for components with intent-filter.
  4. Check whether each one has android:exported.
  5. 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:

  • MAIN
  • LAUNCHER

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.

Take Quiz

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-filter must declare android: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.xml
  • src/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 true only when outside access is needed
  • use false for 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"

SettingMeaningTypical use caseRisk level
trueOther apps or the system can launch/interact with the componentLauncher activity, deep links, share targets, boot receiversHigher if input is not validated
falseOnly your app can use the component directlyInternal screens, internal services, private receiversLower

Component with intent filter vs without intent filter

Component typeNeeds explicit android:exported on Android 12+?Notes
Component with intent-filterYesRequired when targeting API 31+
Component without intent-filterNot required by this specific ruleStill okay to be explicit

Main manifest vs merged manifest

Manifest viewWhat it showsWhy it matters
Main manifestOnly one source fileMay look correct while build still fails
Merged manifestFinal combined result from all sourcesBest place to find the real problem

Fixing your own code vs updating a dependency

Source of problemTypical fix
Your manifest fileAdd android:exported directly
Flavor/build-type manifestUpdate that specific manifest
Library manifestUpdate dependency or override manifest entry in your app
Take Quiz

Cheat Sheet

Quick rules

  • Targeting Android 12+ (targetSdkVersion 31 or higher)?
  • Any activity, service, or receiver with an intent-filter must explicitly declare android: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

  1. Open Merged Manifest.
  2. Search for intent-filter.
  3. For each matching component, confirm android:exported exists.
  4. Check:
    • main manifest
    • flavor manifests
    • debug/release manifests
    • library manifests
  5. 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.

Take Quiz

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 MAIN and LAUNCHER.
  • Create a second manifest file with an activity that handles SEND intents.
  • Intentionally omit android:exported first, then identify the merge error.
  • Update the manifest so both activities compile correctly.
  • Choose true or false based on whether the component should be externally accessible.
Take Quiz

Keep learning

Related questions

View more