普通はあまり使わないからでしょうか、
前回の記事 がらみで、 AndroidManifest.xml に指定する <intent-filter> の "android:priority" 属性について調べていたのですが、あまりちゃんとした情報がなかったのでメモ。
<intent-filter> は指定した Activity, Service, Broadcast Receiver がどのような Intent を受け取ることが出来るかどうか、という指定で、ここにアクションの種類を識別する文字列や、対応するファイル形式を指定するわけですが、これには "android:priority", その名の通り優先順位を設定することが可能です。
一応、
公式のドキュメント では次のように説明されています。
android:priority
- The priority that should be given to the parent component with regard to handling intents of the type described by the filter. This attribute has meaning for both activities and broadcast receivers:
Use this attribute only if you really need to impose a specific order in which the broadcasts are received, or want to force Android to prefer one activity over others.
The value must be an integer, such as "100
". Higher numbers have a higher priority.
android:priority
- このフィルタに記述された種類のインテントを処理する場合の、親コンポーネントの優先順位。
この属性は Activity と Broadcast Receiver の両方に意味があります。
この属性はあなたが本当に特定の実行順序を Broadcast Receiver に強制したい場合、または Android が特定の Activity を他の Activity より優先することを強制したい場合のみに使用してください。
値は "100
" のような整数値で、
大きい値ほど高い優先順位を持ちます。
かなり簡単にさらーっと書かれているんですが、三つほど大事なことが抜け落ちています。一つが最大値、一つが Service に対する挙動、一つが Activity に対する制限です。
優先順位の最大値
これはまあ、仕様上設定されていると言うだけのことで、違反したから何か罰があるわけでもなさそうですが、一応 android:priority 値には最大値が存在します。
最大値と言うよりは、一般的なアプリケーションよりシステムが先に Intent を受け取りたいとき、この "1000" という値を使用するから、一般的なアプリケーションは 1000 未満の値を使用してくれ、というのがより正確でしょうか。従って、通常の最大値は "999" ということになります。
Service に対する挙動
前掲のドキュメントでは Activity 及び Broadcast Receiver についてしか書かれていませんでしたが、 <service> に <intent-filter> が書けるのと同様、もちろん Service に対しても android:priority 値は効果を持ちます。
そもそも Service 自体を暗黙的インテント (クラスを指定しないインテント) で呼び出す場合が少ないからなのかもしれませんが、このときその暗黙的インテントに合致する Service が複数有った場合、 startService や bindService はより優先順位の高い Service を選択するようになります。
Activity に対する制限
で、コレが一番問題かもしれませんが、ドキュメントでは Activity に設定すれば優先順位を上げて他の Activity より優位を強制できる!と書いてあるにもかかわらず、通常のアプリでは Activity に対して android:priority を指定する意味が殆どありません。なぜなら、システム ROM に書き込まれたシステムアプリでない場合、 0 より大きい android:priority 値を設定できないからです。
この事実は前掲のドキュメントでは何も教えてくれなくて寂しいのですが、 Android のソースコードを見てみるとすぐに分かります。
PackageManagerService.addActivity の部分 に、次のようなコードがあるからです。
if (!systemApp && intent.getPriority() > 0 && "activity".equals(type)) {
intent.setPriority(0);
Log.w(TAG, "Package " + a.info.applicationInfo.packageName + " has activity "
+ a.className + " with priority > 0, forcing to 0");
}
ごらんの通り、もしその Activity がシステムアプリ (
ApplicationInfo.FLAG_SYSTEM を持つアプリ)のものでなく、優先順位が 0 より大きく設定されていたら、強制的に 0 にしてしまっています。試しに適当なアプリを作って試せば、
"Package test.app has activity test.app.MainActivity with priority > 0, forcing to 0"
というような Logcat 警告を残して、結局 android:priority 値は無視されてしまうことが分かります。
この制限は、多分セキュリティ上のうんたらかんたら、といった理由で課せられているのでしょうけれども、おかげでユーザの操作によらずに飛び交う暗黙的インテントは、基本的にシステムの Google 謹製アプリが受け取ってしまい、自家製アプリでごにょごにょすることが不可能ということになります。
そういうわけで、基本的には sendOrderedBroadcast による Broadcast を受け取る際の優先順位か、暗黙的インテントによる Service 起動の優先順位でしか android:priority の意味はありません。
(Broadcast Receiver, Service については FLAG_SYSTEM 制限はありません)
昨日の話との関連
以上で終わりですが、昨日の話との関連。
自前で ROM をビルドすればどうとでもできるんでしょうけど、どうせなら一般のアプリで Bluetooth HFP のメモリダイヤル機能をなんとかインチキできないかなーと考えていたら、 Bluetooth ヘッドセットの処理は Phone.apk に実装されていて Service として公開されているんですね。で、システムの Bluetooth サービスが "android.bluetooth.IBluetoothHeadset" という暗黙的インテントでこれを呼び出す実装になってるわけです。
ということは、同じ <intent-filter> でより優先順位の高い BluetoothHeadsetService を実装すれば、もしかしたらごまかせるのかもなーと思って調べてました。
一応、 BluetoothHeadsetService を乗っ取ることは可能なようですが、それ以上はちょっと時間が無くて調べてはおりません。