2011年1月27日木曜日

Android Llcp (Logical Link Control Protocol) of NFC

# 世の中は honeycomb でうかれてますが、今日も gingerbread ですよ。

NFC の Llcp (Logical Link Control Protocol) について調べてみました。

NFC で P2P 通信するためのプロトコルなのですが、残念ながら現状の gingerbread では使えなくなっています。

 Android 2.3 のNFCはP2Pモードが使えない - Android(アンドロイド)情報-ブリリアントサービス -

 この diff みればわかるんですけど、


@@ -323,7 +323,7 @@
/**< Macro to Enable the Peer to Peer Feature */
#define ENABLE_P2P

-#define DEFAULT_NFCIP_MODE_SUPPORT 0x0FU
+#define DEFAULT_NFCIP_MODE_SUPPORT 0x00U // to disable P2P feature


この DEFAULT_NFCIP_MODE_SUPPORT が off にされてます。この定数はそんなに参照されてなくて、たぶんここの設定で、reader が polling で P2P device を認識しないようになってるんじゃないかなーと。


なんですが、もうすぐ使えるようになるんじゃね?的なことを書いてるところもあり

 Nexus S and Gingerbread Phones to Get Full NFC Support Soon -

今はだめだけど、使えるようになったときの為にもやっといて損はないはず!

実装自体はされてるしね!

# 以下のまとめには私の予測がはいっているので注意してね!


■ LLcp について

 ・connection と connectionless がある

 ・connection
   ・TCPみたいな感じ
   ・通信開始に先立ってコネクションを確立
   ・お互いに好きなときに送受信できる
   ・サーバ側 : LlcpServiceSocket
     ・accept ってメソッドがある
   ・クライアント側 : LlcpSocket
     ・connect ってメソッドがある
     ・connect(int sap) : sap を指定
     ・connect(String sn) : サービス名を指定
   ・byte[] を送信


 ・connectionless
   ・UDPみたいな感じ
   ・コネクションなし
   ・一方的に送信するだけ
   ・送受信しようと思ったら、お互いがサーバーのように
    ソケットを用意する必要がある
   ・サーバ側 : LlcpConnectionlessSocket
      ・receiveFrom ってメソッドがある
   ・クライアント側 : LlcpConnectionlessSocket
      ・sendTo ってメソッドがある
   ・LlcpPacket を送受信


 ・サーバ側クライアント側両方とも sap(Service Access Point) を指定してソケットを作成する
   ・port みたいなもの(?)
   ・サーバ側が指定の sap でソケットを作成し、クライアント側も
    同じ(?) sap を指定してソケット作成
     ・ sap を指定してデータ送信
     ・ サービス名指定してデータ送信(connectionのときのみ)


 ・LlcpSocket の connect にサービス名を指定できるのは、
  サーバ側の sap がわからなくてもクライアント側が接続できるため(かな?)


 ・指定した sap がすでに使われていると
  エラー(ErrorCodes.ERROR_SAP_USED) になる


 ・nativeHandle はソケットの識別子(だと思う)



LlcpServiceSocket : クライアントからの接続をまつソケット
  ・ LlcpServiceSocket(ILlcpServiceSocket service, ILlcpSocket socketService, int handle)
  ・ LlcpSocket accept()
    : 戻り値は "接続済みソケット" 送受信はこの LlcpSocket で行う
  ・ void close()

ILlcpServiceSocket
  ・ int accept(int nativeHandle)
     : 戻り値は < 0 のとき ErrorCode, > 0 のとき
      accept したクライアントの handle
  ・ void close(int nativeHandle)

NativeLlcpServiceSocket
  ・ NativeLlcpServiceSocket(int sap, String serviceName, int miu, int rw, int linearBufferLength)
  ・ NativeLlcpSocket doAccept(int miu, int rw, int linearBufferLength)
  ・ boolean doClose()


LlcpSocket
  ・ LlcpSocket(ILlcpSocket service, int handle)
  ・ void connect(int sap)
  ・ void connect(String sn)
  ・ void send(byte[] data)
  ・ int receive(byte[] receiveBuffer)
     : 戻り値は receiveLength
  ・ void close()

ILlcpSocket
  ・ int connect(int nativeHandle, int sap)
     : 戻り値は ErrorCode (Success 含む)
  ・ int connectByName(int nativeHandle, String sn)
     : 戻り値は ErrorCode (Success 含む)
  ・ int send(int nativeHandle, in byte[] data)
     : 戻り値は ErrorCode (Success 含む)
  ・ int receive(int nativeHandle, out byte[] receiveBuffer)
     : 戻り値は receiveLength or ErrorCode
  ・ int close(int nativeHandle)
     : 戻り値は ErrorCode (Success 含む)

NativeLlcpSocket
  ・ NativeLlcpSocket(int sap, int miu, int rw)
  ・ boolean doConnect(int sap)
  ・ boolean doConnectBy(String sn)
  ・ boolean doSend(byte[] data)
  ・ int doReceive(byte[] recvBuff)
     : 戻り値は receiveLength
  ・ boolean doClose()


LlcpConnectionlessSocket
  ・ LlcpConnectionlessSocket(ILlcpConnectionlessSocket service, int handle)
  ・ void sendTo(LlcpPacket packet)
  ・ LlcpPacket receiveFrom()
  ・ void close()

ILlcpConnectionlessSocket
  ・ int sendTo(int nativeHandle, in LlcpPacket packet)
     : 戻り値は ErrorCode (Success 含む)
  ・ LlcpPacket receiveFrom(int nativeHandle)
  ・ void close(int nativeHandle)

NativeLlcpConnectionlessSocket
  ・ boolean doSendTo(int sap, byte[] data)
  ・ LlcpPacket doReceiveFrom(int linkMiu)
  ・ boolean doClose()



上記の各ソケットはこのインタフェースを実装したクラス(NfcService の中で実装されている)のメソッドで作成する
INfcAdapter
  ・ int createLlcpServiceSocket(int sap, String sn, int miu, int rw, int linearBufferLength)
  ・ int createLlcpSocket(int sap, int miu, int rw, int linearBufferLength)
  ・ int createLlcpConnectionlessSocket(int sap)
  ・ ILlcpServiceSocket getLlcpServiceInterface();
  ・ ILlcpSocket getLlcpInterface()
  ・ ILlcpConnectionlessSocket getLlcpConnectionlessInterface()


 ・LlcpServiceSocket の生成では、すでに使われているサービス名
  を指定するとエラー(ErrorCodes.ERROR_SERVICE_NAME_USED)になる


 ・LlcpServiceSocket, LlcpSocket の作成では miu, rw,
  linearBufferLength のパラメータが適切でないと
  エラー (ErrorCodes.ERROR_SOCKET_OPTIONS) になる

   判定式CheckSocketOptions()

1825 private boolean CheckSocketOptions(int miu, int rw, int linearBufferlength) {
1826
1827 if (rw > LLCP_RW_MAX_VALUE || miu < LLCP_MIU_DEFAULT || linearBufferlength < miu) {
1828 return false;
1829 }
1830 return true;
1831 }


 ・miu
   ・ NfcService.LLCP_MIU_DEFAULT より大きくないと
     エラーになる(コードでは 128)

 ・linearBufferLength
   ・ miu より大きくないとエラーになる

 ・rw (Receive Window)
   ・ NfcService.LLCP_RW_MAX_VALUE より小さくないと
     エラーになる(コードでは 15個)


 ・管理できるソケット数は NfcService.LLCP_SOCKET_NB_MAX個まで(コードでは 5個)


ErrorCode
 ・ NFC が有効になっていない場合(端末設定に使うかどうかの項目がある)
    ErrorCodes.ERROR_NOT_INITIALIZED = -17
 ・ sap がすでに使われている
    ErrorCodes.ERROR_SAP_USED = -13
 ・ 管理できるソケット数を超えている
    ErrorCodes.ERROR_INSUFFICIENT_RESOURCES = -9
 ・ miu, rw, linearBufferLength のパラメータが適切でない
    ErrorCodes.ERROR_SOCKET_OPTIONS = -15
 ・ ソケット生成時にすでに使われているサービス名を指定
    ErrorCodes.ERROR_SERVICE_NAME_USED = -14

PHFRINFC_LLCP_MIU_DEFAULT

34 /**
35 * \name LLCP default parameters.
36 *
37 * Definitions for use when wanting to use default LLCP parameter values.
38 *
39 */
40 /*@{*/
41 #define PHFRINFC_LLCP_MIU_DEFAULT 128 /**< Default MIU value (in bytes).*/
42 #define PHFRINFC_LLCP_WKS_DEFAULT 1 /**< Default WKS value (bitfield).*/
43 #define PHFRINFC_LLCP_LTO_DEFAULT 10 /**< Default LTO value (in step of 10ms).*/
44 #define PHFRINFC_LLCP_RW_DEFAULT 1 /**< Default RW value (in frames).*/
45 #define PHFRINFC_LLCP_OPTION_DEFAULT 0 /**< Default OPTION value (in frames).*/
46 #define PHFRINFC_LLCP_MIUX_DEFAULT 0 /**< Default MIUX value (in bytes) */
47 #define PHFRINFC_LLCP_SN_MAX_LENGTH 255 /**< Max length value for the Service Name */
48 #define PHFRINFC_LLCP_RW_MAX 15 /**< Max RW value (in frames).*/
49 /*@}*/



ここのソケットの説明がすごくわかりやすかった。thanks!
ソケット通信メモ(Hishidama's TCP/UDP Socket Memo) -


■ で、試してみたんだよ

 connectionless の場合、receiveFrom メソッドを呼ぶと、native 側で落ちたw
 method が見つからなくて、NFC が再起動しちゃうんだよ。あらら。


D/dalvikvm( 186): GetMethodID: method not found: Landroid/nfc/LlcpPacket;.:()V
I/DEBUG ( 72): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
I/DEBUG ( 72): Build fingerprint: 'google/soju/crespo:2.3.2/GRH78C/93600:user/release-keys'
I/DEBUG ( 72): pid: 186, tid: 205 >>> com.android.nfc <<<
I/DEBUG ( 72): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0000001c
...
I/ServiceManager( 69): service 'nfc' died
...
D/NFC JNI ( 581): NFC Service : loading JNI
I/NfcService( 581): Starting NFC service
D/NFC JNI ( 581): Start Initialization
W/NFC_i2c ( 581): sleeping a little longer...
D/NFC JNI ( 581): NFC capabilities: HAL = 8150100, FW = a70412, HW = 620003, Model = 0, HCI = 1
I/NFC JNI ( 581): NFC Initialized


 connection ありの場合、 accept メソッドを呼ぶと、それっぽい感じで待機状態になる。UIスレッドで呼ぶとUIがブロックされちゃう。問題は connect のほうで、createLlcpSocket で、mLlcpLinkState == NfcAdapter.LLCP_LINK_STATE_ACTIVATED だったら、ソケットを作成するんだけど、そうじゃない場合は、RegisteredSocket に突っ込むだけなんですよ。で、mLlcpLinkState を ACTIVATED にするには、どうも polling で P2P device をキャッチしないとダメみたいなんです。で、この polling で P2P device をキャッチするところが現状の gingerbread だと off になってるみたいです(勝手な予想ですけど。。。)

--------------
追記

 Llcp の仕様は NFC Forum の specifications にあります。

 



 

0 件のコメント:

コメントを投稿