Illustration by Molly Hensley

Package visibility in Android 11

Yacine Rezgui
Jun 30, 2020 · 3 min read

On Android 10 and earlier, apps could query the full list of installed apps on the system using methods like queryIntentActivities(). In most cases, this is far broader access than is necessary for an app to implement its functionality. With our ongoing focus on privacy, we’re introducing changes on how apps can query and interact with other installed apps on the same device on Android 11. In particular, we’re bringing better scoped access to the list of apps installed on a given device.

To provide better accountability for access to installed apps on a device, apps targeting Android 11 (API level 30) will see a filtered list of installed apps by default. In order to access a broader list of installed apps, an app can specify information about apps they need to query and interact with directly. This can be done by adding a <queries> element in the Android manifest.

For most common scenarios, including any implicit intents started with startActivity(), you won’t have to change anything! For other scenarios, like opening a specific third party application directly from your UI, developers will have to explicitly list the application package names or intent filter signatures like this:

<manifest package="com.example.game">
<queries>
<!-- Specific apps you interact with, eg: -->
<package android:name="com.example.store" />
<package android:name="com.example.service" />
<!--
Specific intents you query for,
eg: for a custom share UI
-->
<intent>
<action android:name="android.intent.action.SEND" />
<data android:mimeType="image/jpeg" />
</intent>
</queries>
...
</manifest>

If you use Custom Tabs to open URLs, you might be calling resolveActivity() and queryIntentActivities() in order to launch a non-browser app if one is available for the URL. In Android 11 there’s a better way to do this, which avoids the need to query other apps: the FLAG_ACTIVITY_REQUIRE_NON_BROWSER intent flag. When you call startActivity() with this flag, an ActivityNotFoundException will be thrown if a browser would have been launched. When this happens, you can open the URL in a Custom Tab instead.

try {
val intent = Intent(ACTION_VIEW, Uri.parse(url)).apply {
// The URL should either launch directly in a non-browser app
// (if it’s the default), or in the disambiguation dialog
addCategory(CATEGORY_BROWSABLE)
flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_REQUIRE_NON_BROWSER
}
startActivity(intent)
} catch (e: ActivityNotFoundException) {
// Only browser apps are available, or a browser is the default app for this intent
}

In rare cases, your app might need to query or interact with all installed apps on a device, independent of the components they contain. To allow your app to see all other installed apps, Android 11 introduces the QUERY_ALL_PACKAGES permission. In an upcoming Google Play policy update, look for guidelines for apps that need the QUERY_ALL_PACKAGES permission.

When targeting API level 30 and adding a <queries> element to your app, use the latest available release of the Android Gradle plugin. Soon we’ll be releasing updates to older Android Gradle plugin versions to add support for this element. You can find more information and use cases about Package Visibility in the developer documentation.