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