The Quick Developers Guide to Migrate Their Apps to Android 11

Earlier, apps could query the full list of installed apps on the system using methods like queryIntentActivities(). Mostly, I was far broader access you what you actually require.

With Android 11, this is no longer the case. Using the <queries> element, apps can define the set of other packages that they can access. This element helps encourage the principle of least privilege by telling the system which other packages to make visible to your app.

If your app targets Android 11 or higher, you might need to add the <queries> element in your app’s manifest file. Within the <queries> element, you can specify packages by name, by intent signature, or by provider authority.

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

Example

Suppose you want to send an email, you have to create an Intent for it and let the system find an appropriate app to handle the request.

val intent = Intent().apply {
action = Intent.ACTION_SENDTO
data = (Uri.parse("mailto:[email protected]"))
putExtra(Intent.EXTRA_EMAIL, "Hi! How are you?")
putExtra(Intent.EXTRA_SUBJECT, "Testing Package Visibility")
}startActivity(intent)

This code will work fine (even on the API level 30) if the email app is installed on the device. But what if there are no apps that can handle this request? — The app will crash by throwing ActivityNotFoundException.

A safe option is to first check that if we have at least one app which can handle this request. So we could do something like:

// It will always return null on Android 11, unless <queries> specified in manifest!if (intent.resolveActivity(packageManager) == null) {
toast("No app found to handle this request")
} else {
startActivity(intent)
}

Now, this is where your code will fail, as your app is not allowed to query other installed apps. Here, in order for your code to work, you will have to add <queries> in your app’s manifest file and declare your intent.

App’s Manifest file with <queries> element

Custom Tabs

When using Custom Tabs to open a Web URL, it is possible that there may be some non-browser apps that can also handle that URL. It is recommended that you allow the non-browser app to handle that URL first.

FLAG_ACTIVITY_REQUIRE_NON_BROWSER can be used here to query Non-Browser apps only.

To learn more about it, refer to “Configuring package visibility based on use cases” from Android Documentation.

Important Point

The system makes some apps visible to your app automatically, but it hides other apps by default. Generally, these are apps that are linked to your app in some ways, like, Any app that starts or binds to a service in your app, access a content provider in your app, whose content provider your app accesses, etc.

You can start another app’s activity using either an implicit or explicit intent, regardless of whether that other app is visible to your app. You just cannot query the intent.

What do I do if my App is an App Launcher or similar, where I need to know all the apps that are installed on the device?

To allow your app to see all other installed apps, the system provides the QUERY_ALL_PACKAGES permission.

<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>

This permission is appropriate for apps that genuinely need this permission like a Launcher App, a Security App, Device Management App, Accessibility App, etc.

In an upcoming policy update, look for Google Play to provide guidelines for apps that need the QUERY_ALL_PACKAGES permission.