Package visibility in Android 11

Android 11 changes how apps can query and interact with other installed apps on the same device. If your app targets Android 11, you might need to add a <queries> element in your app's manifest file so that the system knows which other apps to show to your app.

The <queries> element allows you to describe which other apps your app might need to interact with. You can specify apps by package name or by intent signature within a single <queries> element.

The PackageManager methods that return results about other apps, such as queryIntentActivities(), are filtered based on the calling app's <queries> declaration. Explicit interactions with other apps, such as startService(), also require the target app to match one of the declarations in <queries>.

We're interested in hearing your feedback! Please take this short survey to let us know how you're using the feature. In particular, tell us about use cases impacted by this feature.

Query and interact with specific packages

If you know the specific set of apps that you want to query or interact with, such as apps that integrate with your app, or apps whose services you use, include their package names in a set of <package> elements inside the <queries> tag:

<manifest package="com.example.game">
    <queries>
        <package android:name="com.example.store" />
        <package android:name="com.example.services" />
    </queries>
    ...
</manifest>

Query and interact with apps given an intent filter

Your app might need to query or interact with a set of apps that serve a particular purpose, but you might not know the specific package names to include. In this situation, you can list intent filter signatures in your <queries> tag. Your app can then discover apps that have matching <intent-filter> tags.

The following example allows your app to see installed apps that support JPEG image sharing:

<manifest package="com.example.game">
    <queries>
        <intent>
            <action android:name="android.intent.action.SEND" />
            <data android:mimeType="image/jpeg" />
        </intent>
    </queries>
    ...
</manifest>

The <intent> element has a few restrictions:

  • You must include exactly one <action> element.
  • You cannot use the path, pathPrefix, pathPattern, or port attributes in a <data> element. The system behaves as if you set each attribute's value to the generic wildcard character (*).
  • You cannot use the mimeGroup attribute of a <data> element.
  • Within the <data> elements of a single <intent> element, you can use each of the following attributes at most once:

    • mimeType
    • scheme
    • host

    You can distribute these attributes across multiple <data> elements or use them in a single <data> element.

The <intent> element supports the generic wildcard character (*) as the value for a few attributes:

  • The name attribute of the <action> element.
  • The subtype of the mimeType attribute of a <data> element (image/*).
  • The type and subtype of the mimeType attribute of a <data> element (*/*).
  • The scheme attribute of a <data> element.
  • The host attribute of a <data> element.

Unless otherwise specified in the previous list, the system doesn't support a mix of text and wildcard characters, such as prefix*.

Query and interact with all apps

In rare cases, your app might need to query or interact with all installed apps on a device, independent of the components they contain. An example is an app store like Google Play. To allow your app to see all other installed apps, Android 11 introduces the QUERY_ALL_PACKAGES permission.

Note: In the vast majority of cases, it's possible to fulfill your app's use case by declaring the <queries> tag.

In upcoming versions of the Developer Preview, look for Google Play to provide guidelines for apps that need this permission.

Log messages for package filtering

To discover more details about how the changes to package visibility affect your app, you can enable log messages for package filtering. If you're developing a test app or debuggable app in Android Studio, this capability is enabled for you. Otherwise, you can run the following command in a terminal window to enable it manually:

adb shell pm log-visibility --enable your-package-name

Then, whenever packages are filtered out of a PackageManager object's return values, you see a message similar to the following in Logcat:

I/AppsFilter: interaction: PackageSetting{7654321 \
  com.example.myapp/12345} -> PackageSetting{...} BLOCKED

Test the change

To test whether this behavior change has taken effect in your app, complete the following steps:

  1. Install Android Studio 3.6.1 or higher.
  2. Install the latest version of Gradle that Android Studio supports.
  3. Set your app's targetSdkVersion to 'R'.
  4. Don't include the <queries> element in your app's manifest file.
  5. Call getInstalledApplications() or getInstalledPackages(). Both methods should return a filtered list.
  6. See which features of your app aren't working.
  7. Introduce appropriate <queries> entries to fix these features.

Use cases that aren't affected by the change

The following list includes several examples of use cases that don't require a <queries> declaration:

  • The target app is your own app.
  • You use an implicit intent to start an activity. Your app might restrict how it uses implicit intents to interact with other apps.
  • Your app interacts with certain system packages, such as the media provider, that implement core Android functionality.
  • Another app expects a result from your app. This situation applies when your app is a content provider, when the other app invokes your app by calling startActivityForResult(), and when your app is a service that the other app tries to start or connect to.

For example, if another app makes a request to a content provider in your app, the system allows your app to see that other app.

Add restrictions to activity starts

Android 11 adds several flags that allow you to specify when a call to startActivity() from your app should result in an ActivityNotFoundException, rather than having another app fulfill the intent. By using these flags, you shouldn't need to call resolveActivity() or queryIntentActivities().

These flags are particularly useful for web intents. When your app wants to deep-link into an installed app and one isn't available, your app can handle the intent itself by loading the URL in a custom tab or an in-app browser.

Launch a web intent in a non-browser app

When you include the FLAG_ACTIVITY_REQUIRE_NON_BROWSER intent flag, the intent is started only in the following cases:

  • A non-browser app would handle the intent directly.
  • The user can choose a non-browser app in the disambiguation dialog.

Otherwise, an ActivityNotFoundException is thrown, and your app is expected to handle the intent itself.

Kotlin

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.
    tryLoadInCustomTabs(url) // Or use an in-app browser.
}

Java

try {
    Intent intent = new Intent(ACTION_VIEW, Uri.parse(url));
    // The URL should either launch directly in a non-browser app (if it's the
    // default), or in the disambiguation dialog.
    intent.addCategory(CATEGORY_BROWSABLE);
    intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_REQUIRE_NON_BROWSER);
    startActivity(intent);
} catch (ActivityNotFoundException e) {
    // Only browser apps are available, or a browser is the default.
    tryLoadInCustomTabs(url); // Or use an in-app browser.
}

Require only one matching activity

When you include the FLAG_ACTIVITY_REQUIRE_DEFAULT intent flag, your app's intent is invoked when only a single app on the device can handle it, or when an app is the default handler for that intent. This flag reduces friction by not showing the disambiguation dialog and instead allowing the app to choose how content appears for the user.

Kotlin


val url = url-to-load
try {
    // In order for this intent to be invoked, the system must directly launch a
    // non-browser app.
    val intent = Intent(ACTION_VIEW, Uri.parse(url).apply {
        addCategory(CATEGORY_BROWSABLE)
        flags = FLAG_ACTIVITY_NEW_TASK or FLAG_REQUIRE_NON_BROWSER or
                FLAG_ACTIVITY_REQUIRE_DEFAULT
    }
    startActivity(intent)
} catch (e: ActivityNotFoundException) {
    // This code executes in one of the following cases:
    // 1. Only browser apps can handle the intent.
    // 2. The user has set a browser app as the default app.
    // 3. The user hasn't set any app as the default for handling URLs.
    tryLoadInCustomTabs(url)
}

Java

String url = url-to-load;
try {
    // In order for this intent to be invoked, the system must directly launch a
    // non-browser app.
    Intent intent = new Intent(ACTION_VIEW, Uri.parse(url));
    intent.addCategory(CATEGORY_BROWSABLE);
    intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_REQUIRE_NON_BROWSER |
            FLAG_ACTIVITY_REQUIRE_DEFAULT);
    startActivity(intent);
} catch (ActivityNotFoundException e) {
    // This code executes in one of the following cases:
    // 1. Only browser apps can handle the intent.
    // 2. The user has set a browser app as the default app.
    // 3. The user hasn't set any app as the default for handling URLs.
    tryLoadInCustomTabs(url);
}