본문 바로가기
Programming/Android Java

Android NFC

by 개Foot/Dog발?! 2014. 9. 26.

URL : http://blog.daum.net/creazier/15309926


.....


안드로이드 NFC

 

.....안드로이드 버전 2.3 (진저브레드) 부터 NFC 기능이 추가됐습니다. 그 후로 몇 번의 버전 업에 따라 NFC API에도 변화가 있었습니다. 안드로이드 버전에 따라 NFC API기 지원하는 기능을 정리해보면, 아래와 같습니다.


버전

 지원 기능

 2.3

 - 단말의 NFC 기능 지원 여부 판단
 - RFID 태그 인식
 - NDEF (NFC 태그에 데이터를 저장하는 포맷) 메시지 해석

 2.3.3 

 - RFID 태그 종류별 처리
 - 태그 전달 시스템 (Tag Dispatch System) 확립
 - P2P 지원

 4.0

 - Android Beam 지원

 

RFID 태그 인식이나 NDEF 처리, 태그 전달 시스템 등은 모두 '태그 읽기/쓰기 모드'에 해당합니다. P2P 나 Android Beam은 'P2P 모드'에 해당합니다. '카드 모드'에 해당하는 기능에 대한 지원은 NFC API에는 없습니다. 

(일반 애플리케이션 개발자가 사용할 수 있는 API가 없다는 의미이고, 단말에 기능이 없다는 뜻은 아닙니다.)

* 카드에뮬레이션은 현재 Android Kitkat 4.4에서 인터페이스(HCE)를 지원하고 있지만, 완벽하게 지원하지 않고 구현부는 제조사 역량에 따라 구현하도록 되어 있다. 특히 이슈는 지난번에도 언급하였지만, SE(Security Element)이다.


NFC 애플리케이션 개발을 위해 알아야 하는 것들

*NFC 규격을 알야 하는 내용인데, 구체적인 것은 다른 게시물을 참고하자

.....



Hello NFC


AndroidManifest.xml 에 NFC 기능/권한 추가

 

애플리케이션이 NFC 기능을 사용하기 위해서, AndroidManifest.xml 에 다음과 같이 <uses-feature> 와 <uses-permission> 항목을 추가합니다.


<uses-feature android:name="android.hardware.nfc" required="false" />

<uses-permission android:name="android.permission.NFC" />  


<uses-feature>는 이 애플리케이션이 NFC 기능을 사용한다는 것을 의미합니다. 

android:required 속성을 true로 하면 

Google Play에서 NFC 기능을 가진 단말에서만 애플리케이션이 보이도록 할 수도 있습니다. 

<uses-permission>은 애플리케이션이 NFC 접근 권한을 필요로 한다는 의미이고, 애플리케이션 설치 시 사용자에 의해 권한 확인을 받게 됩니다. 



NfcAdapter


NFC 단말에 태그가 인식되면, Intent를 통해서 Activity로 전달됩니다. 

Activity가 이 Intent를 받기 위해서는 NfcAdapter 클래스의 enableForegroundDispatch(..) 를 이용합니다. 

* foregroundDispatch()는 액티비티에서 이 메서드를 사용해 enable시킨 액티비티가 NFC태깅을 통한 NDEFMessage를 우선적으로 받겠다고 설정하는 것이며, 

이를 하지 아니할 경우 반드시 앱이 실행되어 액티비티가 화면에 보이지 않아도, 

AndroidManifest.xml에 NFC관련한 Intent Action들(다른 게시물 참고)과 필요에 따라서 MIME Type을 등록한 앱을 대상으로 앱이 실행되게 된다.


public void enableForegroundDispatch(Activity activity, PendingIntent pendingIntent, IntentFilter[] filters, String[][] techLists)

 

activity는 Intent를 전달 받을 Activity이고, pendingIntent는 전달할 때 사용할 intent를 갖고 있는 PendingIntent 객체입니다. PendingIntent는 나중에 전달할 intent를 갖고 있는 녀석 정도로 생각하면 됩니다.

 

말하자면 'NFC 태그가 인식되면, 이 PendingIntent 안에 있는 intent를 전달해 달라'는 의미입니다.

filters와 techLists를 이용해 인식할 태그의 종류를 지정할 수 있는데, 모두 null로 하면 모든 태그를 인식하게 됩니다.

 

이 메소드를 호출하기 전에 NfcAdapter 객체와 PendingIntent 객체가 필요하므로, onCreate(..) 에서 이 두 객체를 아래와 같이 생성합니다.


private NfcAdapter nfcAdapter;

private PendingIntent pendingIntent;


@Override

public void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);


    nfcAdapter = NfcAdapter.getDefaultAdapter(this);

    Intent intent = new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

    pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);

}  


NfcAdapter.getDefaultAdapter(Context)는 디폴트 NfcAdapter 객체를 가져올 때 사용하는데, NFC를 지원하지 않는 단말에서는 null을 리턴합니다.

 

Intent 객체를 생성할 때 Intent.FLAG_ACTIVITY_SINGLE_TOP 플래그를 준 것은, 태그를 계속 인식할 때 새로운 Activity를 만들지 않고 현재 Activity에서 Intent를 받기 위해서입니다.

 

Activity가 화면에 보이고 있을 때에만 NFC 태그를 인식하기 위해서, onResume(..)에서 enableForegroundDispatch(..)를 호출하고, onPause(..)에서 disableForegroundDispatch(..)를 호출합니다. 


@Override

protected void onResume() {

    super.onResume();

    if (nfcAdapter != null) {

        nfcAdapter.enableForegroundDispatch(this, pendingIntent, null, null);

    }

} 


@Override

protected void onPause() {

    if (nfcAdapter != null) {

        nfcAdapter.disableForegroundDispatch(this);

    }

    super.onPause();

}


이제 태그가 인식되면 onNewIntent(..)로 Intent가 전달될 것입니다.

* 이와 같이 foregroundDispatch를 할 경우 onNewIntent()를 통해 Intent를 받게 되도록 되어 있고(자세한 것은 Intent와 Activity LifeCycle을 이해) foregroundDispatch를 하지 않으면 앱이 실행되면서 Intent를 받게 되므로 최초의 실행시에는 onCreate()부터 받을 수 있으며, getIntent()를 통해 호출하며 전달된 Intent를 가져올 수 있다. 이후 실행된 상태에서는 역시 onNewIntent()를 통해 Intent를 전달받게 된다.


onNewIntent(..)에서는 이 Intent가 태그 인식으로 인한 Intent인지를 판단하고, 태그 ID를 화면에 출력합니다.


@Override

protected void onNewIntent(Intent intent) {

    super.onNewIntent(intent);

    Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);

    if (tag != null) {

        byte[] tagId = tag.getId();

        tagDesc.setText("TagID: " + toHexString(tagId));

    }

} 


NFC 태그 인식으로 발생한 intent에는 NfcAdapter.EXTRA_TAG 이름으로 Tag 객체가 전달됩니다.

이것을 확인하면 NFC 태그 인식으로 인한 intent인지 확인할 수 있습니다.

 

Tag 의 getId() 메소드는 byte[] 형식의 태그 ID를 리턴합니다. toHexString(..) 메소드는 byte[]를

String으로 변환하는 역할을 하는데, 소스는 전체 소스코드에서 확인할 수 있습니다. 



NDEF


* NDEF 메시지와 Record



* NDEF Record Header 구조

헤더 항목 길이  설명
 MB (Message Begin) 1bit NDEF 메시지의 첫 레코드는 이 비트가 1 입니다.
 ME (Message End) 1bit NDEF 메시지의 마지막 레코드는 이 비트가 1입니다. 
 CF (Chunk Flag) 1bit 하나의 페이로드를 여러 개의 레코드로 나누어 전송하는 경우가 있는데, 이때 사용합니다.
 SR (Short Record) 1bit 이 비트가 1이면 '페이로드 길이'의 크기는 1byte이고, 0이면 4byte입니다.
 IL (ID Length) 1bit 레코드 ID가 존재하는 경우 이 비트가 1입니다.
 TNF (Type Name Format) 3bit (별도로 설명)


* NDEF Record 구조


* NDEF TNF Table


.....