Android 11 の Package Visibility に関する変更への対応
Android 11 でプライバシー周りの対応として、パッケージ可視性に関する変更が導入されました。
自アプリを Target SDK Version 30 (R) 以上にしなければ例え Android 11 端末でも影響はありません。
変更内容
対応なしに他アプリの情報を取得する API (PackageManager API) を利用すると、その API に応じて NameNotFoundException が投げられるかデータが存在しない扱いになります(そのアプリが API 結果から除外された List や null になる)。
「他アプリの情報を取得する」という定義がやや曖昧ですが、
- 自アプリの情報を取得する
- 暗黙的Intentを用いて
startActivity
をする - メディアプロバイダーなどのシステムアプリの情報取得
startActivityForResult
、ContentProvider、Service バインドなどによって自アプリが起動された場合
これらのケースでは対応の必要はありません。また最後の他アプリによって自アプリが起動するケースではその他アプリの情報を取得することができるようです。
対応策
- QUERY_ALL_PACKAGE パーミッションを追加する
- AndroidManifest に明示的に他アプリの情報を記述する
- [一部の人のみ] 新しい Intent Flag で該当 API の利用をやめる
QUERY_ALL_PACKAGE パーミッションを追加する
対応としては一番楽で早いのは間違いないんですが、このパーミッションを使うには後日公開されるガイドラインに準拠する必要があります。またすでに公式より例としてあがっているアプリを見る限り、P2P アプリを始めとしたこのパーミッションを使わないと成り立たないアプリが想定されています。多くのアプリではこのパーミッションの利用は選択肢にあがらないでしょうし、恐らく利用出来ないでしょう。
AndroidManifest に明示的に他アプリの情報を記述する
対応が必要なアプリの多くが取るであろう解決策です。取得したいアプリや検索条件となる Intent Filter を明示的に記述することで、該当するアプリに関してのみ情報を取得することが可能です。
<manifest package="com.example.game">
<queries>
<package android:name="com.example.store" />
<intent>
<action android:name="android.intent.action.SEND" />
<data android:mimeType="image/jpeg" />
</intent>
</queries>
...
</manifest>
manifest > queries
に記述します。記述出来る方式は2種類で、パッケージ名を直接指定する package
タグと Intent Filter で記述する intent
タグです。
package
タグの name 属性に正規表現や wildcard などは使えません。FQDN を指定する必要があります。
intent
の記法は従来の Intent Filter と異なる制約があります。
action
タグの指定は必須path
やpathPrefix
,pathPattern
,port
は指定不可- 1つの
data
タグはmimeType
、scheme
またはhost
をそれぞれ高々1つしか使えない
柔軟性のある記法は利用できない・・・かと思いきや一部 wildcard の利用が以下の属性で認められています。
action#name
,data#scheme
,data#host
data#mimeType
におけるimage/*
(subtype)data#mimeType
における*/*
(type と subtype)
ただし prefix*
のように前方一致のような混合記法は認められていません。
[一部の人のみ] 新しい Intent Flag で該当 API の利用をやめる
今回パッケージ可視性の変更にあたり、以下の Intent Flag が追加されました。
- FLAG_ACTIVITY_REQUIRE_NON_BROWSER
- FLAG_ACTIVITY_REQUIRE_DEFAULT
とはいうものの、今回追加された Intent Flag を利用することで本件に対応出来る人はそう多くはないかと思います。
FLAG_ACTIVITY_REQUIRE_NON_BROWSER を用いると、ブラウザアプリ以外で対象の Intent を開けるようになります。今まで手動で resolveActivities してチェックしていた場合はこちらに移すことが出来ます。
また FLAG_ACTIVITY_REQUIRE_DEFAULT を用いると、対象の Intent を開けるアプリが DEFAULT に設定されていない場合に例外を投げることが出来ます。したがって、Chooser を出したくないケースや Chooser を出すならその前に何か案内を出したいといったケースに有用です。公式の例題では FLAG_ACTIVITY_REQUIRE_NON_BROWSER と合わせることで、ブラウザアプリ以外のアプリが DEFAULT 指定されているケースのみ成功するという状態を作っています。
小ネタ
Chrome Custom Tab でだけ開くようにしたい
AndroidManifest に Intent Filter 用の query を書くことで実現出来ます。
<queries>
<intent>
<action android:name="android.support.customtabs.action.CustomTabsService" />
</intent>
</queries>
View アクションを使うのではなく、上記のように直接 Custom Tab に関する Action を指定することで Custom Tab の利用に制限することができます。特定の URL のみであれば data タグを足すなどして、制限を増やす必要があります。
Android TV 上での Intent
Android TV の中には com.google.android.tv.frameworkpackagestubs
という Intent を開く選択肢には現れるものの、ハンドリング出来ない package がいます。このパッケージを除外するには AndroidManifest へ package
タグの記述が必要になりそうです。(今もこの package が存在してるのか調査出来ていないので、詳しい人は教えてください)
[要追加検証] SDK が挿入してくる、いらない queries について
Google Issue Tracker にあげたところ、現在問題が発生していないとしてcloseとなりました。ref https://issuetracker.google.com/u/1/issues/159687397
2023/08/21現在、筆者による追加検証を行っていません。情報をお持ちの方はコメントを頂けると幸いです。
頑張って manifest merger を利用する必要があります。ところが現在は package
しか削除出来ません。
<queries>
<package android:name="com.android.chrome" tools:node="remove"/>
</queries>
上記のようにすることで package
の削除は可能でした。しかし Intent Filter には name 属性がないため、 適当に名前をつけても動作しませんでした。また action
の remove を行うと、当然ながら action
が必須である制約に引っかかり、ビルドが出来ませんでした。
雑感
端末にインストールされたアプリ一覧を収集されるのは気持ちのいいものではないので、今回の変更は割と納得の行くものでした。とはいえ、 package
タグにはもう少し柔軟な記法や何か良い感じの凄い技術でドメイン認証して自社アプリくらいは検索出来ると良かったのになと思わなくもないです。
アプリ開発をしていて、他アプリの情報をとりあえずなんでもいいから欲しい、という状況はそう多くありません。ただ一部認証系のライブラリでは Chrome Tab の利用や連携先アプリを検索したいと思うでしょうし、それが SDK のリリースをしないと対応出来ないというのは少し窮屈にも感じます。
弊社はどうあがいても QUERY_ALL_PACKAGE が必要なので、全てを諦めてガイドラインを待ちます。