2017/01/28

[android][nfc]Reader modeアプリを作る (1)

HCEは動いているようだが、Reader Modeになるアプリを作ったことがないことに気付いた。
ならば、やらねばならぬのぅ。

 

Android 4.4で追加されたNFC reader mode - Qiita
「盛り上がっているのか盛り上がっていないのか」で始まるこの記事は、だいたい3年前のようだ。
当時からそうだったのだけど、未だによくわからないですな。。。
私も、バスや電車に乗るときはnimocaを使っているけど、買い物をするときは現金だし。

ただ、技術者にできる特権として、技術を提供できる、というものがある。
それが、たとえマイナーだったとしても。。。
まあ、私はNFCという技術がけっこう気に入っているので、なるべく使っているというしだいだ。

 

さて、脱線はここまでにして、Reader modeを見ていこう。
ActivityがReader modeになって、Android Beamなどに対応しなくなるという動作らしい。


このBeamを止める動作は、HCE側が同じことできないだろうか。。。
HCEしても、Windowsにかざすと、こちらはP2Pされたと思ってSNEPしてしまうのだ。
Reader modeとHCEが両立できるなら、Windowsでもタグとして認識してくれそうな気がする。
設定画面でAndroid Beamを止める設定はあるのだが、これは下回りとしては止まっていなさそうなのだ。
SNEPはしないものの、LLCPの何もしないやりとり(BLEでいえば、Empty PDUだけのやりとり)はしていそうな気配がある。

 

こういうときは、PN7150だ!
AndroidのCardReaderサンプルを起動させて、nfcDemoApp pollしたPN7150にかざすと、Android Beam(LLCP)を検知していない。
しかし、同じくAndroidのCardEmulationサンプルを起動させてかざすと、LLCPしているログが出続けている。

設定画面でAndroid Beamを無効にしてもLLCPしているログは出た。
NFC自体を無効にすると、出なくなる。
しかし、そこからNFCを有効にすると、私のHCE Sampleタグが読めた。
Android Beamを有効にしても、まだ読めている。
そして、何かをトリガにしてAndroid Beamが有効になるようなのだが、トリガが何かは分からなかった。
けっこう放置したけどHCEタグしか読めず、Google Mapアプリを立ち上げたらLLCPログが出始めた。
ポイントは、NFCを無効にする前にAndroid Beamだけを無効→NFC無効→NFC有効→Android Beam有効、の順番だ。
Android Beamだけを有効にしてもLLCPは始まらず、NFC有効時にBeamが有効なら同時に動かすし、SNEPするアプリが起動すればそれはそれで動かす、という動作か。
まあ、ホーム画面をBeamしてもうれしくはないしな。

私が作ったHCESampleサービスも入っているのだが、これはロック画面以外では読み取れなかった。
これは、作り方がよろしくないだけかもしれないので、また考えるとしよう。


軽くLLCP/SNEP/Android Beamの動作を書いておこう。

NFCは、R/WであるInitiatorがTagであるTargetにリクエストを送り、Targetがレスポンスを返す、という通信になっている。
TargetがInitiatorに対して自発的に送信することができないのだ。
しかし、P2Pのようなデータ交換ではどちらがデータを送りたくなるか分からないので、通信している間は常にInitiatorがTargetに何かを送信し、TargetがInitiatorに何かを返信する、ということをやっている。

InitiatorがR/Wとして動作するのか、P2Pの親として動作するのかは、ISO/IEC 18092に書いてあったと思う。
Targetを捕捉してすぐにその判断が行われ、動作分岐するのだ。
RC-S620/SのリファレンスマニュアルではP2Pになるときの動作が書かれているのだが、違う分岐に行くとHCEすることができるだろう。

SNEPは、LLCPでNDEFのタグを投げる、Simple NDEF Exchange Protocolのことだ、。
Android BeamはSNEPでアプリの情報(AAR)を送信していたはず(昔は、Android Pushというプロトコルもあった)。

 

だから、P2Pとして動作せずにR/Wとして動きたいときは、どちらかがLLCPしないようにすればよいのだけど、HCEはバックグラウンドで動くから、いつLLCPをやめる/戻すのタイミングがわからないということで、R/W側がやらざるを得なくなったというところか。
だって、他のアプリがAndroid Beamするつもりで動いているのに、裏で動いているHCEのせいで動作しなかったら「なんで??」ってなるよねぇ。

じゃあ、HCEがサービスじゃなくてアプリの一部として動作するのであればBeamはしなくていいやん、と思ったのだが、このAndroidサンプルはタグを読んでReaderアプリが起動していなかったら起動させる、ということをやっているようなのだ。
ただ、Google Playアプリが起動するだけなのでよくわからないのだが。。。
HCEするアプリの方に数字を打ち込むようになっているから、それがGoogle Playのアプリに関連づけられているのかもしれん。

すまん、サンプルがどういう動きをするのかは読んでいないのだ。。。
しかし、相手のアプリを起動させるためにBeamをしているだけであれば、HCE側もなんとかできるかもしれない。
希望だけ持っておこう。


サンプルを作ろうと思ったが、Qiitaの記事を書かれた人のサンプルがシンプルなので、それを見るのがよいだろう。
とはいえ、私も自分で実装しておかねば、今後使うときに困るので、自分なりに作っておこう。

 

機能として必要なのは、NfcAdapter.enableReaderMode()と、その引数で指定するNfcAdapter.ReaderCallbackクラスの派生クラスのインスタンスだ。
NfcAdapter.ReaderCallbackには、onTagDiscovered()だけがあればよさそうだ。

こちらのサンプルも、AndroidのサンプルもenableReaderMode()の最後の引数がnullだった。
何かと思ったら、reader modeの追加設定などができるみたい。
なんだろう、onTagDiscovered()が呼ばれたときに渡すデータを設定できるのかな?

また、APIの説明文に「disabling any peer-to-peer and card-emulation modes」と書いてあるので、HCEしているときにBeamを止める、というのは、少なくともこれではできないということになる。。。
この辺りは、やはりどうしようもないな。
やりたいなら、利害関係が発生しないように専用端末を作るしかないだろうか。
HCEする自アプリが起動していたせいで、他のアプリがSNEPする機会を失った、などと訴えられても困るものだ。

0 件のコメント:

コメントを投稿

コメントありがとうございます。
スパムかもしれない、と私が思ったら、
申し訳ないですが勝手に削除することもあります。