2011年1月30日日曜日

Android NFC での FeliCa の NDEF 判定

FeliCa standard に直接 byte[] data を書き込んでいたら、NDEF として認識されなくなっちゃったので、ちょっと調べてみました。

■ いきさつ

Android Bazaar and Conference で Sony さんに FeliCa Standard カードをいただいた。

 ・このカードには URL 情報が入っていた

 ・もらったときには Android 2.3 標準の Tag というアプリで
  NDEF として認識できてた

 ・自分用の FeliCa 情報を dump するアプリ (Suica Reader の
  tag info 部分と同じ) でも isNdef は true になっていた

 ・Raw Bytes をごりごり上書きしたら isNdef が false になってしまった


ということで、isNdef はどうやって判定しているのか調べてみた。


■ NFC の処理

polling で NFC タグが見つかると、NFCService の mHandler に message が送られます。

その結果 NfcService.java#2048 に辿りつきます。

判定は NfcService.java#2052 の if (nativeTag.checkNdef()) で行っています。

これが true だと NdefTag クラスのインスタンスが生成されて、Intent intent = buildNdefTagIntent(tag); で生成された Intent が broadcast されます。


2058 msgNdef[0] = new NdefMessage(buff);
2059 NdefTag tag = new NdefTag(nativeTag.getUid(),
2060 TagTarget.internalTypeToRawTargets(nativeTag.getType()),
2061 nativeTag.getPollBytes(), nativeTag.getActivationBytes(),
2062 nativeTag.getHandle(),
2063 TagTarget.internalTypeToNdefTargets(nativeTag.getType()),
2064 new NdefMessage[][] {msgNdef});
2065 Intent intent = buildNdefTagIntent(tag)



2220 private Intent buildNdefTagIntent(NdefTag tag) {
2221 Intent intent = new Intent();
2222 intent.setAction(NfcAdapter.ACTION_TAG_DISCOVERED);
2223 intent.putExtra(NfcAdapter.EXTRA_TAG, tag);
2224 intent.putExtra(NfcAdapter.EXTRA_ID, tag.getId());
2225 intent.putExtra(NfcAdapter.EXTRA_NDEF_MESSAGES, tag.getNdefMessages());
2226 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2227 return intent;
2228 }


一方 false だと、 Tag クラスのインスタンスが生成されて、NfcAdapter.EXTRA_TAG キー との組み合わせとして Intent の extra に格納されます。


2102 Intent intent = new Intent();
2103 Tag tag = new Tag(nativeTag.getUid(), false,
2104 TagTarget.internalTypeToRawTargets(nativeTag.getType()),
2105 nativeTag.getPollBytes(), nativeTag.getActivationBytes(),
2106 nativeTag.getHandle());
2107 intent.setAction(NfcAdapter.ACTION_TAG_DISCOVERED);
2108 intent.putExtra(NfcAdapter.EXTRA_TAG, tag);
2109 intent.putExtra(NfcAdapter.EXTRA_ID, tag.getId());
2110 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);


では、 checkNdef() を見てみます

NativeNfcTag.java#checkNdef

doCheckNdef を呼んでいます。


100 private native boolean doCheckNdef();
101 public synchronized boolean checkNdef() {
102 if (mWatchdog != null) {
103 mWatchdog.reset();
104 }
105 return doCheckNdef();
106 }


この doCheckNdef() は native 側の com_android_nfc_NativeNfcTag_doCheckNdef を呼んでいます。

com_android_nfc_NativeNfcTag.cpp#571


571 /*
572 * JNI registration.
573 */
574 static JNINativeMethod gMethods[] =
575 {
    ...
582 {"doCheckNdef", "()Z",
583 (void *)com_android_nfc_NativeNfcTag_doCheckNdef},
    ...
590 };


では、com_android_nfc_NativeNfcTag_doCheckNdef を見てみましょう。

com_android_nfc_NativeNfcTag.cpp#com_android_nfc_NativeNfcTag_doCheckNdef


phLibNfc_Ndef_CheckNdef() メソッドを呼んでいます。2番目の引数はこの処理が終わったときの call back です。


500 static jboolean com_android_nfc_NativeNfcTag_doCheckNdef(JNIEnv *e, jobject o)
501 {
    ...
504 jboolean result = JNI_FALSE;
    ...
512 status = phLibNfc_Ndef_CheckNdef(handle, nfc_jni_checkndef_callback,(void *)e);
    ...
523 if (nfc_jni_is_ndef == FALSE)
524 {
525 goto clean_and_return;
526 }
527
528 result = JNI_TRUE;
529
530 clean_and_return:
531 CONCURRENCY_UNLOCK();
532 return result;
533 }


先に call back を見てみましょう。

com_android_nfc_NativeNfcTag.cpp#nfc_jni_checkndef_callback

引数の status が NFCSTATUS_OK なら nfc_jni_is_ndef に TRUE を、そうでなければ FALSE をセットしています。この nfc_jni_is_ndef は上記のcom_android_nfc_NativeNfcTag_doCheckNdef で参照されています。


60 static void nfc_jni_checkndef_callback(void *pContext,
61 phLibNfc_ChkNdef_Info_t info, NFCSTATUS status)
62 {
63 LOG_CALLBACK("nfc_jni_checkndef_callback", status);
64
65 if(status == NFCSTATUS_OK)
66 {
67 if(nfc_jni_ndef_buf)
68 {
69 free(nfc_jni_ndef_buf);
70 }
71 nfc_jni_ndef_buf_len = info.MaxNdefMsgLength;
72 nfc_jni_ndef_buf = (uint8_t*)malloc(nfc_jni_ndef_buf_len);
73 nfc_jni_is_ndef = TRUE;
74 }
75 else
76 {
77 nfc_jni_is_ndef = FALSE;
78 }
79
80 sem_post(nfc_jni_tag_sem);
81 }


では、phLibNfc_Ndef_CheckNdef のほうを見てみましょう。

phLibNfc_ndef_raw.c#phLibNfc_Ndef_CheckNdef

phFriNfc_NdefMap_ChkNdef を呼んでいます。また、 814 行目で先程の call back をgpphLibContext->CBInfo.pClientCkNdefCb にセットしています。


714 /**
715 * This function allows the user to check whether a particular Remote Device
716 * is NDEF compliant or not
717 */
718 NFCSTATUS phLibNfc_Ndef_CheckNdef(phLibNfc_Handle hRemoteDevice,
719 pphLibNfc_ChkNdefRspCb_t pCheckNdef_RspCb,
720 void* pContext)
721 {
    ...
770 for (cr_index = 0; cr_index < PH_FRINFC_NDEFMAP_CR; cr_index++)
771 {
772 /* Register the callback for the check ndef */
773 RetVal = phFriNfc_NdefMap_SetCompletionRoutine(
774 gpphLibContext->ndef_cntx.psNdefMap,
775 cr_index,
776 phLibNfc_Ndef_CheckNdef_Cb,
777 (void *)gpphLibContext);
778 }
779 /*call below layer check Ndef function*/
780 RetVal = phFriNfc_NdefMap_ChkNdef(gpphLibContext->ndef_cntx.psNdefMap);
    ...
812 if(RetVal== NFCSTATUS_PENDING)
813 {
814 gpphLibContext->CBInfo.pClientCkNdefCb = pCheckNdef_RspCb;
815 gpphLibContext->CBInfo.pClientCkNdefCntx = pContext;
816 gpphLibContext->status.GenCb_pending_status=TRUE;
817 gpphLibContext->LibNfcState.next_state = eLibNfcHalStateTransaction;
818 }
819
820 }
821 return RetVal;
822 }
823


では、phFriNfc_NdefMap_ChkNdef を見てみましょう。

phFriNfc_NdefMap.c#phFriNfc_NdefMap_ChkNdef

FeliCa に関係する部分を抜き出すと、phFriNfc_Felica_ChkNdef を呼んでいます。


632 NFCSTATUS phFriNfc_NdefMap_ChkNdef( phFriNfc_NdefMap_t *NdefMap)
633 {
    ...
737 case phHal_eFelica_PICC:
738
739 /*Set the OpMode Type Flag*/
    ...
745 status = phFriNfc_Felica_ChkNdef(NdefMap);
    ...
751 break;
    ...
841 }


では、phFriNfc_Felica_ChkNdef を見てみましょう。

phFriNfc_FelicaMap.c#phFriNfc_Felica_ChkNdef


phFriNfc_Felica_HUpdateManufIdDetails と phFriNfc_Felica_HRdAttrInfo が呼ばれています。


2118 NFCSTATUS phFriNfc_Felica_ChkNdef( phFriNfc_NdefMap_t *NdefMap)
2119 {
    ...
2128 /* check the ndef compliency with the system code reecived in the RemoteDevInfo*/
2129 status = phFriNfc_Felica_HUpdateManufIdDetails(NdefMap);
2130
2131 if (status == NFCSTATUS_SUCCESS)
2132 {
2133
2134 /* set the operation type to Check ndef type*/
2135 NdefMap->Felica.OpFlag = PH_FRINFC_NDEFMAP_FELI_CHK_NDEF_OP;
2136 status = phFriNfc_Felica_HRdAttrInfo(NdefMap);
2137 }
    ...
2149 }


まず、phFriNfc_Felica_HUpdateManufIdDetails を見てみます。

phFriNfc_FelicaMap.c#phFriNfc_Felica_HUpdateManufIdDetails


システムコードが 0x12 0xFC かどうかチェックしています。
これ以外の場合、status として NFCSTATUS_NO_NDEF_SUPPORT が返されるので、NDEFではないと認識されます。

つまり、FeliCa Lite の場合 0x88 0xb4 が返ってくるので、ここで NDEF ではないと認識されてしまいます。


2205 /*!
2206 * ¥brief Checks validity of system code sent from the lower device, during poll operation.
2207 */
2208
  2209 static NFCSTATUS phFriNfc_Felica_HUpdateManufIdDetails(const phFriNfc_NdefMap_t *NdefMap)
2210 {
    ...
2214 /* copy the IDm and PMm in Manufacture Details Structure*/
2215 (void)memcpy( (uint8_t *)(NdefMap->FelicaManufDetails.ManufID),
2216 (uint8_t *)NdefMap->psRemoteDevInfo->RemoteDevInfo.Felica_Info.IDm,
2217 8);
2218 (void)memcpy( (uint8_t *)(NdefMap->FelicaManufDetails.ManufParameter),
2219 (uint8_t *)NdefMap->psRemoteDevInfo->RemoteDevInfo.Felica_Info.PMm,
2220 8);
2221 if((NdefMap->psRemoteDevInfo->RemoteDevInfo.Felica_Info.SystemCode[0] == 0x12)
2222 && (NdefMap->psRemoteDevInfo->RemoteDevInfo.Felica_Info.SystemCode[1] == 0xFC))
2223 {
2224 status = PHNFCSTVAL(CID_NFC_NONE, NFCSTATUS_SUCCESS);
2225 }
2226 else
2227 {
2228 status = PHNFCSTVAL(CID_FRI_NFC_NDEF_MAP,
2229 NFCSTATUS_NO_NDEF_SUPPORT);
2230 }
    ...
2260 return (status);
2261
2262 }


次に、phFriNfc_Felica_HRdAttrInfo を見てみましょう。

phFriNfc_FelicaMap.c#phFriNfc_Felica_HRdAttrInfo


システムコードが 0x12 0xFC だけでは NDEF とは認識されず、ここでさらにチェックされます。

read コマンドを送って、0x000B のサービスコードの1ブロック目を読んでいます。
ここは、 Random Access (Read Only) の領域です。

受信データは NdefMap->SendRecvBuf[] に入ります。

カードのオペレーション終了時に呼ばれる処理は 2772行目で設定されており、
phFriNfc_NdefMap_Process が呼ばれます。


2704 static NFCSTATUS phFriNfc_Felica_HRdAttrInfo(phFriNfc_NdefMap_t *NdefMap)
2705 {
2706
2707 NFCSTATUS status = NFCSTATUS_PENDING;
2708 uint8_t BufIndex = 0;
2709
2710 /* Set the Felica Cmd*/
    ...
2712 NdefMap->Cmd.FelCmd = phHal_eFelica_Raw;
    ...
2720
2721 /* 1st byte represents the length of the cmd packet*/
2722 NdefMap->SendRecvBuf[BufIndex] = 0x00;
2723 BufIndex++;
2724
2725 /* Read/check command code*/
2726 NdefMap->SendRecvBuf[BufIndex] = 0x06;
2727 BufIndex++;
2728
2729 /* IDm - Manufacturer Id : 8bytes*/
    ...
2731 (void)memcpy((&(NdefMap->SendRecvBuf[BufIndex])),
2732 (void * )&NdefMap->psRemoteDevInfo->RemoteDevInfo.Felica_Info.IDm,
2733 8);
    ...
2740
2741 BufIndex+=8;
2742
2743 NdefMap->SendRecvBuf[BufIndex] = 0x01; /* Number of Services (n=1 ==> 0x80)*/
2744 BufIndex++;
2745
2746 NdefMap->SendRecvBuf[BufIndex] = 0x0B; /* Service Code List*/
2747 BufIndex++;
2748
2749 NdefMap->SendRecvBuf[BufIndex] = 0x00; /* Service Code List*/
2750 BufIndex++;
2751
2752 NdefMap->SendRecvBuf[BufIndex] = 0x01; /* Number of Blocks to read)*/
2753 BufIndex++;
2754
2755 NdefMap->SendRecvBuf[BufIndex] = 0x80; /* 1st Block Element : byte 1*/
2756 BufIndex++;
2757
2758 NdefMap->SendRecvBuf[BufIndex] = 0x00; /* 1st Block Element : byte 2, block 1*/
2759 BufIndex++;
2760
2761 NdefMap->SendRecvBuf[PH_NFCFRI_NDEFMAP_FELI_PKT_LEN_INDEX] = BufIndex;
2762
2763 *NdefMap->SendRecvLength = NdefMap->TempReceiveLength;
2764
2765 /* Update the Send Len*/
2766 NdefMap->SendLength = BufIndex;
2767
2768 /* Change the state to PH_NFCFRI_NDEFMAP_FELI_STATE_RD_ATTR */
2769 NdefMap->State = PH_NFCFRI_NDEFMAP_FELI_STATE_RD_ATTR;
2770
2771 /*set the completion routines for the desfire card operations*/
2772 NdefMap->MapCompletionInfo.CompletionRoutine = phFriNfc_NdefMap_Process;
2773 NdefMap->MapCompletionInfo.Context = NdefMap;
2774
2775 /*Call the Overlapped HAL Transceive function */
2776 status = phFriNfc_OvrHal_Transceive( NdefMap->LowerDevice,
2777 &NdefMap->MapCompletionInfo,
2778 NdefMap->psRemoteDevInfo,
2779 NdefMap->Cmd,
2780 &NdefMap->psDepAdditionalInfo,
2781 NdefMap->SendRecvBuf,
2782 NdefMap->SendLength,
2783 NdefMap->SendRecvBuf,
2784 NdefMap->SendRecvLength);
2785 return (status);
2786
2787 }
2788


では、phFriNfc_NdefMap_Process を見てみましょう。

phFriNfc_NdefMap.c#phFriNfc_NdefMap_Process

FeliCa の場合、phFriNfc_Felica_Process が呼ばれています。


854 void phFriNfc_NdefMap_Process( void *Context,
855 NFCSTATUS Status)
856 {
857
858 if ( Context != NULL )
859 {
860 phFriNfc_NdefMap_t *NdefMap = (phFriNfc_NdefMap_t *)Context;
861 /*
862 * 4 Changed
863 * Description: Opmode replace with RevDevTyp.
864 */
865
866 switch ( NdefMap->psRemoteDevInfo->RemDevType )
867 {
    ...
908 case phHal_eFelica_PICC :
    ...
910 /* Remote device is Felica Smart card */
911 phFriNfc_Felica_Process(NdefMap, Status);
    ...
916 break;
917
    ...
960 }
961 }
    ...
969 }
970


では、phFriNfc_Felica_Process を見てみましょう。

phFriNfc_FelicaMap.c#phFriNfc_Felica_Process

2321 if ( NdefMap->SendRecvBuf[10] == 0x00)

で read コマンドが成功したかどうかチェックしています。

2328 Status = phFriNfc_Felica_HUpdateAttrInfo(NdefMap);

ここで、NdefMap の SnedRecvBuf に入っているデータ(タグから読み込んだデータ)を FelicaAttrInfo に入れ、CardState も設定しています。

2360 Status = phFriNfc_MapTool_SetCardState( NdefMap,
2361 NDEFLen);

ここで、phFriNfc_Felica_HUpdateAttrInfo から読み込んだカードの状態を設定しています。


2273 void phFriNfc_Felica_Process(void *Context,
2274 NFCSTATUS Status)
2275 {
2276 uint8_t CRFlag = FALSE;
2277 uint16_t RecvTxLen = 0,
2278 BytesToRecv = 0,
2279 Nbc = 0;
2280 uint32_t TotNoWrittenBytes = 0,
2281 NDEFLen=0;
2282
2283 /*Set the context to Map Module*/
2284 phFriNfc_NdefMap_t *NdefMap = (phFriNfc_NdefMap_t *)Context;
2285
2286 if ( Status == NFCSTATUS_SUCCESS )
2287 {
2288 switch (NdefMap->State)
2289 {
    ...
2319 case PH_NFCFRI_NDEFMAP_FELI_STATE_RD_ATTR:
2320 /* check for the status flag1 and status flag2for the successful read operation*/
2321 if ( NdefMap->SendRecvBuf[10] == 0x00)
2322 {
2323 /* check the Manuf Id in the receive buffer*/
2324 Status = phFriNfc_Felica_HCheckManufId(NdefMap);
2325 if ( Status == NFCSTATUS_SUCCESS)
2326 {
2327 /* Update the Attribute Information in to the context structure*/
2328 Status = phFriNfc_Felica_HUpdateAttrInfo(NdefMap);
2329 if ( Status == NFCSTATUS_SUCCESS )
2330 {
2331 PH_NFCFRI_NDEFMAP_FELI_CAL_LEN_BYTES(NdefMap->FelicaAttrInfo.LenBytes[0],
2332 NdefMap->FelicaAttrInfo.LenBytes[1],
2333 NdefMap->FelicaAttrInfo.LenBytes[2],
2334 NDEFLen);
2335
2336 if ( NdefMap->Felica.OpFlag == PH_FRINFC_NDEFMAP_FELI_WR_ATTR_RD_OP )
2337 {
    ...
2346 }
2347 else if( NdefMap->Felica.OpFlag == PH_FRINFC_NDEFMAP_FELI_RD_ATTR_RD_OP )
2348 {
    ...
2356 }
2357 else if( NdefMap->Felica.OpFlag == PH_FRINFC_NDEFMAP_FELI_CHK_NDEF_OP )
2358 {
2359
2360 Status = phFriNfc_MapTool_SetCardState( NdefMap,
2361 NDEFLen);
2362 /* check status value*/
2363 NdefMap->CardType = PH_FRINFC_NDEFMAP_FELICA_SMART_CARD;
2364 /*reset the buffer index*/
2365 NdefMap->ApduBuffIndex = 0;
2366 /* set the Next operation Flag to indicate need of reading attribute information*/
2367 NdefMap->Felica.OpFlag = PH_FRINFC_NDEFMAP_FELI_OP_NONE;
2368 /* call respective CR */
2369 phFriNfc_Felica_HCrHandler(NdefMap,PH_FRINFC_NDEFMAP_CR_CHK_NDEF,Status);
2370 }
2371 else if ( NdefMap->Felica.OpFlag == PH_FRINFC_NDEFMAP_FELI_WR_EMPTY_MSG_OP )
2372 {
    ...
2381 }
2382 else
2383 {
2384
2385 /* invalid operation occured*/
2386 Status = PHNFCSTVAL(CID_FRI_NFC_NDEF_MAP,¥
2387 NFCSTATUS_INVALID_DEVICE_REQUEST);
2388 CRFlag =TRUE ;
2389 }
2390 }
2391 else
2392 {
2393 CRFlag =TRUE ;
2394 }
2395 }
2396 else
2397 {
2398 CRFlag =TRUE ;
2399 }
2400 }
2401 else
2402 {
2403 CRFlag =TRUE;
2404 /*handle the Error case*/
2405 Status = PHNFCSTVAL(CID_FRI_NFC_NDEF_MAP,¥
2406 NFCSTATUS_READ_FAILED);
2407 }
2408 if ( CRFlag == TRUE )
2409 {
2410 /* call respective CR */
2411 phFriNfc_Felica_HCrHandler(NdefMap,PH_FRINFC_NDEFMAP_CR_RD_NDEF,Status);
2412 }
2413 break;
2414
    ...
2667 }
2668 }
2669 else
2670 {
    ...
2697 }
2698
2699 }



では、受信したデータが NDEF 形式かどうかチェックするところを見てみましょう。

phFriNfc_FelicaMap.c#phFriNfc_Felica_HUpdateAttrInfo

受信データ NdefMap->SendRecvBuf[] はこんな形式になっています。


  LEN Bytes
0 1 2 3 4 5 6 7 8 9 10 11 12
1d 07 -------- IDm ---------- 00 00 01
↑ ↑ ↑
LEN COMMAND RD SUCCESS

13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
---------------- data 部分 --------------------
↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
ver | Nbw | Nmaxb2 WriteFlag RdWrFlag CheckSum
Nbr Nmaxb1


data部分がサービス部分に書かれているデータです。ここのフォーマットを NDEF にするポイントは


74 #define PH_NFCFRI_NDEFMAP_FELI_VERSION_INDEX 13 /* Specifies Index of the version in Attribute Resp Buffer*/

61 #define PH_NFCFRI_NDEFMAP_NFCDEV_MAJOR_VER_NUM 0x01
62 #define PH_NFCFRI_NDEFMAP_NFCDEV_MINOR_VER_NUM 0x00
63
64 /* Macros to find major and minor TAG : Ex:Type1/Type2/Type3/Type4 version numbers*/
65 #define PH_NFCFRI_NDEFMAP_GET_MAJOR_TAG_VERNO(a) (((a) & (0xf0))>>(4))
66 #define PH_NFCFRI_NDEFMAP_GET_MINOR_TAG_VERNO(a) ((a) & (0x0f))


SendRecvBuf[13] = data[0] : Version : 0x1*
Felica の場合、上位 4bit が 0001 でなければならない、下位 4bit は 0000 以上でなければならない

SendRecvBuf[14] = data[1] : Nbr
Nmaxb より小さくなければならない
SendRecvBuf[15] = data[2] : Nbw
Nmaxb より小さくなければならない
SendRecvBuf[16] = data[3] : Nmaxb1
SendRecvBuf[17] = data[4] : Nmabb2
Nmaxb : 2 byte (上位 1byte が SendRecvBuf[16], 下位 1byte が SendRecvBuf[17])
 0 以外でなければならない
SendRecvBuf[18] = data[5] : RFU 0x00 でなければならない
SendRecvBuf[19] = data[6] : RFU 0x00 でなければならない
SendRecvBuf[20] = data[7] : RFU 0x00 でなければならない
SendRecvBuf[21] = data[8] : RFU 0x00 でなければならない
SendRecvBuf[22] = data[9] : WriteFlag 0x00 か 0x0f でなければならない
SendRecvBuf[23] = data[10] : RdWrFlag 0x00(READ_ONLY) か 0x01(READ_WRITE) でなければならない
SendRecvBuf[24] = data[11] : LEN Bytes
SendRecvBuf[25] = data[12] : LEN Bytes
SendRecvBuf[26] = data[13] : LEN Bytes
DataLen : 3 byte (最上位 1byte が SendRecvBuf[24], 真ん中 1byte が SendRecvBuf[25], 最下位 1byte が SendRecvBuf[26])
Nbc : (uint16_t )((( DataLen % 16) == 0 ) ? (DataLen >> 4) : ((DataLen >> 4) +1));
Nmaxb より小さくなければならない
0 の場合 READ_ONLY ではいけない
SendRecvBuf[27] = data[14] : ChkSum1 : Check Sum
SendRecvBuf[28] = data[15] : ChkSum2 : Check Sum
RecvChkSum : 2 byte (上位 1byte が ChkSum1, 下位 1byte が ChkSum2)
   SendRecvBuf[13] から SendRecvBuf[26] までの値の加算

チェックサムを行っているところ

2817 static NFCSTATUS phFriNfc_Felica_HCalCheckSum(const uint8_t *TempBuffer,
2818 uint8_t StartIndex,
2819 uint8_t EndIndex,
2820 uint16_t RecvChkSum)
2821 {
2822 NFCSTATUS Result = NFCSTATUS_SUCCESS;
2823 uint16_t CheckSum=0,
2824 BufIndex=0;
2825
2826 for(BufIndex = StartIndex;BufIndex <=EndIndex;BufIndex++)
2827 {
2828 CheckSum += TempBuffer[BufIndex];
2829 }
2830 if( RecvChkSum != CheckSum )
2831 {
2832 Result = PHNFCSTVAL(CID_FRI_NFC_NDEF_MAP,
2833 NFCSTATUS_INVALID_FORMAT);
2834 }
2835 return (Result);
2836 }


ということで、まとめるとこんな感じ


13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
1* ----------- 00 00 00 00 00/0f 00/01 --------------
↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
ver | Nbw | Nmaxb2 WriteFlag RdWrFlag CheckSum
Nbr Nmaxb1


例えば、こんなの


13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
10 00 00 01 00 00 00 00 00 00 01 00 00 00 00 12
↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
ver | Nbw | Nmaxb2 WriteFlag RdWrFlag CheckSum
Nbr Nmaxb1


ここで読んだ内容は phFriNfc_Felica_AttrInfo
に突っ込まれます。

2866 status = phFriNfc_MapTool_ChkSpcVer(NdefMap,
2867 PH_NFCFRI_NDEFMAP_FELI_VERSION_INDEX);

で、バージョンの値をチェックし、

2939 status = phFriNfc_Felica_HCalCheckSum(NdefMap->SendRecvBuf,

で、チェックサムを検証しています。


2848 /*!
2849 * \brief On successful read attribute blk information, this function validates and stores the
2850 * Attribute informations in to the context.
2851 */
2852 static NFCSTATUS phFriNfc_Felica_HUpdateAttrInfo(phFriNfc_NdefMap_t *NdefMap)
2853 {
2854 NFCSTATUS status = NFCSTATUS_PENDING;
2855 uint8_t CRFlag = FALSE,
2856 Nmaxb1, Nmaxb2 = 0,
2857 ChkSum1 = 0, ChkSum2=0;
2858
2859 uint16_t Nmaxblk = 0,
2860 RecvChkSum=0,
2861 NdefBlk = 0;
2862 uint32_t DataLen =0;
2863
2864
2865 /* Validate T3VNo and NFCDevVNo */
2866 status = phFriNfc_MapTool_ChkSpcVer(NdefMap,
2867 PH_NFCFRI_NDEFMAP_FELI_VERSION_INDEX);
2868 if ( status != NFCSTATUS_SUCCESS )
2869 {
2870 CRFlag = TRUE;
2871 }
2872 else
2873 {
2874 /* get the Nmaxb from the receive buffer*/
2875 Nmaxb1 = NdefMap->SendRecvBuf[16];
2876 Nmaxb2 = NdefMap->SendRecvBuf[17];
2877
2878 Nmaxblk = (((uint16_t)Nmaxb1 << 8) | (Nmaxb2 & 0x00ff));
2879
2880 if ( Nmaxblk != 0 )
2881 {
2882 /* check the Nbr against the Nmaxb*/
2883 if ( NdefMap->SendRecvBuf[14] > Nmaxblk )
2884 {
2885 CRFlag = TRUE;
2886 }
2887 else
2888 {
2889 /*check Nbw > Nmaxb*/
2890 /*check the write flag validity*/
2891 /*check for the RFU bytes validity*/
2892 if ( (NdefMap->SendRecvBuf[15] > Nmaxblk) ||
2893 ((NdefMap->SendRecvBuf[22] != 0x00) && (NdefMap->SendRecvBuf[22] !=0x0f ))||
2894 ( (NdefMap->SendRecvBuf[23] != 0x00) && (NdefMap->SendRecvBuf[23] !=0x01 ))||
2895 ( NdefMap->SendRecvBuf[18] != 0x00) ||
2896 ( NdefMap->SendRecvBuf[19] != 0x00) ||
2897 ( NdefMap->SendRecvBuf[20] != 0x00) ||
2898 ( NdefMap->SendRecvBuf[21] != 0x00))
2899
2900 {
2901 CRFlag = TRUE;
2902 }
2903 else
2904 {
2905 /* check the validity of the actual ndef data len*/
2906 PH_NFCFRI_NDEFMAP_FELI_CAL_LEN_BYTES( NdefMap->SendRecvBuf[24],
2907 NdefMap->SendRecvBuf[25],
2908 NdefMap->SendRecvBuf[26],
2909 DataLen);
2910
2911
2912 /* Calculate Nbc*/
2913 NdefBlk = (uint16_t )((( DataLen % 16) == 0 ) ? (DataLen >> 4) : ((DataLen >> 4) +1));
2914
2915 /* check Nbc against Nmaxb*/
2916 if ((NdefBlk > Nmaxblk))
2917 {
2918 CRFlag = TRUE;
2919 }
2920 else
2921 {
2922 /*Store the attribute information in phFriNfc_Felica_AttrInfo*/
2923 NdefMap->FelicaAttrInfo.Version = NdefMap->SendRecvBuf[PH_NFCFRI_NDEFMAP_FELI_VERSION_INDEX];
2924 NdefMap->FelicaAttrInfo.Nbr = NdefMap->SendRecvBuf[14];
2925 NdefMap->FelicaAttrInfo.Nbw = NdefMap->SendRecvBuf[15];
2926
2927 NdefMap->FelicaAttrInfo.Nmaxb = Nmaxblk;
2928
2929 NdefMap->FelicaAttrInfo.WriteFlag = NdefMap->SendRecvBuf[22];
2930 NdefMap->FelicaAttrInfo.RdWrFlag = NdefMap->SendRecvBuf[23];
2931
2932 /* Get CheckSum*/
2933 ChkSum1 = NdefMap->SendRecvBuf[27];
2934 ChkSum2 = NdefMap->SendRecvBuf[28];
2935
2936 RecvChkSum = (((uint16_t)ChkSum1 << 8) | (ChkSum2 & 0x00ff));
2937
2938 /* Check the check sum validity?*/
2939 status = phFriNfc_Felica_HCalCheckSum(NdefMap->SendRecvBuf,
2940 PH_NFCFRI_NDEFMAP_FELI_VERSION_INDEX,
2941 26,
2942 RecvChkSum);
2943 if ( status != NFCSTATUS_SUCCESS )
2944 {
2945 CRFlag = TRUE;
2946 }
2947 else
2948 {
2949 /*check RW Flag Access Rights*/
2950 /* set to read only cannot write*/
2951 if ( NdefMap->FelicaAttrInfo.RdWrFlag == 0x00 )
2952 {
2953 NdefMap->CardState = PH_NDEFMAP_CARD_STATE_READ_ONLY;
2954 }
2955 else
2956 {
2957 NdefMap->CardState = PH_NDEFMAP_CARD_STATE_READ_WRITE;
2958 }
2959
2960 NdefMap->FelicaAttrInfo.LenBytes[0] = NdefMap->SendRecvBuf[24];
2961 NdefMap->FelicaAttrInfo.LenBytes[1] = NdefMap->SendRecvBuf[25];
2962 NdefMap->FelicaAttrInfo.LenBytes[2] = NdefMap->SendRecvBuf[26];
2963 status = PHNFCSTVAL(CID_NFC_NONE,NFCSTATUS_SUCCESS);
2964 }
2965 }
2966 }
2967 }
2968 }
2969 else
2970 {
2971 CRFlag = TRUE;
2972 }
2973 }
2974 if ( (status == NFCSTATUS_INVALID_FORMAT ) && (CRFlag == TRUE ))
2975 {
2976 NdefMap->CardState = PH_NDEFMAP_CARD_STATE_INVALID;
2977 }
2978 if ( CRFlag == TRUE )
2979 {
2980 /*Return Status Error Invalid Format*/
2981 status = PHNFCSTVAL(CID_FRI_NFC_NDEF_MAP,NFCSTATUS_INVALID_FORMAT);
2982 }
2983
2984 return (status);
2985 }



phFriNfc_MapTool_SetCardState も見ておきましょう。

phFriNfc_MapTools.c#phFriNfc_MapTool_SetCardState


  64 NFCSTATUS phFriNfc_MapTool_SetCardState(phFriNfc_NdefMap_t *NdefMap,
65 uint32_t Length)
66 {
    ...

110 }
111 Result = ((NdefMap->CardState ==
112 PH_NDEFMAP_CARD_STATE_INVALID)?
113 PHNFCSTVAL(CID_FRI_NFC_NDEF_MAP,
114 NFCSTATUS_NO_NDEF_SUPPORT):
115 Result);
116 return Result;
117 }

 
 ここで、 NdefMap->CardState が PH_NDEFMAP_CARD_STATE_INVALID だと NFCSTATUS_NO_NDEF_SUPPORT が帰るので
 NDEF としては認識してくれません。


ということで、Raw bytes をごりごりカードにかいて、NDEF として認識することができました。


 

2011年1月27日木曜日

Android 3.0 Platform Highlights - New UI Framework for creating great tablet apps -

New UI Framework for creating great tablet apps のまとめ

 ・ Activity の画面を Fragment というサブコンポーネントに
   分割できるようになった

 ・ holographic テーマが組み込まれた

 ・ 3D stack, serach box, a date/time picker, number picker,
   calendar, popup menu などの新しいwidget type が追加された
 
 ・ 3D stack や grid や list をフリップするようなホーム画面
   ウィジェットが追加された

 ・ 画面上部に Action Bar が付けられるようになった。カスタマイズも可能

 ・ notifications に新しく large and small icons, a title,
   a priority flag を含められるようになった

 ・ リストやグリッド用に複数選択モードが追加された

 ・ クリップボードを使ってコピー&ペーストができるようになった

 ・ drag and drop ができるようになった


ここから訳
--------------------------------------------------------------------------------

New Developer Features

Android 3.0 platform は大画面デバイスのアプリケーション対して最適にデザインされています。これらのデバイスですばらしいビジュアルとインタラクションエクスペリエンスを実現するために必要な全てのツールを開発者に提供します。

 ・すばらしいタブレットアプリを作るための新しい UI framework

 ・ハイパフォーマンス 2D & 3D グラフィックス

 ・マルチコアプロセッサアーキテクチャのサポート

 ・リッチなマルチメディアと接続性

 ・エンタープライズ向けの拡張機能

 ・既存アプリの互換性


New UI Framework for creating great tablet apps
 - すばらしいタブレットアプリを作るための新しい UI framework -


コンテンツとデザインの柔軟性をより細かく制御するための Activity fragments

Android 3.0 から、開発者はアプリケーションの Activities を Fragments と呼ばれるサブコンポーネントに分割できるようになりました。そして、これらを多様な方法で組み合わせることで、よりリッチでインタラクティブなエクスペリエンスを作ることができます。
例えば、アプリケーションはFragments のセットを使うことで、ユーザーが各ペインと独立にインタラクトできる真のマルチペインUIを作ることができます。Fragments は追加、削除、置換、Activity の内部へのダイナミックなアニメーションが可能で、それらはモジュール化され、複数の Activities に渡って再利用できます。
これらがモジュールなため、Fragments は開発者にとって、小さいスクリーンデバイスと同じくらい大きなスクリーンでも適切に実行できるアプリケーションを書く効果的な方法を提供します。

再デザインされた UI widget

Android 3.0 は、開発者が簡単に新しいタイプのコンテンツをアプリケーションに追加するための、アップデートされた UI widgets set を提供します。新しい UI widgets はタブレットや大きな画面で使うために再デザインされ、新しい holographic UI テーマが組み込まれています。いくつかの新しい widget types が使えるようになりました。例えば、 3D stack, serach box, a date/time picker, number picker, calendar, popup menu など。ほとんどの再デザインされた UI widgets はホーム画面上に表示される application widgets 内で remote vies として使うことができます。

ホーム画面ウィジェットの拡張

ホーム画面ウィジェットはユーザーに人気です。なぜなら、アプリケーション独自のデータをホーム画面から直接すすばやくアクセスすることができるからです。Android 3.0 はより多くのタイプのコンテンツとユーザーへの新しいインタラクションモードを提供することで、開発者のホーム画面ウィジェットを次のレベルへ引き上げます。
開発者はより多くの標準 UI widget タイプのホーム画面ウィジェットを使用できます。これにはユーザーが 3D スタックやグリッドやリスト上のコンテンツ集をフリップできる widgets が含まれます。ユーザーはスクロールのためのタッチジェスチャーや、コンテンツをフリップするなどの新しい方法でホーム画面ウィジェットとインタラクトできます。

Action Bar の永続化

プラットフォームは各アプリケーションに自身の Action Bar インスタンスを画面の上部に提供します。これにより、アプリケーションはユーザーに contextual options, widgets, status, navigation などへのクイックアクセスを提供できるようになります。アプリケーションは Action Bar インスタンスの表示テーマをカスタマイズすることができます。Action Bar によって、開発者はアプリケーションのより多くの機能をユーザーのなじみやすい位置にさらすことができます。また、複数の Activities や状態をまたぐアプリケーションの使用において単一のエクスペリエンスを提供できます。

よりリッチな通知(ノーティフィケーション)

ノーティフィけションは Android のユーザーエクスペリエンスにとってキーパートの 1つです。なぜなら、それによってアプリケーションはアップデートやステータス情報をユーザーにリアルタイムに示すことができるからです。Android 3.0 ではこの機能を拡張しています。開発者はよりリッチなコンテンツを含むことができ、より多くのプロパティをコントロールできるようになります。あたらしいビルダークラスによって、開発者は大きいアイコン、小さいアイコン、タイトル、プライオリティフラグ、その他のすでに使えるプロパティを含むノーティフィケーションを作成することができます。ノーティフィケーションは remote Views として現在使用可能な UI widgets の set を拡張することでより多くのタイプのコンテンツを表示できます。

複数選択、クリップボード、ドラッグアンドドロップ

プラットフォームは開発者が使える便利な新しいインタラクションモードを提供します。リストやグリッド内のアイテム集を管理するために、開発者は新しい複数選択モードを使うことができます。これにより、ユーザーは1つのアクションで複数のアイテムを選択できます。さらに、開発者は新しい system-wide クリップボードを使うことができます。これにより、ユーザーは簡単にデータタイプをクリップボードにコピーし、アプリケーションに出すことができます。ユーザーのファイルの管理や生成を簡単にするために、開発者は DragEvent フレームワークを通してドラッグアンドドロップインタラクションを追加することができるようになりました。

Android 3.0 Preview SDK

Android 3.0 Preview SDK

Android 3.0 は Android platform の次のメジャーリリースで、タブレットデバイスに最適化されています。我々は Preview SDK を提供します。これにより、アプリケーション開発や既に存在するアプリケーションを次期タブレットに最適化するスタートが切れます。

Preview SDK とは?

Android 3.0 preview SDK は次にくる Android 3.0 バージョンの開発目的だけのための early look です。

Preview SDK に含まれるもの:
  ・Android エミュレータを使うための early Android 3.0 system image
  ・Android 3.0 library の non-final APIs
  ・extra large Android Virtual Device 用の新しい
   WXGA エミュレータスキン
  ・Android 3.0 用の新しいドキュメント。完全なAPIリファレンス、
   新しいデベロッパーガイド、Android 3.0 と 2.3 の間の API
   differences report を含む。

注意点:
  ・Preivew SDK 内の APIs は最終的なものではありません。
   いくつかの APIs は最終的なSDKが使用可能になるときに、
   振る舞いや利用可能かどうかが変わる可能性があります。
  ・preview SDK に対してビルドしたアプリケーションを公開
   することはできません。Preview SDK に対してビルドした
   アプリケーションは Android エミュレータでのみ実行できます。
  ・developer.android.com のドキュメントは Android 3.0 の
   ドキュメントを含みません。Android 3.0 用の API
   reference と developer guides を読むには、
   Android 3.0 preview documentation を AVD nad SDK
   Manager からインストールする必要があります。

エミュレータのパフォーマンスについて

Android エミュレータは ARM instruction set architecture をあなたのコンピュータでをシミュレートしなければならないことに加え、WXGA screen がエミュレータの通常扱う画面よりもかなり大きいことで、エミュレータのパフォーマンスは通常よりかなり遅くなります。

特に、エミュレータの初期化時は遅くなり、ハードウェアによっては数分かかることがあります。エミュレータのブート時のユーザフィードバックは限られています。どうか我慢してホームスクリーンが現れるのを待ち続けてください。

我々はエミュレータでのパフォーマンス問題を解決するために頑張っています。そして、それは将来のリリースで改善されます。その間に、我々は開発者に新しいAPIへのアクセスと基本的なテスト環境を可能な限り早く提供したかったのです。

エミュレータでのパフォーマンスは Android 3.0 が走っている実際のデバイスでのアプリの速度やスピードには反映しないことを覚えていてください。しかし、依然としてエミュレータ上での開発とテストは、アプリケーションの表示や新しいプラットフォームでの機能を確かめるための重要なツールです。


preview で何ができる?

Preview SDK は既存のアプリケーションの新しいプラットフォームでのテストや、Android 3.0 API を使った新しいアプリケーションの開発を意図したものです。

あなたが、すでに Android アプリケーションを持っているなら、しなければならないことはありません。Android アプリケーションは常に forward-compatible (前方互換がある)です。もし、あなたのアプリケーションが Android API を正しく使っているなら、あなたのアプリケーションは余計なことをなにもせずとも Android 3.0 が走っているデバイス上で正しく動きます。しかし、適切なパフォーマンと良いエクスペリエンスが Android 3.0 タブレットで動作したときにユーザーに提供されることを確認するためには、次のことをお勧めします。

1. Android 3.0 でアプリケーションをテストする

単に Android 3.0 preview を自分の Android SDK にインストールし、AVD を使って Android 3.0 system image を作成し、あなたのアプリケーションをインストールし、いくつかのテストを実行してください。上記で言及したように、あなたのアプリケーションは期待どおりに実行されなければいけません。しかし、Activity layouts が大きな画面や他のアスペクト比では、あまり賢くなく期待通りに動作しないことを見つけるでしょう。

2. 新しい "Holographic" テーマを継承する

Android 3.0 はアップデートされた UI widgets のセットを提供します。これはタブレットのような大きな画面で使われることを目的として再デザインされたもので、新しい holographic テーマが組み込まれています。あなたの既存のアプリケーションはこの新しいデザインを簡単に継承することができます。そのためには、 エレメントの android:targetSdkVersion 属性に "Honeycomb" を設定します。

もし、android:targetSdkVersion 属性をアップグレードしたくない場合は、android:minSdkVersion に 9 以前を設定してください。こうすることで、あなたのアプリケーションは Android 2.3 の widget designs を使用し、holographic テーマを継承しません。

あなたのアプリケーションをシステム UIと合わせるために、新しい widget スタイルとシステムテーマを継承するように変更することを、我々は強く推奨します。しかし、そうすると以前のシステムテーマに基づいたあなたのアプリケーションの色やテキストデザインと競合するかもしれないことに注意してください。よって、holographic テーマを使うときは、あなたのアプリケーションUIを検査してください。

3. extra large screens 用の代替レイアウトを提供する

Supporting Multiple Screensガイドで議論したように Android 2.3 以降では extra large screens 用の代替レイアウトを提供するための xlarge resource qualifier をサポートします。
代替レイアウトを提供することで、extra large screen でアプリケーションが実行されるときにユーザーエクスペリエンスを改善することができます。これには、新しい APIs を使う必要がありません。

例えば、タブレット用の新しいレイアウトを作るときに考慮するべき点を並べてみました:

  ・Landscape layout:
      タブレットでの通常の向きは landscape (wide) です。
      そのため、あなたの Activites では wide viewing area
      などの適切なレイアウトが提供されているか確かめて
      ください。

  ・Button position:
      最もよく使われるボタンの位置は、両手でタブレットを
      つかんだときに簡単にアクセスできるところにあるか考える。

注意: xlarge screen 用の代替リソースを minSdkVersion を変更せずに提供することができます。例えば、もし代替レイアウトを res/layout-xlarge に追加しても、Android 1.5 などの古いバーションとの互換性は保たれます。このような xlarge screens をサポートしていない場合は、単にこのレイアウトディレクトリが無視されます。

一方、もし、新しいアプリケーションを開発するか、既存のアプリケーションを Android 3.0 で追加された APIs を使用してアップグレードしたい場合は、Android 3.0 preview platform に対して開発をスタートすることを奨励します。他の Android version と同じ方法で開始できます。

最初にすること--既存のアプリケーションをテストするか、新しいアプリをつくる--次のセクションの手順に従って Preview SDK をインストールしてください。


どうやって手に入れる?

Preview SDK を得るには、Android SDK と AVD manager を使ってダウンロードします。

もし、あなたが新しい Android 開発者なら、downloading the Android SDK starter packageから始めてください。

Android SDK and AVD Manager を起動して、次をインストールします:

  ・SDK Platform Android Honeycomb Preview
  ・Android SDK Tools, revision 9
  ・Android SDK Platform-tools, revision 2
  ・Documentation for Android 'Honeycomb' Preview
  ・Samples for SDK API Honeycomb Preview

注意: Honeycomb Preview SDK 用の documentation component をインストールしてください。Android 3.0 API の API reference は online では提供されていません。

これらのコンポーネントをインストールしたら、SDK ディレクトリを開いて、docs/sdk/ へ移動し、 index.html をブラウザで開いてください。



 

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 にあります。

 



 

2011年1月26日水曜日

Android USB で端末が2台つながった状態でそれぞれ adb logcat する方法

くごー先生ありがとうございます!

忘れそうだからメモっとく。

USB で端末が 2台接続されている状態で adb -d logcat すると「一個以上つながってますけど!」って怒られます


$ adb -d logcat
- waiting for device -
error: more than one device


こんなときは、adb devices で device id を調べて adb -s [device id] logcat で指定すればOK!


$ adb devices
List of devices attached
00000000000000EC device
11111111111100EC device

yuki@dynabook:~$ adb -s 00000000000000EC logcat



 

Edy Reader と nanaco Reader も公開しました。



Suica Reader に続いて Edy Reader と nanaco Reader も公開しました。
Android Market からダウンロードできます。

Android 2.3 以上で NFC に対応した端末が必要です。
# つまり、Nexus S ですけど、、、

残高と処理(支払い or チャージ)と日時が見れます。



やっぱり非公開APIをたたいてるので、(だからと言ってなにかあるわけじゃないですが)
ご使用は自己責任でお願いします。



 

2011年1月23日日曜日

2000年1月1日からの経過日数を日付に変換

メモ


import java.util.Calendar;

static public String getDate(int days) {

Calendar calendar = Calendar.getInstance();

calendar.set(2000, 0, 1);
calendar.add(Calendar.DATE, days);

return calendar.get(Calendar.YEAR) + "年" + (calendar.get(Calendar.MONTH) + 1) + "月" + calendar.get(Calendar.DATE) + "日";
}


days が 2000年1月1日からの経過日数。


 

2011年1月12日水曜日

Nexus S で Suica の乗車履歴がとれるアプリを公開しました。

Android 2.3 以上で NFC に対応している端末(いまのところ Nexus S だけだけど、、、)で Suica や PASMO などの交通ICカードの乗車履歴を表示するアプリ「Suica Reader」を公開しました。

ICOCA とかもOKなはず。




# 非公開APIをたたいてるので、自己責任で使用してください。

# 用法用量を守って正しくお使いください。

# モバイル Suica には対応してません

# ほぼデバッグ用のUIのままですが、おおめに見てください。(その内直すよ。きっと)

2011年1月10日月曜日

Android あらかじめ作成した SQLite database をアプリに取り込む

Android のサンプルやチュートリアルでは、アプリ実行時に SQLite データベースを作成してデータの追加や更新、削除などを行っているのがほとんどです。

しかし、あらかじめ作成しておいた SQLite database をアプリに仕込みたい場合があります。

そこで、ここでは sqlite3 など使って作成した自分の SQLite database ファイルを、アプリの asset に入れ、初回起動時にアプリのシステムデータベース領域にコピーする方法を紹介します。


1. SQLite database ファイルを用意する

私は Ubuntu 派なので普通に sqlite3 を使います。(Windows とか Mac はよくわかりません... これとか? SQLite Database Browser)

主テーブルの他に android_metadata という名前のテーブルを作成します。中身は locale です。主テーブルの primary key は _id という名前にします(Android が id を理解できる用にするためです)。

$ cat createTable.sql create table android_metadata ( locale text default 'en_US' ); create table sample_data ( _id integer primary key not null, name text, address text, tel text );
ちなみに日本は ja_JP です。


データベースとテーブルを作成します。
$ sqlite3 mysqlite_database.db < createTable.sql

作成したテーブルに CSVファイルをインポートします。
ここでは、

1,name1,address1,tel1
2,name2,address2,tel2
...

というデータを想定しています。

# 改行コードの "\r" や、各カラムの " 囲みを削除するには
#
# $ tr -d "\r" < old.csv | tr -d '"' > new.csv
#
# とかやると幸せになれるかも。
# 文字コードは nkf とかで。
# primary key 部分は awk とかで頑張ってみるとか。

sqlite3 -separator , mysqlite_database.db ".import mydata.csv sample_data"

なにも言われなければうまくいったかも
sqlite3 mysqlite_database.db "select * from sample_data where _id=1" とかで中身をチェックして入っていればOK。


2. 展開&コピーして Android アプリからアクセスできるようにする

1. で作った SQLite database ファイルを asset に入れ、SQLiteOpenHelper を継承したクラスを作成します。

public class DataBaseHelper extends SQLiteOpenHelper { private static String DB_NAME = "my_database"; private static String DB_NAME_ASSET = "my_database.db"; private static final int DATABASE_VERSION = 17; private SQLiteDatabase mDatabase; private final Context mContext; private final File mDatabasePath; public DataBaseHelper(Context context) { super(context, DB_NAME, null, DATABASE_VERSION); mContext = context; mDatabasePath = mContext.getDatabasePath(DB_NAME); } /** * asset に格納したデータベースをコピーするための空のデータベースを作成する */ public void createEmptyDataBase() throws IOException { boolean dbExist = checkDataBaseExists(); if (dbExist) { // すでにデータベースは作成されている } else { // このメソッドを呼ぶことで、空のデータベースがアプリのデフォルトシステムパスに作られる getReadableDatabase(); try { // asset に格納したデータベースをコピーする copyDataBaseFromAsset(); String dbPath = mDatabasePath.getAbsolutePath(); SQLiteDatabase checkDb = null; try { checkDb = SQLiteDatabase.openDatabase(dbPath, null, SQLiteDatabase.OPEN_READWRITE); } catch (SQLiteException e) { } if (checkDb != null) { checkDb.setVersion(DATABASE_VERSION); checkDb.close(); } } catch (IOException e) { throw new Error("Error copying database"); } } } /** * 再コピーを防止するために、すでにデータベースがあるかどうか判定する * * @return 存在している場合 {@code true} */ private boolean checkDataBaseExists() { String dbPath = mDatabasePath.getAbsolutePath(); SQLiteDatabase checkDb = null; try { checkDb = SQLiteDatabase.openDatabase(dbPath, null, SQLiteDatabase.OPEN_READONLY); } catch (SQLiteException e) { // データベースはまだ存在していない } if (checkDb == null) { // データベースはまだ存在していない return false; } int oldVersion = checkDb.getVersion(); int newVersion = DATABASE_VERSION; if (oldVersion == newVersion) { // データベースは存在していて最新 checkDb.close(); return true; } // データベースが存在していて最新ではないので削除 File f = new File(dbPath); f.delete(); return false; } /** * asset に格納したデーだベースをデフォルトのデータベースパスに作成したからのデータベースにコピーする */ private void copyDataBaseFromAsset() throws IOException{ // asset 内のデータベースファイルにアクセス InputStream mInput = mContext.getAssets().open(DB_NAME_ASSET); // デフォルトのデータベースパスに作成した空のDB OutputStream mOutput = new FileOutputStream(mDatabasePath); // コピー byte[] buffer = new byte[1024]; int size; while ((size = mInput.read(buffer)) > 0) { mOutput.write(buffer, 0, size); } // Close the streams mOutput.flush(); mOutput.close(); mInput.close(); } public SQLiteDatabase openDataBase() throws SQLException { return getReadableDatabase(); } @Override public void onCreate(SQLiteDatabase db) { } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } @Override public synchronized void close() { if(mDataBase != null) mDataBase.close(); super.close(); } }


あとは、普通にアクセスする

private DataBaseHelper mDbHelper; private SQLiteDatabase db; private StationCodeDao mStationCodeDao; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setDatabase(); } @Override public void onDestroy() { db.close(); super.onDestroy(); } private void setDatabase() { mDbHelper = new DataBaseHelper(this); try { mDbHelper.createEmptyDataBase(); db = mDbHelper.openDataBase(); } catch (IOException ioe) { throw new Error("Unable to create database"); } catch(SQLException sqle){ throw sqle; } } private static final String[] COLUMNS = {"_id", "name", "address", "tel"}; private Cursor findData(int id) { Cursor cursor = db.query(TABLE_NAME, COLUMNS, "where _id=" + id, null, null, null, COLUMN_ID); return cursor; }

---------------------------------------

2011年2月4日 追記

assets に入れた db が 1MB 以上の場合、うまくいかないことがあるみたいです。
そんなときは、itog さんのブログ

apkに1MB以上のデータベースファイルを含める - Hacking My Way ~ itogのhack日記 -

を参考に、db を圧縮するなり分割するなりすると、うまくいくようです。


---------------------------------------

2013年10月22日 追記

データベースのパスを固定から Context の getDatabasePath() を使うように修正しました。
バージョン番号を使って、アップデート時にデータベースを再度 Asset からコピーできるようにしました。



 

2011年1月8日土曜日

あけましておめでとうございます。

今頃かよ。というツッコミはおいていて。。。

明後日、1月9日に Android Bazaar and Conference というイベントがあります。



前回の ABC Spring でも女子部Tシャツの販売をしていたので、もしかしたら、、、今回も、、、

なんたって、あの Andy Rubin も持ってるんですから、女子部Tシャツw




最初に参加したのは、ABC 2009 Spring でした。

木南さんの Handson に参加したばっかりにおかげで、立派な andronjo になれましたw

その次の ABC 2009 Winter がきっかけで女子部ができたんですよ。

ABC 2009 Spring でも女性の参加者が少なかったんですけど、ABC 2009 Winter になってもまったく増えてなかったので、これはまずいだろうと。

それで、そのとき知り合った現部長の矢野さんとか、GTUG で知り合ってた lychee さんとかと意気投合して女子部作ることになったんですよ。

なつかしいなぁ。



それでは、みなさま明後日お会いしましょう〜。

2011年1月6日木曜日

NDEF メモ

NDEF (NFC Data Exchange Format)

 ・NDEF Tag には 0 個以上の NDEF Messages が含まれる
   ・通常は NDEF Tag に含まれる NDEF Message は 1個
 ・NDEF Message には複数の NDEF Records が含まれる

 * NdefTag の fields は Tag を読んだときにセットされる immutable data だが、このクラスから帰ってくる配列は clone ではないため、modify しないように気をつけること


NDEF Tag - NDEF Message
|
+ NDEF Message - NDEF Record
... |
+ NDEF Record
|
...


 ・NDEF Record には TNF, type, ID, flag, payload が含まれる
 ・payload の長さが 0xff 未満の時 short record という

■ TNF
 ・3 bit = 0x00 - 0x07

 ・0x00 : TNF_EMPTY
   Empty
   (type, ID, paylaod は空でなければならない)

 ・0x01 : TNF_WELL_KNOWN
   NFC Forum well-known type [NFC RTD]
   (type field は RTD type name format を使う
    (RTD_TEXT, RTD_URI など))

 ・0x02 : TNF_MIME_MEDIA
   Media-type as defined in RFC 2046 [RFC 2046]
   (type field は RFC 2046 で定義された
    media type BNF construct 形式の値を含む)

 ・0x03 : TNF_ABSOLUTE_URI
   Absolute URI as defined in RFC 3986 [RFC 3986]
   (RFC 3986 で定義された absolute-URI
    BNF construct 形式の値を含む)

 ・0x04 : TNF_EXTERNAL_TYPE
   NFC Forum external type [NFC RTD]
   (type field は RTD 外部名定義に従った値を含む、
    RTD_TEXT や RTD_URI をこのTNFと使うべきではない)

 ・0x05 : TNF_UNKNOWN
   Unknown
   ("application/octet-scream" MIME type のようなもの、
    payload はNDEF Message にエンコードされていない)

 ・0x06 : TNF_UNCHANGED
   Unchanged
   (payload は chunked NDEF Record の中間または最終の chunk、
    payload type は最初の chunk で指定され、続く chunks は
    TNF_UNCHANGED で type field が空でなければならない。
    それ以外の場面で TNF_UNCHANGED を使ってはならない)

 ・0x07 : TNF_RESERVED
   Reserved
   (Reserved TNF type、NFC Forum NDEF Specification v1.0 は
    この TNF を TNF_UNKNOWN として処理することを推奨している)


■ type
 ・0 - 255 bytes
 ・空の場合もある

 ・0x54 : RTD_TEXT
   ("T" TNF_WELL_KNOWN と一緒に使う)

 ・0x55 : RTD_URI
   ("U" TNF_WELL_KNOWN と一緒に使う)

 ・{0x53, 0x70} : RTD_SMART_POSTER
   ("Sp" TNF_WELL_KNOWN と一緒に使う)

 ・{0x61, 0x63} : RTD_ALTERNATIVE_CARRIER
   ("ac" TNF_WELL_KNOWN と一緒に使う)

 ・{0x48, 0x63} : RTD_HANDOVER_CARRIER
   ("Hc" TNF_WELL_KNOWN と一緒に使う)

 ・{0x48, 0x72} : RTD_HANDOVER_REQUEST
   ("Hr" TNF_WELL_KNOWN と一緒に使う)

 ・{0x48, 0x73} : RTD_HANDOVER_SELECT
   ("Hs" TNF_WELL_KNOWN と一緒に使う)


■ ID
 ・0 - 255 bytes
 ・空の場合もある


■ payload
 ・< 2^32 -1 bytes
 ・空の場合もある


■ flag
 ・5 bit
 ・b4 : MB flag (最初の record のとき 1)
 ・b3 : ME flag (最後の record のとき 1)
 ・b2 : CF flag
 ・b1 : SR flag (short record のとき 1)
 ・b0 : IL flag (id length (id) が提供される場合 1,
         提供されない場合は ID Length field と
         ID field は省略される)


Record Layout 10 byte

b7 b6 b5 b4 b3 b2 b1 b0
| flag | TNF |
| type length |
| payload length 3 |
| payload length 2 |
| payload length 1 |
| payload length 0 |
| id length |
| type |
| id |
| payload |



NDEF short record
 ・SR flag が 1
 ・the PAYLOAD_LENGTH field は 8 byte
 ・payloads or chunks は 255 byte まで

NDEF record chunk
 ・pull payload の 一部
 ・NDEF record は full payload よりも payload の chunk を含むことが多い
 ・chunked payload の最後の record chunk 以外は CF flag が 1
 ・中間と最後の chunked record の TYPE_LENGTH と IL fields は 0x06 (unchanged) でなければならない
 ・PAYLOAD_LENGTH が 0 かそれ以外かにかかわらず、TYPE fields は entire chunked payload の type でなければならない

NDEF chunked payload
 ・Application data は異なる NDEF record で運ばれるの複数の chunk に分けられる
 ・最後の record を除いて CF flag は 1
 ・single NDEF record に入らない大規模な entity 向け
 ・payload size が予め決まっていない動的に生成されるコンテンツの転送向け
 ・コンテンツの多重送信やストリーミングをサポートすることではない

The payload type
 ・コンテンツ部分のタイプ
 ・URIs [RFC 3986]
 ・MIME media type constructs [RFC 2046]
   ・image/png
   ・image/jpeg
   ・message/http
   ・application/xml; charset=“utf-16" など
 ・NFC-specific type format
 ・文字コードは US-ASCII

The payload identifier
 ・付加的な識別子
 ・URI 用に応じた識別子や、絶対的な識別子がある
 ・他の payload との cross-reference 用 URI linking technology をサポートできる

NDEF Encapsulation Constructs
 ・NDEF message で運ばれる NDEF records に数に制限はない
 ・NDEF message を直接ネストすることはできない
 ・NDEF record 内に Full NDEF message としてネストすることは可能





 

2011年1月4日火曜日

NFC メモ

なんか、混沌としてきたので、いったん出す。

以降はただのメモ
・Android NFC のコード(@hide とか native 側) とかには felica って名前の付いた定数とか関数とかたくさんある。

・多分 polling して felica を認識してるときのコマンドは
0x00 0xff 0xff 0x01 0x00

  ・REQ コマンドの 0x00, phHal_eFelicaCmdList の phHal_eFelica_Check じゃないかなぁ
  ・システムコードがかえってきてるから多分リクエストコードは 0x01 と予想
  ・システムコードに依存しないで polling してると思うからシステムコードは 0xffff と予想
  ・多分タイムスロットは 0 だと思うから 0x00 と予想


・リフレクションで transceive を呼び出して felica と通信できないかと試したけど、NFCSTATUS_PENDING が帰ってきてよくわからない

  たぶん
  ・ コマンドが認識できなくて PICC がレスポンスを返していない?
     ・コマンドが間違い?
     ・プロトコルが間違い?
     ・Android NFC の transceive では 0xF0 という Falica Raw コマンド?らしき命名のがつかわれているので、read コマンドがそもそもできない?というか 0xF0 って JISX6319-4 の表10にないんだけど、、、なくはないか任意選択のコマンドってことなのか?
     ・phHal_eFelicaCmdList
  ・ IDm が認識できなくて PICC がレスポンスを返していない?
     ・転送データの形式が間違い?
     ・プロトコルが間違い?

・基本的には PICC 側にデータを投げて、仕掛けた callback で結果をうけとっている

・CRCは Android 側で付加してくれる?それとも transceive のデータに含む?
  ・ polling してるんだから、Android 側のような気もしないでもないのだが。。。


--------------------------------------------------------------

参考ページ

vvacame さんの togatter

Android 2.3 GingerBread NFCをやってみる - TOPGATE Google関連技術サイト -

NFC とは?

NFC Forum

NFC(Near Field Communication)ってナニ?/株式会社トッパンTDKレーベル -

Felica -Wikipedia-

MIFARE -Wikipedia-

PaSoRi -Wikipedia-

JISX6319-4

libnfc

IDm

Felica

IC SFCard Fan

規格採用例
Mifare® (ISO/IEC 14443 Type A)/td>ICテレフォンカード、たばこカードのほか、欧州では広く普及
ISO/IEC 14443 Type B住民基本台帳カード、社員証、学生証など
FeliCa™Suica、Edy、PASMO、ICOCA、おサイフケータイなど、国内では交通系カードや電子マネーとして広く普及
ISO/IEC 15693物流、小売などのICタグ

※ FeliCa™ はソニーの登録商標です。
※MIFARE®はNXPセミコンダクターズの登録商標です。


NFC基本機能
●ISO/IEC18092、FeliCa™、MIFARE® (ISO/IEC 14443 Type-A) 、 そしてISO/IEC14443-4(T=CL) のICカード/タグといった非接触ICカードとして機能する、カードエミュレーション機能
●上記規格に対応するICカード・タグの読み書きができる、リーダ・ライタエミュレーション機能
●NFC対応の機器間で、212kbpsや424kbpsでの通信ができる、端末間通信機能
NFC拡張機能
●NFC対応の機器間で、BluetoothやWiFiのペアリングができる、NFC端末間ペアリング機能


# 大学の学生証(東工大)は Nexus S で反応しませんでした。
# suica, pasumo, edy は反応、felica として認証されてました。
# 武蔵小杉の駐輪場カードは反応したが、RawTarget は other でした。


■ Felica メモ

・ICチップ内部のメモリは16バイト固定長のレコードのみがサポートされている
・変調は ASK10% 、マンチェスタ(Manchester)符号を採用
・暗号処理
  ・相互認証にトリプルDES
  ・通信路にDESもしくはトリプルDESを利用
  ・Dualカードタイプ(接触/非接触)では公開鍵暗号方式の処理が可能なものもある

・FeliCaポート/パソリを使用したソフトウェアを開発するためにはSDK For FeliCaが必要
・SDK For FeliCa のバージョン
  ・Lite : 平文通信を利用したアプリケーションを開発できる。
  ・Professional : セキュリティ機能を利用したアプリケーションを開発できる。
  ・Enterprise : 上記機能に加え、カードの発行(フォーマット)機能を備える。

SDK For FeliCa の wiki

・SDK For FeliCa は法人向けであり個人では購入することはできない
・個人レベルで FeliCa 対応アプリケーションを開発するものとして felicaliblibpasori が有志により開発されている

felicalib
libpafe

--------------------------------------------------------------

ソケット関係

NFC Forum,機器間の相互運用の促進とピア・ツー・ピア通信を実現する仕様を発表

NFC Forum Announces Two New Specifications to Foster Device Interoperability and Peer-to-Peer Device Communication

--------------------------------------------------------------
2011/01/25 追記

# Felica じゃなくて FeliCa なんだね。C が大文字。知らなかった。

FeliCa Lite の特徴
 ・小型化
 ・セキュリティー機能を簡易化
   ・FeliCa Liteはサーバーからの片方向認証のみ
 ・シンプルなファイルシステム
 ・FeliCa Standardとの下位互換性
 ・低コスト
 ・ユーザーメモリー領域は、1ブロック16byteが14ブロック分の224byte

その他リンクメモ
 ・http://www.mail-archive.com/android-developers@googlegroups.com/msg138446.html

 ・第481回:iC通信とは - ケータイ Watch -

コードメモ
 ・com_android_nfc_NativeLlcpServiceSocket.cpp

 ・NfcService.java

 ・com_android_nfc_NativeNfcManager.cpp

 ・com_android_nfc.cpp

 ・INfcTag.aidl

 ・NativeNfcManager.java

 ・phLibNfc_initiator.c

 ・phLibNfc_SE.c

 ・phLibNfc_discovery.c

 ・NativeLlcpConnectionlessSocket.java
 
 ・phHal4Nfc_ADD.c

2011年1月2日日曜日

Android Overscroll を使う簡単な方法

羊さんが素敵なエントリを書いてくれてました「OverScrollでListViewをビョーンってする方法 - hdk_embeddedの日記 -

でも、やっぱりフレームワークの ListView とか ScrollView で on/off できないってことはないんじゃないのかな?と思って調べてみました。

ScrollView の onTouchEvent メソッドのなかでは、ちゃんと overScrollBy メソッドが呼ばれています。

android.wdiget.ScrollView

public boolean onTouchEvent(MotionEvent ev) {

...

case MotionEvent.ACTION_MOVE:
if (mIsBeingDragged) {
// Scroll to follow the motion event
final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
final float y = ev.getY(activePointerIndex);
final int deltaY = (int) (mLastMotionY - y);
mLastMotionY = y;

final int oldX = mScrollX;
final int oldY = mScrollY;
final int range = getScrollRange();
if (overScrollBy(0, deltaY, 0, mScrollY, 0, range,
0, mOverscrollDistance, true)) {
// Break our velocity if we hit a scroll barrier.
mVelocityTracker.clear();
}
onScrollChanged(mScrollX, mScrollY, oldX, oldY);

final int overscrollMode = getOverScrollMode();
if (overscrollMode == OVER_SCROLL_ALWAYS ||
(overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0)) {
final int pulledToY = oldY + deltaY;
if (pulledToY < 0) {
mEdgeGlowTop.onPull((float) deltaY / getHeight());
if (!mEdgeGlowBottom.isFinished()) {
mEdgeGlowBottom.onRelease();
}
} else if (pulledToY > range) {
mEdgeGlowBottom.onPull((float) deltaY / getHeight());
if (!mEdgeGlowTop.isFinished()) {
mEdgeGlowTop.onRelease();
}
}
if (mEdgeGlowTop != null
&& (!mEdgeGlowTop.isFinished() ||
!mEdgeGlowBottom.isFinished())) {
invalidate();
}
}
}
break;



if (overScrollBy(0, deltaY, 0, mScrollY, 0, range, 0, mOverscrollDistance, true))

という部分です。

この overScrollBy メソッドの 8番目の引数がキモです。
overScrollBy メソッドのリファレンスには

maxOverScrollX Number of pixels to overscroll by in either direction along the X axis.

と書いてあります。つまり、これがオーバースクロールする距離ということです。
では、この mOverscrollDistance には何が入っているでしょう?

android.wdiget.ScrollView

private void initScrollView() {
mScroller = new OverScroller(getContext());
setFocusable(true);
setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
setWillNotDraw(false);
final ViewConfiguration configuration = ViewConfiguration.get(mContext);
mTouchSlop = configuration.getScaledTouchSlop();
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
mOverscrollDistance = configuration.getScaledOverscrollDistance();
mOverflingDistance = configuration.getScaledOverflingDistance();
}


このように、initScrollView メソッドのなかで、 mOverscrollDistance に ViewConfiguration の getScaledOverscrollDistance メソッドで取得した値を入れています。

では、 ViewConfiguration クラスを見てみましょう。

android.view.ViewConfiguration

package android.view;

import android.content.Context;
import android.util.DisplayMetrics;
import android.util.SparseArray;

/**
* Contains methods to standard constants used in the UI for timeouts, sizes, and distances.
*/
public class ViewConfiguration {

...

/**
* Max distance to overscroll for edge effects
*/
private static final int OVERSCROLL_DISTANCE = 0;

/**
* Max distance to overfling for edge effects
*/
private static final int OVERFLING_DISTANCE = 4;

...

private ViewConfiguration(Context context) {
final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
final float density = metrics.density;

mEdgeSlop = (int) (density * EDGE_SLOP + 0.5f);
mFadingEdgeLength = (int) (density * FADING_EDGE_LENGTH + 0.5f);
mMinimumFlingVelocity = (int) (density * MINIMUM_FLING_VELOCITY + 0.5f);
mMaximumFlingVelocity = (int) (density * MAXIMUM_FLING_VELOCITY + 0.5f);
mScrollbarSize = (int) (density * SCROLL_BAR_SIZE + 0.5f);
mTouchSlop = (int) (density * TOUCH_SLOP + 0.5f);
mPagingTouchSlop = (int) (density * PAGING_TOUCH_SLOP + 0.5f);
mDoubleTapSlop = (int) (density * DOUBLE_TAP_SLOP + 0.5f);
mWindowTouchSlop = (int) (density * WINDOW_TOUCH_SLOP + 0.5f);

// Size of the screen in bytes, in ARGB_8888 format
mMaximumDrawingCacheSize = 4 * metrics.widthPixels * metrics.heightPixels;

mOverscrollDistance = (int) (density * OVERSCROLL_DISTANCE + 0.5f);
mOverflingDistance = (int) (density * OVERFLING_DISTANCE + 0.5f);
}

...

public int getScaledOverscrollDistance() {
return mOverscrollDistance;
}

...

}


なんと、OVERSCROLL_DISTANCE が 0 じゃないですか!
そりゃあオーバースクロールしないですよ。。。

AbsListView でもまったく同じことをしてるので、やっぱり mOverscrollDistance は 0 です。。。


はっ!ということは、単に overScrollBy メソッドを override して mOverscrollDistance 部分に 0 以外の値を入れればいいだけかも。

ということでやってみた。


package yanzm.example.overscroll;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.ListView;

public class MyListView extends ListView {

private int mOverscrollDistance = 200;

public MyListView(Context context) {
super(context);
}

public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
}

public MyListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}


@Override
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, mOverscrollDistance, isTouchEvent);
}
}


overScrollBy メソッドを override して、第8引数に 0 以外の値(ここでは 200) を渡すだけ!


<?xml version="1.0" encoding="utf-8"?>
<yanzm.example.overscroll.MyListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"

android:overScrollMode="always"
android:overScrollHeader="#000000"
android:overScrollFooter="#000000"
/>


この overScrollHeader と overScrollFooter がビローンってしたときの領域の色です。(リソースIDを指定すれば画像も可能 android:overScrollHeader="@drawable/icon" とか)


package yanzm.example.overscroll;

import android.app.ListActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;

public class MyActivity extends ListActivity {

String[] mData = {
"Apple",
"Banana",
"Grape",
"Lemon",
"Melon",
"Orange",
"Peach",
"Water Melon",
};

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.list);

ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.list_item, mData);
setListAdapter(adapter);
}
}


調子にのりましたw



Android Bazaar and Conference 2011 で女子部セッションあります。
「DJモグタソのオールナイトヒャッハー」
# すっごい面白いよ。


 

2011年1月1日土曜日

Android PreferenceActivity で保存する SharedPreference の名前

メモ

PreferenceActivity で保存する SharedPreference の名前は

「パッケージ名 + _preferences」

になる。


package yanzm.sample.sharedpreference;

public class MyPreference extends PreferenceActivity {

@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.pref);

String preferenceName = getPreferenceManager().getSharedPreferencesName();

Log.d("MyPreference", preferenceName);
}
}


D/MyPreference(21223): yanzm.sample.sharedpreference_preferences