Let’s be explicit about our intent(-filters)
By: Nicole Borrelli
Tags: Latest, Security, Android App Development, Android, Featured

An important change is coming to Android 12 that improves both app and platform security. This change affects all apps that target Android 12.

Activities, services, and broadcast receivers with declared intent-filters now must explicitly declare whether they should be exported or not.

❗️If your app fails with one of these error messages, it’s most likely related to this change.

Installation did not succeed.

The application could not be installed: INSTALL_FAILED_VERIFICATION_FAILURE

List of apks:

[0] ‘…/build/outputs/apk/debug/app-debug.apk’

Installation failed due to: ‘null’

Or

INSTALL_PARSE_FAILED_MANIFEST_MALFORMED: Failed parse during installPackageLI: /data/app/vmdl538800143.tmp/base.apk (at Binary XML file line #…): com.example.package.ActivityName: Targeting S+ (version 10000 and above) requires that an explicit value for android:exported be defined when intent filters are present”

The Fix

The fix for these errors is to add the attribute android:exported to any <activity> , <activity-alias> , <service> , or <receiver> components that have <intent-filter> s declared in the app’s AndroidManifest.xml file.

⚠️ Do not just add android:exported=”true” to all of these components! Examine each component that includes an <intent-filter> definition and ask yourself, “Do I want any app installed on someone’s device to be able to start this component?”

The answer to this depends on what the app does, how other apps interact with it, and other app specific considerations. Here are a few common examples of common intent-filters with a suggested value and explanation of why it was chosen:

Activity with <category android:name=”android.intent.category.LAUNCHER” /> : android:exported=”true”

This is probably your app’s MainActivity , and since the launcher on Android could be a regular application, this activity has to be exported or the launcher won’t be able to start it.

Activity with <action android:name=”android.intent.action.VIEW” /> : android:exported=”true”

This activity is responsible for handling the “open with” action from other apps.

Activity with <action android:name=”android.intent.action.SEND” /> or <action android:name=”android.intent.action.SEND_MULTIPLE”/> : android:exported=”true”

This activity is responsible for handling the content that other apps share with it. See Receiving simple data from other apps.

Service with <action android:name=”android.media.browse.MediaBrowserService” /> : android:exported=”true”

If this is a service that exposes the app’s media library to other apps, then it should be exported to allow other apps to connect and browse that library. The service likely extends, either directly or indirectly, MediaBrowserServiceCompat . If it does not, then it may not be necessary to export it.

Service with <action android:name=”com.google.firebase.MESSAGING_EVENT” /> : android:exported=”false”

This is the service that Firebase Cloud Messaging will invoke and should extend FirebaseMessagingService . This service should not be exported, as Firebase will be able to start the component whether it’s exported or not. For more information, see Set up a Firebase Cloud Messaging client app on Android.

Receiver with <action android:name=”android.intent.action.BOOT_COMPLETED” /> : android:exported=”false”

Because the system is delivering this action to the broadcast receiver it can do so whether it is exported or not.

Background

Prior to Android 12, components (activites, services, and broadcast receivers only) with an intent-filter declared were automatically exported

This activity is exported by default:

<activity android:name=".MainActivity">

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER"

</intent-filter>

</activity>

This activity is not exported:

<activity android:name=".MainActivity" />

While this may seem like a reasonable default, mistakes here can leave an app vulnerable. For example, let’s say our app had an activity to playback videos:

<activity android:name=”.PlayVideoActivity” />

Later we find that we’re linking to this activity explicitly from many places, and so, in order to have our app be more loosely-coupled, we change our activity to include an intent-filter to allow the system to select the activity:

<activity android:name=”.PlayVideoActivity”>

<intent-filter>

<action android:name=”android.intent.action.VIEW” />

<data

android:mimeType=”video/*”

android:scheme=”content” />

</intent-filter>

</activity>

Suddenly we have a problem. Our activity, which was only designed to be used internally by our app, is now exported!

Once we target Android 12, the system will prevent this by requiring us to be explicit about the value for android:exported. In this case we don’t want it to be exported, so we can set that attribute and keep our app safe.

<activity

android:name=”.PlayVideoActivity”

android:exported=”false”>

<intent-filter>

<action android:name=”android.intent.action.VIEW” />

<data

android:mimeType=”video/*”

android:scheme=”content” />

</intent-filter>

</activity>

TL;DR

An important change is coming in Android 12 to improve security. Apps that target that version will need to explicitly declare a value for the android:exported attribute of any activity , activity-alias , service , or broadcast receiver that includes an intent-filter in its AndroidManifest.xml file. If it does not, the app will fail to be installed.

Carefully consider what value to set this attribute to, and when in doubt, favor setting android:exported=”false” .

For more information about intents and intent-filters, see Receiving an implicit intent.

For more information on other updates for security and privacy, see this page.

For more information on all of the changes coming in Android 12, check out the blog post for Developer Preview 1!