Let’s be explicit about our intent(-filters)

Nicole Borrelli
Feb 18 · 4 min read
Image for post
Image for post
Illustration by Molly Hensley

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

⚠️ 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

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

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!

Android Developers

The official Android Developers publication on Medium

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store