Android 2.3.3 でのNFCの注意点

Nexus SがAPI Level 10の2.3.3にアップデートされたので、実機のNFC機能について調べてみたところ、いくつか落とし穴があったのでそれをメモとして記します。

ACTION_TAG_DISCOVERED は優先度が低く、届かない場合がある

公式のAPIリファレンス (NfcAdapter  |  Android Developers)に以下の記載があるように、先に ACTION_NDEF_DISCOVERED か ACTION_TECH_DISCOVERED のintentに反応するアプリが存在する場合には、ACTION_TAG_DISCOVERED のintentは発生しません。

public static final String ACTION_TAG_DISCOVERED

Since: API Level 9
Intent to start an activity when a tag is discovered.
This intent will not be started when a tag is discovered if any activities respond to ACTION_NDEF_DISCOVERED or ACTION_TECH_DISCOVERED for the current tag.
Constant Value: "android.nfc.action.TAG_DISCOVERED"

よって、API Level 9で作成したアプリは、API Level 10のアプリが存在する環境では、ほとんどの場合で intent を受け取れなくなる可能性があります。*1 広くICカードのタッチに反応したいという意図で ACTION_TAG_DISCOVERD を受け取るように AndoridManifest.xml に記述した場合でも同様です。

よって、広い種類のカードに反応するアプリを作りたい場合は、ACTION_TECH_DISCOVERD を使った intent-filter を記述した上で、そのアプリがどの種類のカードに対応しているかを明示的に(広く)宣言する必要があります。

API Level 9と10の両方で動作するアプリを作りたい場合には、AndroidManifest.xml に記載するのではなく、IntentFilterクラスを用いて intent-filter を登録することも検討してみた方が良いでしょう。

公式ドキュメントの間違い

APIリファレンスのみを見ていればこの罠には引っかかりにくいのですが、NFCについてのガイドドキュメントに間違いがあります(Near field communication overview  |  Android Developers)。

ACTION_TECH_DISCOVERED でのタグの記述位置について、ガイドドキュメントに問題があります。 タグは の外に記述しましょう。APIリファレンス(NfcAdapter  |  Android Developers)に記載の方法を参考にしてください。

正解
   <activity android:name=".nfc.TechFilter" android:label="NFC/TechFilter">
       <!-- Add a technology filter -->
       <intent-filter>
           <action android:name="android.nfc.action.TECH_DISCOVERED" />
           <!-- これも足しておく -->
           <category android:name="android.intent.category.DEFAULT" />
       </intent-filter>

       <!-- 正しい位置 -->
       <meta-data android:name="android.nfc.action.TECH_DISCOVERED"
           android:resource="@xml/filter_nfc"
       />
   </activity>

Javaリフレクションを利用したNFC勝手アプリは正常動作しない

まだちゃんと調べていないのですが、@hiddenなメソッドや変数などが変わっているようで、2.3〜2.3.2で使えていたリフレクションでのタグへの書き込みや、transieve でのコマンド送受信が正常動作しないようです。

NFCにとってAPI Level 9とはなんだったのか

前述の ACTION_TAG_DISCOVERED の優先順位の問題もあるので、API Level 9用に作られたNFC対応アプリは API Level 10環境では不遇です。ことNFCに関しては、とっととAPI Level 9を見捨てるのも一つの手かと思います。

しかし、これから少なくとも数ヶ月間、API Level 9な端末も市場に投入されるだろうというのも容易に予想されます。その中にNFCのハードウェアを持ったものもありそうですので、それらの端末をどう相手にするかということが悩ましい期間がありそうです。

幸い(?)、FeliCa対応の日本のAndroidおサイフケータイは、2.3になったとしてもNFCをサポートする可能性が低いと思われます。ABC 2011 Winter のLTの資料などにも記載しましたが、日本のおサイフケータイはカードエミュレーションモードが標準状態であり、R/Wモードが標準状態の AndroidNFCとの相性が悪いからです。Nexus Sは日本では正式に発売されていないわけなので、日本国内にまともなNFC対応のGingerbreadな端末はしばらく存在しない可能性が高そうです :-)

頑張って IntentFilterクラスやリフレクションを駆使しつつ、API Level 9と10を両対応していくのか、API Level 9方式を使ってAPI Level 10環境での取りこぼしを許容するのか、それともAPI Level 9を切り捨てて10のみ対応していくのか、開発者はアプリを作る前にちゃんと考えましょう。

*1:API Level 9の方法で作ったアプリ(tagletなど)をインストールした2.3.3のNexus Sに、NDEFが格納されたNFC Forum Tag を端末にかざした場合、標準のTag(タグ)アプリが優先的に起動します。