NFC

2 NFC auf Android

2 NFC auf Android

NFC

2 NFC auf Android


Dieses Kapitel geht konkret auf die Nutzung von NFC unter Android ein. Neben der grundlegenden Konfiguration einer Android-App für NFC werden Sie erfahren, wie man Tags liest und schreibt. Abgeschlossen wir das Kapitel mit der Vorstellung von Android Beam.

2.1 Entwicklung seit Android 2.3

Android 2.3

Seit Android 2.3 (API Level 9) sind grundlegende Tag-Reading-Funktionen im Android-Betriebssystem im android.nfc-Package enthalten. Allerdings beschränkten sich diese Funktionen tatsächlich darauf, allgemein nicht weiter spezifizierte NFC Tags zu erkennen und per Intent an eine Android Activity weiterzugeben. Das war leider nicht sonderlich praktisch, da jede Android-App denselben Intent-Filter nutzen musste und somit in den meisten Fällen der Intent-Chooser-Dialog zur Auswahl einer App angezeigt wurde. Da NFC davon lebt, eben die Daten direkt mit der passenden Applikation verknüpfen zu können, bestand hier also reichlich Nachholbedarf.

Android 2.3.3

Mit Android 2.3.3 wurde es richtig spannend, denn nun wurden zahlreiche neue Features im Bereich NFC eingeführt. So wurde umfassender Support für das Tag Reading und Writing eingeführt sowie NDEF Push, wodurch ein Teil der NFC-Peer-to-Peer-Funktionalität abgedeckt wird. Die größte Neuerung und dramatische Verbesserung bestand jedoch darin, dass Tags nun anhand deutlich besserer Intent-Filter unterschieden werden konnten. Somit war nun der Weg frei, um Tags, beispielsweise URLs, anhand eines Hostnamens und Schemas direkt und relativ sicher an eine einzige App zu binden. Ein NFC Tag konnte nun zielgerichtet eine spezielle Android Activity öffnen. Der zeitraubende Intent-Chooser-Dialog war somit Vergangenheit.

Android 4.0

Während das Erstellen der NDEF Messages, welche auf den Tags oftmals abgelegt warden unter Android 2.3.3 noch recht komplex durch manuell zu erstellende byte-Arrays vonstatten ging wurden ab Android 4 einige Convenience-Methoden zur Erzeugung dieser Datenformate eingfuehrt. Ebenso wurden speziell fuer Android Apps sogenannte Android Package Records aufgenommen, welche die unmissverstaendliche Verknuepfung eines NFC Tags und der darin enthaltenen NDEF Message mit einer Android App ermoeglichen. Die P2P Funktionalität wurde unter Android 4.0 in Android Beam umbenannt und leicht erweitert. So kann man seit Android 4.0 die zu übertragenden Nachrichten nicht nur einmal beim Erzeugen der Aktivität festlegen, sonder per Callback dann definieren, wenn tatsächlich zwei Geräte per Android Beam kommunizieren wollen.

Android 4.1

Bezüglich NFC wurde Android mit der Version 4.1 dahingehend erweitert, dass nun grössere Dateien einfach per Android Beam miteinander geteilt warden können. Dabei wird NFC zum Herstellen einer Bluetooth oder WifiDirekt Verbindung verwendet, der eigentliche Datenaustausch geschieht dann über diese schnelleren Übertragungsprotokolle.

2.2 Grundlegende Konfiguration – AndroidManifest.xml & Co

Zunächst sollten Sie die NFC Permission im AndroidManifest.xml deklarieren, um aus Ihrer App auf die NFC-Features zugreifen zu können:

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

Die minimale SDK-Version sollte 9 sein, wodurch Sie jedoch nur Tag lesen können. Es wird empfohlen, mindestens mit der SDK-Version 10 zu arbeiten, die Android 2.3.3 entspricht. Einige der Convenience-Methoden zum Erzeugen von NDEF Messages benötigen Android 4, wobei Sie hier auf Grund einiger Bugs bezüglich des Schreibens von NFC Tags besser auf API Level 15 setzen sollten. API Level 14 entspricht Android 4.0 und sollte ausreichend sein, wenn Ihre App nur Tags lessen muss. Leider hat sich in Android 4.0.0 bis 4.0.2 ein lästiger Bug eingeschlichen, der das Formatieren und damit eine der Vorstufen zum Beschreiben von NDEF-kompatiblen Tags unmöglich machte.

<uses-sdk android:minSdkVersion="15" />

Um dem Android Market oder anderen Stores wie dem Amazon App Store zu signalisieren, dass Ihre App NFC-Features benutzt und passende Geräte NFC-Hardware haben muss, sollten Sie dies über <uses-feature /> ausdrücken.

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

Wenn Ihre App NFC nur als Zusatzfeature benötigt, dann können Sie den <uses-feature> Tag auch weglassen. Damit wird Ihre App nicht ausgeschlossen, wenn ein Gerät keine NFC Hardware hat, ansonsten aber alle Bedingungen (wie API Level) unterstützt.

Sie müssen dann innerhalb Ihrer Applikation per
NfcAdapter.getDefaultAdapter(context) überprüfen, ob NFC hardware verfügbar ist. getDefaultAdapter(context) liefert null, wenn keine passende Hardware vorhanden ist.

Nachdem Sie diese drei Zeilen ins AndroidManifest.xml eingetragen haben kann es losgehen!

2.3 Tags lesen

2.3.1 NFCAdapter und NFCManager

Die zentralen Klassen im Umgang mit NFC auf Android sind NFCAdapter und NFCManager. Die einzige Aufgabe des NFCManager ist (zumindest derzeit), per Methode getDefaultAdapter den Zugriff zu einem NFCAdapter zu ermöglichen. Da dies jedoch genauso direkt mit der statischen Methode getDefaultAdapter des NFCAdapter direkt zu erreichen ist, konzentrieren wir uns gleich auf diesen:

NfcAdapter nfc = NfcAdapter.getDefaultAdapter(getApplicationContext());

Wie Sie sehen, benötigt der Aufruf eine Instanz von Context, die beispielsweise in Aktivitäten durch den Aufruf von getApplicationContext erlangt werden kann.

Die Methode isEnabled des NFCAdapters gibt Aufschluss darüber, ob die NFC-Hardware aktiviert ist. Es bietet sich an, den Anwender auf die Settings-Aktivität zu verweisen, um dieses Feature zu aktivieren:

if (!nfc.isEnabled())
{
//maybe some quick info message, toast or so?
startActivity(new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS));
}

2.3.2 Foreground Dispatch Mode: Alle Tags führen zu meiner App

Da NFC auf Betriebssystemebene implementiert ist, sorgt ein mehrstufiger Dispatch-Prozess für die Zustellung von gelesenen Tags an die passenden Android-Applikationen (per Intent-Filter von Activities, später mehr dazu). Um alle vom Betriebssystem erkannten Tags jedoch ausnahmsweise an eine geöffnete Activity weiterzuleiten, kann der Foreground Dispatch Mode benutzt werden. Er ist sehr praktisch, um Erfahrung mit unterschiedlichen Tag-Typen zu sammeln. Später, wenn wir das Beschreiben von NFC Tags behandeln, wird der Foreground Dispatch Mode auch eine wichtige Rolle spielen.

Sobald die NFC-Hardware des Geräts einen NFC-Chip ausgelesen hat, werden je nach Chip und verwendeter Chiptechnologie Intents ausgesendet. Um diese Intents der passenden Applikation zuzuweisen, gibt es ein dreistufiges Filtersystem. Sobald eine Aktivität im Vordergrund ist, kann sie über den Android-Foreground-Dispatch-Mechanismus sämtliche Intents, die durch NFC ausgelöst wurden, für sich beanspruchen.

Typischerweise ruft eine Aktivität die Methode enableForegroundDispatch in der onResume-Methode auf. In der onPause-Methode muss dann die Methode disableForegroundDispatch aufgerufen werden, da es sonst zu Fehlern kommt.

Um den Foreground Dispatch zu aktivieren, müssen vier Parameter an die enableForegroundDispatch-Methode übergeben werden. Dabei handelt es sich verständlicherweise um eine Referenz zur aktuellen Aktivität, einen PendingIntent (der abgeschickt wird, sobald ein Tag gefunden wurde) sowie ein Array von IntentFilter und ein Array von so genannten NFC Techs. Um sämtliche Tags abzufangen, kann man die letzten beiden Parameter weglassen, also null übergeben (wir gehen auf die Tag Intent Filter sowie Tag-Technologien noch im Detail ein):

//Aufruf in onResume
Intent i = new Intent(activity, activity.getClass());
i.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent intent = PendingIntent.getActivity(activity, 0, i, 0);
nfc.enableForegroundDispatch(activity, intent, null, null);

//Aufruf in onPause
nfc.disableForegroundDispatch(activity);

An dieser Stelle ist ein kleines Detail sehr wichtig: Der Intent, der zur Erzeugung des PendingIntent verwendet wird, bekommt das Flag SINGLE_TOP gesetzt. Dadurch wird erreicht, dass die bereits vorhandene Aktivität im Speicher nicht erneut gestartet wird. Vielmehr wird nun die Methode onNewIntent der Aktivität aufgerufen. Überschreiben Sie also die onNewIntent-Methode wie folgt, um auf den gefundenen Tag (in der gleichen Aktivität) zuzugreifen:

public void onNewIntent(Intent intent) {
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
//mach' was mit dem Tag!
}

2.3.3 Tag- und TagTechnology-Klassen

Über die Extras des Intents können Sie nun den Tag abrufen. Um mit dem Tag zu arbeiten, wird er in eine Instanz einer der TagTechnology-Klassen umgewandelt. Da es diverse Klassen gibt, sollten Sie zunächst überprüfen, welche Technology dieser Tag unterstützt. Mit der Methode getTechList greifen Sie auf einen Array von Klassennamen der TagTechnologies zu. Sobald Sie sich sicher sind, dass der gefundene Tag beispielsweise die TagTechnology android.nfc.tech.Ndef unterstützt, können Sie den Tag in eine solche Instanz verwandeln. Dadurch können Sie nun auf die speziellen Features dieser TagTechnology zugreifen:

String techs[] = tag.getTechList();
boolean containsNdef = false;
boolean containsNdefFormatable = false;

for (String tech : techs) {
if (tech.equals("android.nfc.tech.Ndef"))
containsNdef = true;

if (tech.equals("android.nfc.tech.NdefFormatable"))
containsNdefFormatable = true;
}

if (containsNdef) {
Ndef ndef = Ndef.get(tag);
Log.d("demo", "NdeF Tag Tech discovered");
Log.d("demo", "NFC Forum Tag Type: " + ndef.getType());
Log.d("demo", "Max size in bytes: " + ndef.getMaxSize());
Log.d("demo", "Can tag be made read only? " + ndef.canMakeReadOnly());
Log.d("demo", "Is writable?: " + ndef.isWritable());

NFCUtil.printNdefMessageDetails(ndef.getCachedNdefMessage()));

}

if (containsNdefFormatable) {
NdefFormatable ndef = NdefFormatable.get(tag);
Log.d("demo", "NdefFormatable Tag Tech discovered\n");
}

Es gibt derzeit neun TagTechnology-Klassen: NfcA, NfcB, NfcF, NfcV, IsoDep, MifareClassic, MifareUltralight, Ndef und NdefFormatable. Je nach Tag-Technologie werden unterschiedliche Methoden und dadurch Features jeweiligen Tags angeboten.

Bild7074.PNG

Abbildung 2.1: TagTechnology-Klassen

TagTechnology-Klassen: Wir beschränken uns in diesem Buch auf die Ndef- sowie NdefFormatable-Klassen. NDEF (NFC Forum Data Exchange Format) stellt einen vom NFC Forum ausgerufenen Standard dar. Je nach Anwendung kann es auch sinnvoll sein, mit speziellen TagTechnology-Klassen wie NfcF (JIS 6319-4) oder anderen zu entwickeln, jedoch setzt das detaillierte Wissen zu dem jeweiligen Standard voraus. Spezialfunktionen wie Kryptografie sind allerdings nur mittels dieser TagTechnology-Klassen möglich.

Kommen wir kurz auf das Foreground-Dispatch-Feature zurück. Wir hatten im Beispiel null für die Liste der Tag-Technologien angegeben. Dadurch haben wir sichergestellt, dass alle verfügbaren Tags, egal mit welcher Tag-Technologie der gefundene Tag ausgestattet ist, an unsere Aktivität übergeben wurde. Um diesen Filter auf Basis der Tag-Technologien einzuschränken, hätten wir wie folgt einen multidimensionalen String Array erstellen können:

private String techListsArray[][] = new String[][] { new String[] { NfcA.class.getName() } };
nfc.enableForegroundDispatch(activity, intent, null, techListsArray);

In diesem Fall würde der PendingIntent nur dann ausgelöst, wenn der gefundene Tag auch die Technologie NfcA unterstützt. Es wurde wohl gemerkt ein multidimensionaler String Array erstellt, da Sie dadurch unterschiedliche Tag-Technologie-Gruppen definieren können, die gematcht werden sollen. Folgendes Beispiel erstellt einen multidimensionalen String Array, der nur Tags mit folgenden Kriterien matcht:

Der Tag unterstützt NfcA UND NfcB oder

Der Tag unterstützt NfcV

techListsArray = new String[][] { new String[] { NfcA.class.getName(), NfcB.class.getName() }, new String[] {NfcV.class.getName()} };

Wir beschränken uns im Weiteren auf die Tag-Technology-Klassen ...