6 月 3 日の「#Android11: The Beta Launch Show」にぜひご参加ください。

Android 11 でのパッケージの公開設定

Android 11 では、同じデバイス上にインストール済みの別のアプリに対して、アプリがクエリまたは操作を行う方法が変更されています。アプリが Android 11 をターゲットとする場合、アプリのマニフェスト ファイルに <queries> 要素を追加して、そのアプリに表示するのは別のどのアプリかをシステムに通知することが必要になる可能性があります。

<queries> 要素を使用すると、アプリから操作が必要になる可能性のある別のアプリを指定できます。複数のアプリのパッケージ名またはインテント シグネチャを 1 つの <queries> 要素に指定できます。

別のアプリに関する結果を返す PackageManager のメソッド(queryIntentActivities() など)は、呼び出し元のアプリの <queries> の宣言に基づいてフィルタされます。別のアプリへの明示的な操作(startService() など)では、その対象アプリが <queries> 内の宣言のいずれかに一致することも必要です。

Google では皆様からのフィードバックをお待ちしております。こちらの簡単なアンケートにご協力ください。この機能をどのようにご利用かをお知らせいただければ幸いです。特に、この機能により影響を受けるユースケースについてお知らせください。

特定のパッケージに対するクエリと操作

アプリが別のアプリと統合される場合や、別のアプリのサービスを使用する場合など、アプリがクエリや操作を行う特定のアプリのセットについて把握している場合、そのパッケージ名を指定した <package> 要素のセットを <queries> タグに含めます。

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

インテント フィルタが指定されたアプリに対するクエリと操作

アプリが特定の目的を果たすアプリのセットに対してクエリや操作を行う必要があっても、指定するその個々のパッケージ名がわからない場合があります。その場合は、インテント フィルタのシグネチャのリストを <queries> タグに指定できます。それにより、アプリは一致する <intent-filter> タグを持つアプリを見つけることができます。

次の例でアプリは、JPEG 画像の共有をサポートするインストール済みのアプリを見つけることができます。

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

<intent> 要素には以下のような制限があります。

  • <action> 要素は 1 つだけ含める必要があります。
  • <data> 要素内で pathpathPrefixpathPatternport の各属性を使用することはできません。各属性の値を汎用のワイルドカード文字(*)に設定した場合と同じように動作することになります。
  • <data> 要素の mimeGroup 属性は使用できません。
  • 1 つの <intent> 要素の複数の <data> 要素にわたって、以下の各属性を 1 回だけ使用できます。

    • mimeType
    • scheme
    • host

    これらの属性は複数の <data> 要素に分散させることも、1 つの <data> 要素で使用することもできます。

<intent> 要素は、属性の値として以下の汎用のワイルドカード文字(*)をサポートしています。

  • <action> 要素の name 属性。
  • <data> 要素の mimeType 属性のサブタイプ(image/*)。
  • <data> 要素の mimeType 属性のタイプとサブタイプ(*/*)。
  • <data> 要素の scheme 属性。
  • <data> 要素の host 属性。

前のリストで特に指定されていない限り、システムはテキストとワイルドカード文字(prefix* など)の組み合わせに対応していません。

すべてのアプリに対するクエリと操作

まれなケースですが、アプリがデバイス上のインストール済みのアプリの「すべて」に対して、各アプリに含まれるコンポーネントによらず、クエリや操作を行うことが必要になる場合があります。Google Play のようなアプリストアがその一例です。Android 11 では、アプリがインストール済みの他のすべてのアプリを見つけられるように、QUERY_ALL_PACKAGES 権限が導入されました。

注: ほとんどの場合、<queries> タグを宣言することで、アプリのユースケースを実現できます。

デベロッパー プレビューの今後のバージョンでは、この権限が必要なアプリ向けのガイドラインを Google Play で提供する予定です。

パッケージ フィルタリングのログメッセージ

パッケージの公開設定の変更がアプリに及ぼす影響の詳細を確認するには、パッケージ フィルタリングのログ メッセージを有効にします。Android Studio でテストアプリやデバッグ可能なアプリを開発している場合は、この機能は自動的に有効になります。それ以外の場合は、ターミナル ウィンドウで次のコマンドを実行すると、手動で有効にできます。

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

その後で、パッケージが PackageManager オブジェクトの戻り値からフィルタされるたびに、Logcat に次のようなメッセージが表示されます。

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

変更をテストする

この動作の変更がアプリで有効になっているかどうかは、次の手順で確認できます。

  1. Android Studio 3.6.1 以上をインストールします。
  2. Android Studio がサポートしている最新バージョンの Gradle をインストールします。
  3. アプリの targetSdkVersion'R' に設定します。
  4. アプリのマニフェスト ファイルに <queries> 要素は含めないでください。
  5. getInstalledApplications() または getInstalledPackages() を呼び出します。どちらのメソッドも、フィルタされたリストを返します。
  6. アプリのどの機能が動作していなかを確認します。
  7. 適切な <queries> のエントリを含めて、その機能を修正してください。

変更の影響を受けないユースケース

<queries> の宣言を必要としないユースケースの例を以下に示します。

  • 自分のアプリを対象とする操作
  • 暗黙的インテントを使用してアクティビティを開始する場合。アプリが別のアプリを操作するのに暗黙的インテントを使用する場合その方法を制限することが考えられます。
  • メディア プロバイダなど、Android のコア機能を実装する特定のシステム パッケージをアプリが操作する場合。
  • アプリからの結果を別のアプリが期待する場合。この状況が当てはまるのは、アプリがコンテンツ プロバイダの場合、アプリが別のアプリから startActivityForResult() を使って呼び出される場合、アプリが別のアプリによって開始または接続されようとするサービスの場合です。

たとえば、アプリ内のコンテンツ プロバイダに別のアプリがリクエストする場合、アプリからそのリクエスト元のアプリを確認できます。

アクティビティの開始に制限を追加する

Android 11 には、アプリから startActivity() への呼び出しを ActivityNotFoundException にするタイミングを指定できる複数のフラグが追加されました。この場合、そのインテントを別のアプリが処理することはありません。これらのフラグを使用すると、resolveActivity()queryIntentActivities() を呼び出す必要はありません。

これらのフラグは、特にウェブ インテントの場合に便利です。アプリがディープリンクしようとするインストール済みのアプリを利用できないときに、アプリ自身でそのインテントを処理できます。処理するには、その URL をカスタムタブやアプリ内のブラウザに読み込みます。

ブラウザ以外のアプリでウェブ インテントを起動する

FLAG_ACTIVITY_REQUIRE_NON_BROWSER インテント フラグを含めたインテントは、以下の場合にのみ開始されます。

  • ブラウザ以外のアプリがインテントを直接処理する場合。
  • 曖昧さ回避のダイアログでユーザーがブラウザ以外のアプリを選択する場合。

上記以外では、ActivityNotFoundException がスローされ、そのアプリ自身がインテントを処理することが求められます。

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.
}

アクティビティの一致が 1 つだけ必要

FLAG_ACTIVITY_REQUIRE_DEFAULT インテント フラグを含めたインテントが処理されるのは、デバイス上でそれを処理できるアプリが 1 つだけの場合、またはそのインテントのデフォルト ハンドラのアプリがある場合のみです。このフラグを指定すると、曖昧さ回避のダイアログを表示しなくても、代わりにアプリがコンテンツをどのようにユーザーに表示するかを選択できるので、煩わしさを減らせます。

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);
}