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 みればわかるんですけど、

  1. @@ -323,7 +323,7 @@  
  2.  /**< Macro to Enable the Peer to Peer Feature */  
  3.  #define ENABLE_P2P  
  4.    
  5. -#define DEFAULT_NFCIP_MODE_SUPPORT      0x0FU  
  6. +#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()
  1. 1825     private boolean CheckSocketOptions(int miu, int rw, int linearBufferlength) {  
  2. 1826   
  3. 1827         if (rw > LLCP_RW_MAX_VALUE || miu < LLCP_MIU_DEFAULT || linearBufferlength < miu) {  
  4. 1828             return false;  
  5. 1829         }  
  6. 1830         return true;  
  7. 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
  1. 34 /** 
  2. 35  *  \name LLCP default parameters. 
  3. 36  * 
  4. 37  *  Definitions for use when wanting to use default LLCP parameter values. 
  5. 38  * 
  6. 39  */  
  7. 40  /*@{*/  
  8. 41 #define PHFRINFC_LLCP_MIU_DEFAULT         128  /**< Default MIU value (in bytes).*/  
  9. 42 #define PHFRINFC_LLCP_WKS_DEFAULT         1    /**< Default WKS value (bitfield).*/  
  10. 43 #define PHFRINFC_LLCP_LTO_DEFAULT         10   /**< Default LTO value (in step of 10ms).*/  
  11. 44 #define PHFRINFC_LLCP_RW_DEFAULT          1    /**< Default RW value (in frames).*/  
  12. 45 #define PHFRINFC_LLCP_OPTION_DEFAULT      0    /**< Default OPTION value (in frames).*/  
  13. 46 #define PHFRINFC_LLCP_MIUX_DEFAULT        0    /**< Default MIUX value (in bytes) */  
  14. 47 #define PHFRINFC_LLCP_SN_MAX_LENGTH       255  /**< Max length value for the Service Name */  
  15. 48 #define PHFRINFC_LLCP_RW_MAX              15   /**< Max RW value (in frames).*/  
  16. 49 /*@}*/  



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


■ で、試してみたんだよ

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

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


 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 件のコメント:

コメントを投稿