mercredi 26 septembre 2012

Getting started with NFC on Android

This posts introduces Near Field Communication (NFC), its Modes, the Android APIs that can be used to deal with NFC tags and the mysterious NDEF Format (NDEFMessages, NDEFRecord, RTC, TNF, etc.).

Not all Android-based smartphones support NFC, actually the list is limited to Nexus S, Galaxy S II, Xperia Acro, Lg LU6200, Acer Liquid express, Sony Nozomi, Nexus Prime, LG Gelato nfc, etc.

The NFC API was introduced with Android 2.3 (API 9, Dec. 2010) on Nexus S. A more complete API was released win Android 2.3.3 (API 10, Feb. 2011). This API provides tools for detecting TAGs, reading infromation stored on the tag and writing information, and also communicating with other NFC tags. These operations are also called modes:
  • Peer-to-Peer Mode: limited support on Android
  • Reader/Writer Mode: excellent support
  • Card Emulation Mode: no support
The Core classes of the NFC API are NFCAdapter, NFCManager, Tag Technology Classes.
Here is a typical manafest file for an application that want to use the Android NFC API:
Android Manifest
//NFC Permission 
<uses-permission android:name="android.permission.NFC" />
//API Level 
<uses-sdk android:minSdkVersion="10" />
//NFC Feature 
<uses-feature android:name="android.hardware.nfc" android:required="true" />

<application android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" >
<activity android:name=".NfcRWActivity" android:label="NfcRWActivity">
  <intent-filter>
    <action android:name="android.nfc.action.NDEF_DISCOVERED" />
    <data android:mimeType="application/json" />
    <category android:name="android.intent.category.DEFAULT" />
  </intent-filter>
</activity>
</application>
NFCManager used to get the NFCAdapter the real thing that control Foreground Dispatch and P2P NDEF Push, check if NFC is turned on
NfcManager nfc_manager = (NfcManager) getSystemService(NFC_SERVICE);
NFCAdapter nfc_adapter = nfc_manager.getDefaultAdapter();

To read a tag it is easy: unlock phone, touch tag, most likely the intent chooser will open.

Foreground Dispatch System

Foreground Dispatch allows the declaration of the currently active application as prioritar for handling the tags read by user. Thus the Foreground Activity may redirect all intents related to tag, technology or NDEF discovery to itself.
nfcAdapter.enableForegroundDispatch(activity, pendingIntent, intentFiltersArray, techListsArray);

pi = PendingIntent.getActivity(activity, 0, new Intent(activity, activity.getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);

IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
ndef.addDataType("*/*");
intentFiltersArray = new IntentFilter[] { ndef };
techListsArray = new String[][] {new String[] { NfcA.class.getName() } };
Don't forget to enable in onResume(), disable in onPause(). The tag can be read in onNewIntent().
@Override
protected void onResume() {
  super.onResume();
  nfcAdapter.enableForegroundDispatch(this, pi, intentFiltersArray, techListsArray);
}

@Override
protected void onPause() {
  super.onPause();
  nfcAdapter.disableForegroundDispatch(this);
}

@Override 
protected void onNewIntent(Intent intent) {
  super.onNewIntent(intent);
  Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
  tag.getId() //returns id as byte[]
  String techs[] = tag.getTechList();
  for (String tech: techs) {
   if (tech.equals("android.nfc.tech.Ndef")) {
    Ndef ndef = Ndef.get(tag);
    ndef.getType();
    ndef.getMaxSize();
    ndef.canMakeReadOnly();
    ndef.isWritable();
    NFCUtil.printNdefMessageDetails(ndef.getCachedNdefMessage());
   }
  }
}

Intent Dispatch System

Ndef discovered
For text or any other mime type:
<intent-filter>
  <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
  <data android:mimeType="text/plain"/>
  <category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
For url:
<intent-filter>
  <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
  <data android:scheme="http" android:host="www.r0ly.fr"/>
  <category android:name="android.intent.category.DEFAULT"/>
</intent-filter>

Tech discovered
In the manifest
<intent-filter>
  <action android:name="android.nfc.action.TECH_DISCOVERED"/>
</intent-filter>

<meta-data android:name="android.nfc.action.TECH_DISCOVERED" android:resource="@xml/techlist"/>
In the xml/techlist.xml file:
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
  <tech-list>
    <tech>android.nfc.tech.IsoDep</tech>
    <tech>android.nfc.tech.MifareClassic</tech>
  </tech-list>
</resources>

Tag Technologies maps to tech specifications or to pseudo-technologie or capabilities like NDEF or NDEFFormatable.
  • TagTechnology The interface that all tag technology classes must implement.
  • NfcA  Provides access to NFC-A (ISO 14443-3A) properties and I/O operations.
  • NfcB Provides access to NFC-B (ISO 14443-3B) properties and I/O operations.
  • NfcF Provides access to NFC-F (JIS 6319-4) properties and I/O operations.
  • NfcV Provides access to NFC-V (ISO 15693) properties and I/O operations.
  • IsoDep Provides access to ISO-DEP (ISO 14443-4) properties and I/O operations.
  • Ndef Provides access to NDEF data and operations on NFC tags that have been formatted as NDEF.
  • NdefFormatable Provides a format operations for tags that may be NDEF formattable.
  • MifareClassic Provides access to MIFARE Classic properties and I/O operations, if this Android device supports MIFARE.
  • MifareUltralight Provides access to MIFARE Ultralight properties and I/O operations, if this Android device supports MIFARE.
NDEF stands for NFC Data Exchange FormatRecords can be MIME-type media, URIs or RTDs (Record Type Definitions)
NdefRecord
Reading Tag Content
//r = NdefRecord
if (r.getTnf() == NdefRecord.TNF_ABSOLUTE_URI) {
  b.append(String.format("TNF_ABSOLUTE_URI: type(%1$s), id(%2$s), payload(%3$s)\n", new String(r.getType()), idBytes.toString(), new String(r.getPaylod())));

} else if (r.getTnf() == NdefRecord.TNF_MIME_MEDIA) {
  b.append(String.format("TNF_MIME_MEDIA: type(%1$s), id(%2$s), payload(%3$s)\n", new String(r.getType()), idBytes.toString(),new String(r.getPayload())));

}

What to listen for?

The following figure depicts how the precedent intent related to tag reading are dispatch, so if you want your application to:
  • receive a notification about a tag been read while your app is in foreground, then you should use for enableForegroundDispatch
  • receive a notification even if it is not the foreground app, then register for NDEF_DISCOVERED.
  • receive a notification when the tag is of a specific technology TECH_DISCOVERED.
  • otherwise you should register for TAG_DISCOVERED.
Tag dispatch
Direct Start
1. Write custom NDEF MIME-type media messages
2. Use custom IntentFilter to blind to your messages

Writing NDEF MIME
NdefMessage msg = NFCUtil.getNdefMimeMessage("application/json", "{\"key\":\"value\"}");
Intent i = new Intent(this, WriteActivity.class);
i.putExtra(WriteActivity.NDEF_MESSAGE, msg);
startActivity(i);
private static NdefRecord getMimeRecord(String mimeType, String content) {
  NdefRecord record = new NdefRecord(NdefRecord.TNF_MIME_MEDIA, mimeType.getBytes(), getRandomIdBytes(), content.getBytes());
  return record;
}
Ndef ndef = Ndef.get(tag);
if (ndef.isWritable() && ndef.getMaxSize() > this.msg.toByteArray().length) {
  ndef.connect();
  ndef.writeNdefMessage(this.msg);
  ndef.close();
} else {
  // do something
}
P2P: NDEF Push
Create NDEF Message which is pushed to another active device once the other device is close
NfcAdapter.enableForegroundNdefPush(activity, ndefmessage);
NfcAdapter.disableForegroundNdefPush(activity);

Aucun commentaire:

Enregistrer un commentaire