summaryrefslogtreecommitdiffstats
path: root/core/java/android/nfc/tech
diff options
context:
space:
mode:
authorJeff Hamilton <jham@android.com>2011-01-21 01:13:06 -0600
committerJeff Hamilton <jham@android.com>2011-01-21 13:32:58 -0600
commit4e21e1d21a877cce4db5ec8c5786604cc10f2d7e (patch)
tree25cadcf38afab0b0874f35a029e8ee5604a153bc /core/java/android/nfc/tech
parent11576102a9794ef964a08bc372dc09803f58997a (diff)
downloadframeworks_base-4e21e1d21a877cce4db5ec8c5786604cc10f2d7e.zip
frameworks_base-4e21e1d21a877cce4db5ec8c5786604cc10f2d7e.tar.gz
frameworks_base-4e21e1d21a877cce4db5ec8c5786604cc10f2d7e.tar.bz2
API changes for NFC.
Bug: 3366009 Change-Id: Ia28e03e1501421906b640b78d4c9a59a20e9c668
Diffstat (limited to 'core/java/android/nfc/tech')
-rw-r--r--core/java/android/nfc/tech/BasicTagTechnology.java181
-rw-r--r--core/java/android/nfc/tech/IsoDep.java132
-rw-r--r--core/java/android/nfc/tech/MifareClassic.java417
-rw-r--r--core/java/android/nfc/tech/MifareUltralight.java130
-rw-r--r--core/java/android/nfc/tech/Ndef.java264
-rw-r--r--core/java/android/nfc/tech/NdefFormatable.java108
-rw-r--r--core/java/android/nfc/tech/NfcA.java99
-rw-r--r--core/java/android/nfc/tech/NfcB.java101
-rw-r--r--core/java/android/nfc/tech/NfcF.java95
-rw-r--r--core/java/android/nfc/tech/NfcV.java94
-rw-r--r--core/java/android/nfc/tech/TagTechnology.java145
11 files changed, 1766 insertions, 0 deletions
diff --git a/core/java/android/nfc/tech/BasicTagTechnology.java b/core/java/android/nfc/tech/BasicTagTechnology.java
new file mode 100644
index 0000000..a909631
--- /dev/null
+++ b/core/java/android/nfc/tech/BasicTagTechnology.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc.tech;
+
+import android.nfc.ErrorCodes;
+import android.nfc.Tag;
+import android.nfc.TagLostException;
+import android.nfc.TransceiveResult;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * A base class for tag technologies that are built on top of transceive().
+ */
+/* package */ abstract class BasicTagTechnology implements TagTechnology {
+ private static final String TAG = "NFC";
+
+ /*package*/ final Tag mTag;
+ /*package*/ boolean mIsConnected;
+ /*package*/ int mSelectedTechnology;
+
+ BasicTagTechnology(Tag tag, int tech) throws RemoteException {
+ int[] techList = tag.getTechnologyList();
+ int i;
+
+ // Check target validity
+ for (i = 0; i < techList.length; i++) {
+ if (tech == techList[i]) {
+ break;
+ }
+ }
+ if (i >= techList.length) {
+ // Technology not found
+ throw new IllegalArgumentException("Technology " + tech + " not present on tag " + tag);
+ }
+
+ mTag = tag;
+ mSelectedTechnology = tech;
+ }
+
+ BasicTagTechnology(Tag tag) throws RemoteException {
+ this(tag, tag.getTechnologyList()[0]);
+ }
+
+ @Override
+ public Tag getTag() {
+ return mTag;
+ }
+
+ /** Internal helper to throw IllegalStateException if the technology isn't connected */
+ void checkConnected() {
+ if ((mTag.getConnectedTechnology() != getTechnologyId()) ||
+ (mTag.getConnectedTechnology() == -1)) {
+ throw new IllegalStateException("Call connect() first!");
+ }
+ }
+
+ @Override
+ public int getTechnologyId() {
+ return mSelectedTechnology;
+ }
+
+ /**
+ * Helper to indicate if {@link #connect} has succeeded.
+ * <p>
+ * Does not cause RF activity, and does not block.
+ * @return true if {@link #connect} has completed successfully and the {@link Tag} is believed
+ * to be within range. Applications must still handle {@link java.io.IOException}
+ * while using methods that require a connection in case the connection is lost after this
+ * method returns.
+ */
+ public boolean isConnected() {
+ if (!mIsConnected) {
+ return false;
+ }
+
+ try {
+ return mTag.getTagService().isPresent(mTag.getServiceHandle());
+ } catch (RemoteException e) {
+ Log.e(TAG, "NFC service dead", e);
+ return false;
+ }
+ }
+
+ @Override
+ public void connect() throws IOException {
+ try {
+ int errorCode = mTag.getTagService().connect(mTag.getServiceHandle(), getTechnologyId());
+
+ if (errorCode == ErrorCodes.SUCCESS) {
+ // Store this in the tag object
+ mTag.setConnectedTechnology(getTechnologyId());
+ mIsConnected = true;
+ } else {
+ throw new IOException();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "NFC service dead", e);
+ throw new IOException("NFC service died");
+ }
+ }
+
+ @Override
+ public void reconnect() throws IOException {
+ if (!mIsConnected) {
+ throw new IllegalStateException("Technology not connected yet");
+ }
+
+ try {
+ int errorCode = mTag.getTagService().reconnect(mTag.getServiceHandle());
+
+ if (errorCode != ErrorCodes.SUCCESS) {
+ mIsConnected = false;
+ mTag.setTechnologyDisconnected();
+ throw new IOException();
+ }
+ } catch (RemoteException e) {
+ mIsConnected = false;
+ mTag.setTechnologyDisconnected();
+ Log.e(TAG, "NFC service dead", e);
+ throw new IOException("NFC service died");
+ }
+ }
+
+ @Override
+ public void close() {
+ try {
+ /* Note that we don't want to physically disconnect the tag,
+ * but just reconnect to it to reset its state
+ */
+ mTag.getTagService().reconnect(mTag.getServiceHandle());
+ } catch (RemoteException e) {
+ Log.e(TAG, "NFC service dead", e);
+ } finally {
+ mIsConnected = false;
+ mTag.setTechnologyDisconnected();
+ }
+ }
+
+ /** Internal transceive */
+ /*package*/ byte[] transceive(byte[] data, boolean raw) throws IOException {
+ checkConnected();
+
+ try {
+ TransceiveResult result = mTag.getTagService().transceive(mTag.getServiceHandle(), data, raw);
+ if (result == null) {
+ throw new IOException("transceive failed");
+ } else {
+ if (result.isSuccessful()) {
+ return result.getResponseData();
+ } else {
+ if (result.isTagLost()) {
+ throw new TagLostException("Tag was lost.");
+ }
+ else {
+ throw new IOException("transceive failed");
+ }
+ }
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "NFC service dead", e);
+ throw new IOException("NFC service died");
+ }
+ }
+}
diff --git a/core/java/android/nfc/tech/IsoDep.java b/core/java/android/nfc/tech/IsoDep.java
new file mode 100644
index 0000000..f6d141a
--- /dev/null
+++ b/core/java/android/nfc/tech/IsoDep.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc.tech;
+
+import android.nfc.Tag;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * A low-level connection to a {@link Tag} using the ISO-DEP technology, also known as
+ * ISO1443-4.
+ *
+ * <p>You can acquire this kind of connection with {@link #get}.
+ * Use this class to send and receive data with {@link #transceive transceive()}.
+ *
+ * <p>Applications must implement their own protocol stack on top of
+ * {@link #transceive transceive()}.
+ *
+ * <p class="note"><strong>Note:</strong>
+ * Use of this class requires the {@link android.Manifest.permission#NFC}
+ * permission.
+ */
+public final class IsoDep extends BasicTagTechnology {
+ private static final String TAG = "NFC";
+
+ /** @hide */
+ public static final String EXTRA_HI_LAYER_RESP = "hiresp";
+ /** @hide */
+ public static final String EXTRA_HIST_BYTES = "histbytes";
+
+ private byte[] mHiLayerResponse = null;
+ private byte[] mHistBytes = null;
+
+ /**
+ * Returns an instance of this tech for the given tag. If the tag doesn't support
+ * this tech type null is returned.
+ *
+ * @param tag The tag to get the tech from
+ */
+ public static IsoDep get(Tag tag) {
+ if (!tag.hasTech(TagTechnology.ISO_DEP)) return null;
+ try {
+ return new IsoDep(tag);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /** @hide */
+ public IsoDep(Tag tag)
+ throws RemoteException {
+ super(tag, TagTechnology.ISO_DEP);
+ Bundle extras = tag.getTechExtras(TagTechnology.ISO_DEP);
+ if (extras != null) {
+ mHiLayerResponse = extras.getByteArray(EXTRA_HI_LAYER_RESP);
+ mHistBytes = extras.getByteArray(EXTRA_HIST_BYTES);
+ }
+ }
+
+ /**
+ * Sets the timeout of an IsoDep transceive transaction in milliseconds.
+ * If the transaction has not completed before the timeout,
+ * any ongoing {@link #transceive} operation will be
+ * aborted and the connection to the tag is lost. This setting is applied
+ * only to the {@link Tag} object linked to this technology and will be
+ * reset when {@link IsoDep#close} is called.
+ * The default transaction timeout is 300 milliseconds.
+ */
+ public void setTimeout(int timeout) {
+ try {
+ mTag.getTagService().setIsoDepTimeout(timeout);
+ } catch (RemoteException e) {
+ Log.e(TAG, "NFC service dead", e);
+ }
+ }
+
+ @Override
+ public void close() {
+ try {
+ mTag.getTagService().resetIsoDepTimeout();
+ } catch (RemoteException e) {
+ Log.e(TAG, "NFC service dead", e);
+ }
+ super.close();
+ }
+
+ /**
+ * Return the historical bytes if the tag is using {@link NfcA}, null otherwise.
+ */
+ public byte[] getHistoricalBytes() {
+ return mHistBytes;
+ }
+
+ /**
+ * Return the hi layer response bytes if the tag is using {@link NfcB}, null otherwise.
+ */
+ public byte[] getHiLayerResponse() {
+ return mHiLayerResponse;
+ }
+
+ /**
+ * Send data to a tag and receive the response.
+ * <p>
+ * This method will block until the response is received. It can be canceled
+ * with {@link #close}.
+ * <p>Requires {@link android.Manifest.permission#NFC} permission.
+ *
+ * @param data bytes to send
+ * @return bytes received in response
+ * @throws IOException if the target is lost or connection closed
+ */
+ public byte[] transceive(byte[] data) throws IOException {
+ return transceive(data, true);
+ }
+}
diff --git a/core/java/android/nfc/tech/MifareClassic.java b/core/java/android/nfc/tech/MifareClassic.java
new file mode 100644
index 0000000..1c52322
--- /dev/null
+++ b/core/java/android/nfc/tech/MifareClassic.java
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc.tech;
+
+import android.nfc.Tag;
+import android.nfc.TagLostException;
+import android.os.RemoteException;
+
+import java.io.IOException;
+
+/**
+ * Technology class representing MIFARE Classic tags (also known as MIFARE Standard).
+ *
+ * <p>Support for this technology type is optional. If the NFC stack doesn't support this technology
+ * MIFARE Classic tags will still be scanned, but will only show the NfcA technology.
+ *
+ * <p>MIFARE Classic tags have sectors that each contain blocks. The block size is constant at
+ * 16 bytes, but the number of sectors and the sector size varies by product. MIFARE has encryption
+ * built in and each sector has two keys associated with it, as well as ACLs to determine what
+ * level acess each key grants. Before operating on a sector you must call either
+ * {@link #authenticateSector(int, byte[], boolean)} or
+ * {@link #authenticateBlock(int, byte[], boolean)} to gain authorize your request.
+ */
+public final class MifareClassic extends BasicTagTechnology {
+ /**
+ * The well-known default MIFARE read key. All keys are set to this at the factory.
+ * Using this key will effectively make the payload in the sector public.
+ */
+ public static final byte[] KEY_DEFAULT =
+ {(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF};
+ /**
+ * The well-known, default MIFARE Application Directory read key.
+ */
+ public static final byte[] KEY_MIFARE_APPLICATION_DIRECTORY =
+ {(byte)0xA0,(byte)0xA1,(byte)0xA2,(byte)0xA3,(byte)0xA4,(byte)0xA5};
+ /**
+ * The well-known, default read key for NDEF data on a MIFARE Classic
+ */
+ public static final byte[] KEY_NFC_FORUM =
+ {(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7};
+
+ /** A MIFARE Classic tag */
+ public static final int TYPE_CLASSIC = 0;
+ /** A MIFARE Plus tag */
+ public static final int TYPE_PLUS = 1;
+ /** A MIFARE Pro tag */
+ public static final int TYPE_PRO = 2;
+ /** The tag type is unknown */
+ public static final int TYPE_UNKNOWN = 5;
+
+ /** The tag contains 16 sectors, each holding 4 blocks. */
+ public static final int SIZE_1K = 1024;
+ /** The tag contains 32 sectors, each holding 4 blocks. */
+ public static final int SIZE_2K = 2048;
+ /**
+ * The tag contains 40 sectors. The first 32 sectors contain 4 blocks and the last 8 sectors
+ * contain 16 blocks.
+ */
+ public static final int SIZE_4K = 4096;
+ /** The tag contains 5 sectors, each holding 4 blocks. */
+ public static final int SIZE_MINI = 320;
+ /** The capacity is unknown */
+ public static final int SIZE_UNKNOWN = 0;
+
+ private boolean mIsEmulated;
+ private int mType;
+ private int mSize;
+
+ /**
+ * Returns an instance of this tech for the given tag. If the tag doesn't support
+ * this tech type null is returned.
+ *
+ * @param tag The tag to get the tech from
+ */
+ public static MifareClassic get(Tag tag) {
+ if (!tag.hasTech(TagTechnology.MIFARE_CLASSIC)) return null;
+ try {
+ return new MifareClassic(tag);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /** @hide */
+ public MifareClassic(Tag tag) throws RemoteException {
+ super(tag, TagTechnology.MIFARE_CLASSIC);
+
+ // Check if this could actually be a MIFARE Classic
+ NfcA a = NfcA.get(tag);
+
+ mIsEmulated = false;
+ mType = TYPE_UNKNOWN;
+ mSize = SIZE_UNKNOWN;
+
+ switch (a.getSak()) {
+ case 0x08:
+ // Type == classic
+ // Size = 1K
+ mType = TYPE_CLASSIC;
+ mSize = SIZE_1K;
+ break;
+ case 0x09:
+ // Type == classic mini
+ // Size == ?
+ mType = TYPE_CLASSIC;
+ mSize = SIZE_MINI;
+ break;
+ case 0x10:
+ // Type == MF+
+ // Size == 2K
+ // SecLevel = SL2
+ mType = TYPE_PLUS;
+ mSize = SIZE_2K;
+ break;
+ case 0x11:
+ // Type == MF+
+ // Size == 4K
+ // Seclevel = SL2
+ mType = TYPE_PLUS;
+ mSize = SIZE_4K;
+ break;
+ case 0x18:
+ // Type == classic
+ // Size == 4k
+ mType = TYPE_CLASSIC;
+ mSize = SIZE_4K;
+ break;
+ case 0x20:
+ // TODO this really should be a short, not byte
+ if (a.getAtqa()[0] == 0x03) {
+ // Type == DESFIRE
+ break;
+ } else {
+ // Type == MF+
+ // SL = SL3
+ mType = TYPE_PLUS;
+ mSize = SIZE_UNKNOWN;
+ }
+ break;
+ case 0x28:
+ // Type == MF Classic
+ // Size == 1K
+ // Emulated == true
+ mType = TYPE_CLASSIC;
+ mSize = SIZE_1K;
+ mIsEmulated = true;
+ break;
+ case 0x38:
+ // Type == MF Classic
+ // Size == 4K
+ // Emulated == true
+ mType = TYPE_CLASSIC;
+ mSize = SIZE_4K;
+ mIsEmulated = true;
+ break;
+ case 0x88:
+ // Type == MF Classic
+ // Size == 1K
+ // NXP-tag: false
+ mType = TYPE_CLASSIC;
+ mSize = SIZE_1K;
+ break;
+ case 0x98:
+ case 0xB8:
+ // Type == MF Pro
+ // Size == 4K
+ mType = TYPE_PRO;
+ mSize = SIZE_4K;
+ break;
+ }
+ }
+
+ /** Returns the size of the tag, determined at discovery time */
+ public int getSize() {
+ return mSize;
+ }
+
+ /** Returns the size of the tag, determined at discovery time */
+ public int getType() {
+ return mType;
+ }
+
+ /** Returns true if the tag is emulated, determined at discovery time */
+ public boolean isEmulated() {
+ return mIsEmulated;
+ }
+
+ /** Returns the number of sectors on this tag, determined at discovery time */
+ public int getSectorCount() {
+ switch (mSize) {
+ case SIZE_1K: {
+ return 16;
+ }
+ case SIZE_2K: {
+ return 32;
+ }
+ case SIZE_4K: {
+ return 40;
+ }
+ case SIZE_MINI: {
+ return 5;
+ }
+ default: {
+ return 0;
+ }
+ }
+ }
+
+ /** Returns the sector size, determined at discovery time */
+ public int getSectorSize(int sector) {
+ return getBlockCount(sector) * 16;
+ }
+
+ /** Returns the total block count, determined at discovery time */
+ public int getTotalBlockCount() {
+ int totalBlocks = 0;
+ for (int sec = 0; sec < getSectorCount(); sec++) {
+ totalBlocks += getSectorSize(sec);
+ }
+
+ return totalBlocks;
+ }
+
+ /** Returns the block count for the given sector, determined at discovery time */
+ public int getBlockCount(int sector) {
+ if (sector >= getSectorCount()) {
+ throw new IllegalArgumentException("this card only has " + getSectorCount() +
+ " sectors");
+ }
+
+ if (sector <= 32) {
+ return 4;
+ } else {
+ return 16;
+ }
+ }
+
+ private byte firstBlockInSector(int sector) {
+ if (sector < 32) {
+ return (byte) ((sector * 4) & 0xff);
+ } else {
+ return (byte) ((32 * 4 + ((sector - 32) * 16)) & 0xff);
+ }
+ }
+
+ // Methods that require connect()
+ /**
+ * Authenticate the entire sector that the given block resides in.
+ * <p>This requires a that the tag be connected.
+ */
+ public boolean authenticateBlock(int block, byte[] key, boolean keyA) throws IOException {
+ checkConnected();
+
+ byte[] cmd = new byte[12];
+
+ // First byte is the command
+ if (keyA) {
+ cmd[0] = 0x60; // phHal_eMifareAuthentA
+ } else {
+ cmd[0] = 0x61; // phHal_eMifareAuthentB
+ }
+
+ // Second byte is block address
+ cmd[1] = (byte) block;
+
+ // Next 4 bytes are last 4 bytes of UID
+ byte[] uid = getTag().getId();
+ System.arraycopy(uid, uid.length - 4, cmd, 2, 4);
+
+ // Next 6 bytes are key
+ System.arraycopy(key, 0, cmd, 6, 6);
+
+ try {
+ if ((transceive(cmd, false) != null)) {
+ return true;
+ }
+ } catch (TagLostException e) {
+ throw e;
+ } catch (IOException e) {
+ // No need to deal with, will return false anyway
+ }
+ return false;
+ }
+
+ /**
+ * Authenticate for a given sector.
+ * <p>This requires a that the tag be connected.
+ */
+ public boolean authenticateSector(int sector, byte[] key, boolean keyA) throws IOException {
+ checkConnected();
+
+ byte addr = (byte) ((firstBlockInSector(sector)) & 0xff);
+
+ // Note that authenticating a block of a sector, will authenticate
+ // the entire sector.
+ return authenticateBlock(addr, key, keyA);
+ }
+
+ /**
+ * Sector indexing starts at 0.
+ * Block indexing starts at 0, and resets in each sector.
+ * <p>This requires a that the tag be connected.
+ * @throws IOException
+ */
+ public byte[] readBlock(int sector, int block) throws IOException {
+ checkConnected();
+
+ byte addr = (byte) ((firstBlockInSector(sector) + block) & 0xff);
+ return readBlock(addr);
+ }
+
+ /**
+ * Reads absolute block index.
+ * <p>This requires a that the tag be connected.
+ * @throws IOException
+ */
+ public byte[] readBlock(int block) throws IOException {
+ checkConnected();
+
+ byte addr = (byte) block;
+ byte[] blockread_cmd = { 0x30, addr };
+
+ return transceive(blockread_cmd, false);
+ }
+
+ /**
+ * Writes absolute block index.
+ * <p>This requires a that the tag be connected.
+ * @throws IOException
+ */
+ public void writeBlock(int block, byte[] data) throws IOException {
+ checkConnected();
+
+ byte addr = (byte) block;
+ byte[] blockwrite_cmd = new byte[data.length + 2];
+ blockwrite_cmd[0] = (byte) 0xA0; // MF write command
+ blockwrite_cmd[1] = addr;
+ System.arraycopy(data, 0, blockwrite_cmd, 2, data.length);
+
+ transceive(blockwrite_cmd, false);
+ }
+
+ /**
+ * Writes relative block in sector.
+ * <p>This requires a that the tag be connected.
+ * @throws IOException
+ */
+ public void writeBlock(int sector, int block, byte[] data) throws IOException {
+ checkConnected();
+
+ byte addr = (byte) ((firstBlockInSector(sector) + block) & 0xff);
+
+ writeBlock(addr, data);
+ }
+
+ public void increment(int block) throws IOException {
+ checkConnected();
+
+ byte[] incr_cmd = { (byte) 0xC1, (byte) block };
+
+ transceive(incr_cmd, false);
+ }
+
+ public void decrement(int block) throws IOException {
+ checkConnected();
+
+ byte[] decr_cmd = { (byte) 0xC0, (byte) block };
+
+ transceive(decr_cmd, false);
+ }
+
+ public void transfer(int block) throws IOException {
+ checkConnected();
+
+ byte[] trans_cmd = { (byte) 0xB0, (byte) block };
+
+ transceive(trans_cmd, false);
+ }
+
+ public void restore(int block) throws IOException {
+ checkConnected();
+
+ byte[] rest_cmd = { (byte) 0xC2, (byte) block };
+
+ transceive(rest_cmd, false);
+ }
+
+ /**
+ * Send raw NfcA data to a tag and receive the response.
+ * <p>
+ * This method will block until the response is received. It can be canceled
+ * with {@link #close}.
+ * <p>Requires {@link android.Manifest.permission#NFC} permission.
+ * <p>This requires a that the tag be connected.
+ *
+ * @param data bytes to send
+ * @return bytes received in response
+ * @throws IOException if the target is lost or connection closed
+ */
+ public byte[] transceive(byte[] data) throws IOException {
+ return transceive(data, true);
+ }
+}
diff --git a/core/java/android/nfc/tech/MifareUltralight.java b/core/java/android/nfc/tech/MifareUltralight.java
new file mode 100644
index 0000000..f096298
--- /dev/null
+++ b/core/java/android/nfc/tech/MifareUltralight.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc.tech;
+
+import android.nfc.Tag;
+import android.os.RemoteException;
+
+import java.io.IOException;
+
+/**
+ * Technology class representing MIFARE Ultralight and MIFARE Ultralight C tags.
+ *
+ * <p>Support for this technology type is optional. If the NFC stack doesn't support this technology
+ * MIFARE Ultralight class tags will still be scanned, but will only show the NfcA technology.
+ *
+ * <p>MIFARE Ultralight class tags have a series of 4 bytes pages that can be individually written
+ * and read in chunks of 4 for a total read of 16 bytes.
+ */
+public final class MifareUltralight extends BasicTagTechnology {
+ /** A MIFARE Ultralight tag */
+ public static final int TYPE_ULTRALIGHT = 1;
+ /** A MIFARE Ultralight C tag */
+ public static final int TYPE_ULTRALIGHT_C = 2;
+ /** The tag type is unknown */
+ public static final int TYPE_UNKNOWN = 10;
+
+ private static final int NXP_MANUFACTURER_ID = 0x04;
+
+ private int mType;
+
+ /**
+ * Returns an instance of this tech for the given tag. If the tag doesn't support
+ * this tech type null is returned.
+ *
+ * @param tag The tag to get the tech from
+ */
+ public static MifareUltralight get(Tag tag) {
+ if (!tag.hasTech(TagTechnology.MIFARE_ULTRALIGHT)) return null;
+ try {
+ return new MifareUltralight(tag);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /** @hide */
+ public MifareUltralight(Tag tag) throws RemoteException {
+ super(tag, TagTechnology.MIFARE_ULTRALIGHT);
+
+ // Check if this could actually be a Mifare
+ NfcA a = NfcA.get(tag);
+
+ mType = TYPE_UNKNOWN;
+
+ if (a.getSak() == 0x00 && tag.getId()[0] == NXP_MANUFACTURER_ID) {
+ // could be UL or UL-C
+ mType = TYPE_ULTRALIGHT;
+ }
+ }
+
+ /** Returns the type of the tag */
+ public int getType() {
+ return mType;
+ }
+
+ // Methods that require connect()
+ /**
+ * Reads a single 16 byte block from the given page offset.
+ *
+ * <p>This requires a that the tag be connected.
+ *
+ * @throws IOException
+ */
+ public byte[] readBlock(int page) throws IOException {
+ checkConnected();
+
+ byte[] blockread_cmd = { 0x30, (byte) page}; // phHal_eMifareRead
+ return transceive(blockread_cmd, false);
+ }
+
+ /**
+ * Writes a 4 byte page to the tag.
+ *
+ * <p>This requires a that the tag be connected.
+ *
+ * @param page The offset of the page to write
+ * @param data The data to write
+ * @throws IOException
+ */
+ public void writePage(int page, byte[] data) throws IOException {
+ checkConnected();
+
+ byte[] pagewrite_cmd = new byte[data.length + 2];
+ pagewrite_cmd[0] = (byte) 0xA2;
+ pagewrite_cmd[1] = (byte) page;
+ System.arraycopy(data, 0, pagewrite_cmd, 2, data.length);
+
+ transceive(pagewrite_cmd, false);
+ }
+
+ /**
+ * Send raw NfcA data to a tag and receive the response.
+ * <p>
+ * This method will block until the response is received. It can be canceled
+ * with {@link #close}.
+ * <p>Requires {@link android.Manifest.permission#NFC} permission.
+ * <p>This requires a that the tag be connected.
+ *
+ * @param data bytes to send
+ * @return bytes received in response
+ * @throws IOException if the target is lost or connection closed
+ */
+ public byte[] transceive(byte[] data) throws IOException {
+ return transceive(data, true);
+ }
+}
diff --git a/core/java/android/nfc/tech/Ndef.java b/core/java/android/nfc/tech/Ndef.java
new file mode 100644
index 0000000..03f2184
--- /dev/null
+++ b/core/java/android/nfc/tech/Ndef.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc.tech;
+
+import android.nfc.ErrorCodes;
+import android.nfc.FormatException;
+import android.nfc.INfcTag;
+import android.nfc.NdefMessage;
+import android.nfc.NfcAdapter;
+import android.nfc.Tag;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * A high-level connection to a {@link Tag} using one of the NFC type 1, 2, 3, or 4 technologies
+ * to interact with NDEF data. MiFare Classic cards that present NDEF data may also be used
+ * via this class. To determine the exact technology being used call {@link #getTechnologyId()}
+ *
+ * <p>You can acquire this kind of connection with {@link #get}.
+ *
+ * <p class="note"><strong>Note:</strong>
+ * Use of this class requires the {@link android.Manifest.permission#NFC}
+ * permission.
+ */
+public final class Ndef extends BasicTagTechnology {
+ private static final String TAG = "NFC";
+
+ /** @hide */
+ public static final int NDEF_MODE_READ_ONLY = 1;
+ /** @hide */
+ public static final int NDEF_MODE_READ_WRITE = 2;
+ /** @hide */
+ public static final int NDEF_MODE_UNKNOWN = 3;
+
+ /** @hide */
+ public static final String EXTRA_NDEF_MSG = "ndefmsg";
+
+ /** @hide */
+ public static final String EXTRA_NDEF_MAXLENGTH = "ndefmaxlength";
+
+ /** @hide */
+ public static final String EXTRA_NDEF_CARDSTATE = "ndefcardstate";
+
+ /** @hide */
+ public static final String EXTRA_NDEF_TYPE = "ndeftype";
+
+ public static final int OTHER = -1;
+ public static final int NFC_FORUM_TYPE_1 = 1;
+ public static final int NFC_FORUM_TYPE_2 = 2;
+ public static final int NFC_FORUM_TYPE_3 = 3;
+ public static final int NFC_FORUM_TYPE_4 = 4;
+ public static final int MIFARE_CLASSIC = 101;
+
+ private final int mMaxNdefSize;
+ private final int mCardState;
+ private final NdefMessage mNdefMsg;
+ private final int mNdefType;
+
+ /**
+ * Returns an instance of this tech for the given tag. If the tag doesn't support
+ * this tech type null is returned.
+ *
+ * @param tag The tag to get the tech from
+ */
+ public static Ndef get(Tag tag) {
+ if (!tag.hasTech(TagTechnology.NDEF)) return null;
+ try {
+ return new Ndef(tag);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Internal constructor, to be used by NfcAdapter
+ * @hide
+ */
+ public Ndef(Tag tag) throws RemoteException {
+ super(tag, TagTechnology.NDEF);
+ Bundle extras = tag.getTechExtras(TagTechnology.NDEF);
+ if (extras != null) {
+ mMaxNdefSize = extras.getInt(EXTRA_NDEF_MAXLENGTH);
+ mCardState = extras.getInt(EXTRA_NDEF_CARDSTATE);
+ mNdefMsg = extras.getParcelable(EXTRA_NDEF_MSG);
+ mNdefType = extras.getInt(EXTRA_NDEF_TYPE);
+ } else {
+ throw new NullPointerException("NDEF tech extras are null.");
+ }
+
+ }
+
+ /**
+ * Get the primary NDEF message on this tag. This data is read at discovery time
+ * and does not require a connection.
+ */
+ public NdefMessage getCachedNdefMessage() {
+ return mNdefMsg;
+ }
+
+ /**
+ * Get NDEF tag type.
+ * <p>Returns one of {@link #NFC_FORUM_TYPE_1}, {@link #NFC_FORUM_TYPE_2},
+ * {@link #NFC_FORUM_TYPE_3}, {@link #NFC_FORUM_TYPE_4},
+ * {@link #MIFARE_CLASSIC} or {@link #OTHER}.
+ * <p>Platforms of this API revision will always return one of the above
+ * values. Platforms at future API revisions may return other values, which
+ * can be treated as {@link #OTHER} by applications targeting this API.
+ * <p>Android devices with NFC support must always correctly enumerate
+ * NFC Forum tag types, and may optionally enumerate
+ * {@link #MIFARE_CLASSIC} since it requires proprietary technology.
+ * Devices that cannot enumerate {@link #MIFARE_CLASSIC} will use
+ * {@link #OTHER} instead.
+ */
+ public int getType() {
+ return mNdefType;
+ }
+
+ /**
+ * Get maximum NDEF message size in bytes
+ */
+ public int getMaxSize() {
+ return mMaxNdefSize;
+ }
+
+ /**
+ * Provides a hint on whether writes are likely to succeed.
+ * <p>Requires {@link android.Manifest.permission#NFC} permission.
+ * @return true if write is likely to succeed
+ */
+ public boolean isWritable() {
+ return (mCardState == NDEF_MODE_READ_WRITE);
+ }
+
+ // Methods that require connect()
+ /**
+ * Get the primary NDEF message on this tag. This data is read actively
+ * and requires a connection.
+ */
+ public NdefMessage getNdefMessage() throws IOException, FormatException {
+ checkConnected();
+
+ try {
+ INfcTag tagService = mTag.getTagService();
+ int serviceHandle = mTag.getServiceHandle();
+ if (tagService.isNdef(serviceHandle)) {
+ NdefMessage msg = tagService.ndefRead(serviceHandle);
+ if (msg == null) {
+ int errorCode = tagService.getLastError(serviceHandle);
+ switch (errorCode) {
+ case ErrorCodes.ERROR_IO:
+ throw new IOException();
+ case ErrorCodes.ERROR_INVALID_PARAM:
+ throw new FormatException();
+ default:
+ // Should not happen
+ throw new IOException();
+ }
+ }
+ return msg;
+ } else {
+ return null;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "NFC service dead", e);
+ return null;
+ }
+ }
+
+ /**
+ * Overwrite the primary NDEF message
+ * @throws IOException
+ */
+ public void writeNdefMessage(NdefMessage msg) throws IOException, FormatException {
+ checkConnected();
+
+ try {
+ INfcTag tagService = mTag.getTagService();
+ int serviceHandle = mTag.getServiceHandle();
+ if (tagService.isNdef(serviceHandle)) {
+ int errorCode = tagService.ndefWrite(serviceHandle, msg);
+ switch (errorCode) {
+ case ErrorCodes.SUCCESS:
+ break;
+ case ErrorCodes.ERROR_IO:
+ throw new IOException();
+ case ErrorCodes.ERROR_INVALID_PARAM:
+ throw new FormatException();
+ default:
+ // Should not happen
+ throw new IOException();
+ }
+ }
+ else {
+ throw new IOException("Tag is not ndef");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "NFC service dead", e);
+ }
+ }
+
+ /**
+ * Indicates whether a tag can be made read-only with
+ * {@link #makeReadonly()}
+ */
+ public boolean canMakeReadonly() {
+ if (mNdefType == NFC_FORUM_TYPE_1 || mNdefType == NFC_FORUM_TYPE_2) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Sets the CC field to indicate this tag is read-only
+ * and permanently sets the lock bits to prevent any further NDEF
+ * modifications.
+ * This is a one-way process and can not be reverted!
+ * @throws IOException
+ */
+ public boolean makeReadonly() throws IOException {
+ checkConnected();
+
+ try {
+ INfcTag tagService = mTag.getTagService();
+ if (tagService.isNdef(mTag.getServiceHandle())) {
+ int errorCode = tagService.ndefMakeReadOnly(mTag.getServiceHandle());
+ switch (errorCode) {
+ case ErrorCodes.SUCCESS:
+ return true;
+ case ErrorCodes.ERROR_IO:
+ throw new IOException();
+ case ErrorCodes.ERROR_INVALID_PARAM:
+ return false;
+ default:
+ // Should not happen
+ throw new IOException();
+ }
+ }
+ else {
+ throw new IOException("Tag is not ndef");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "NFC service dead", e);
+ return false;
+ }
+ }
+}
diff --git a/core/java/android/nfc/tech/NdefFormatable.java b/core/java/android/nfc/tech/NdefFormatable.java
new file mode 100644
index 0000000..2919c43
--- /dev/null
+++ b/core/java/android/nfc/tech/NdefFormatable.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc.tech;
+
+import android.nfc.ErrorCodes;
+import android.nfc.FormatException;
+import android.nfc.INfcTag;
+import android.nfc.NdefMessage;
+import android.nfc.NfcAdapter;
+import android.nfc.Tag;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * An interface to a {@link Tag} allowing to format the tag as NDEF.
+ *
+ * <p>You can acquire this kind of connection with {@link #get}.
+ *
+ * <p class="note"><strong>Note:</strong>
+ * Use of this class requires the {@link android.Manifest.permission#NFC}
+ * permission.
+ */
+public final class NdefFormatable extends BasicTagTechnology {
+ private static final String TAG = "NFC";
+
+ /**
+ * Returns an instance of this tech for the given tag. If the tag doesn't support
+ * this tech type null is returned.
+ *
+ * @param tag The tag to get the tech from
+ */
+ public static NdefFormatable get(Tag tag) {
+ if (!tag.hasTech(TagTechnology.NDEF_FORMATABLE)) return null;
+ try {
+ return new NdefFormatable(tag);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Internal constructor, to be used by NfcAdapter
+ * @hide
+ */
+ public NdefFormatable(Tag tag) throws RemoteException {
+ super(tag, TagTechnology.NDEF_FORMATABLE);
+ }
+
+ /**
+ * Formats a tag as NDEF, if possible. You may supply a first
+ * NdefMessage to be written on the tag.
+ */
+ public void format(NdefMessage firstMessage) throws IOException, FormatException {
+ checkConnected();
+
+ try {
+ int serviceHandle = mTag.getServiceHandle();
+ INfcTag tagService = mTag.getTagService();
+ int errorCode = tagService.formatNdef(serviceHandle, MifareClassic.KEY_DEFAULT);
+ switch (errorCode) {
+ case ErrorCodes.SUCCESS:
+ break;
+ case ErrorCodes.ERROR_IO:
+ throw new IOException();
+ case ErrorCodes.ERROR_INVALID_PARAM:
+ throw new FormatException();
+ default:
+ // Should not happen
+ throw new IOException();
+ }
+ // Now check and see if the format worked
+ if (tagService.isNdef(serviceHandle)) {
+ errorCode = tagService.ndefWrite(serviceHandle, firstMessage);
+ switch (errorCode) {
+ case ErrorCodes.SUCCESS:
+ break;
+ case ErrorCodes.ERROR_IO:
+ throw new IOException();
+ case ErrorCodes.ERROR_INVALID_PARAM:
+ throw new FormatException();
+ default:
+ // Should not happen
+ throw new IOException();
+ }
+ } else {
+ throw new IOException();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "NFC service dead", e);
+ }
+ }
+}
diff --git a/core/java/android/nfc/tech/NfcA.java b/core/java/android/nfc/tech/NfcA.java
new file mode 100644
index 0000000..24badc4
--- /dev/null
+++ b/core/java/android/nfc/tech/NfcA.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc.tech;
+
+import android.nfc.Tag;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import java.io.IOException;
+
+/**
+ * A low-level connection to a {@link Tag} using the NFC-A technology, also known as
+ * ISO1443-3A.
+ *
+ * <p>You can acquire this kind of connection with {@link #get}.
+ * Use this class to send and receive data with {@link #transceive transceive()}.
+ *
+ * <p>Applications must implement their own protocol stack on top of
+ * {@link #transceive transceive()}.
+ *
+ * <p class="note"><strong>Note:</strong>
+ * Use of this class requires the {@link android.Manifest.permission#NFC}
+ * permission.
+ */
+public final class NfcA extends BasicTagTechnology {
+ /** @hide */
+ public static final String EXTRA_SAK = "sak";
+ /** @hide */
+ public static final String EXTRA_ATQA = "atqa";
+
+ private short mSak;
+ private byte[] mAtqa;
+
+ /**
+ * Returns an instance of this tech for the given tag. If the tag doesn't support
+ * this tech type null is returned.
+ *
+ * @param tag The tag to get the tech from
+ */
+ public static NfcA get(Tag tag) {
+ if (!tag.hasTech(TagTechnology.NFC_A)) return null;
+ try {
+ return new NfcA(tag);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /** @hide */
+ public NfcA(Tag tag) throws RemoteException {
+ super(tag, TagTechnology.NFC_A);
+ Bundle extras = tag.getTechExtras(TagTechnology.NFC_A);
+ mSak = extras.getShort(EXTRA_SAK);
+ mAtqa = extras.getByteArray(EXTRA_ATQA);
+ }
+
+ /**
+ * Returns the ATQA/SENS_RES bytes discovered at tag discovery.
+ */
+ public byte[] getAtqa() {
+ return mAtqa;
+ }
+
+ /**
+ * Returns the SAK/SEL_RES discovered at tag discovery.
+ */
+ public short getSak() {
+ return mSak;
+ }
+
+ /**
+ * Send data to a tag and receive the response.
+ * <p>
+ * This method will block until the response is received. It can be canceled
+ * with {@link #close}.
+ * <p>Requires {@link android.Manifest.permission#NFC} permission.
+ *
+ * @param data bytes to send
+ * @return bytes received in response
+ * @throws IOException if the target is lost or connection closed
+ */
+ public byte[] transceive(byte[] data) throws IOException {
+ return transceive(data, true);
+ }
+}
diff --git a/core/java/android/nfc/tech/NfcB.java b/core/java/android/nfc/tech/NfcB.java
new file mode 100644
index 0000000..abeef32
--- /dev/null
+++ b/core/java/android/nfc/tech/NfcB.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc.tech;
+
+import android.nfc.Tag;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import java.io.IOException;
+
+/**
+ * A low-level connection to a {@link Tag} using the NFC-B technology, also known as
+ * ISO1443-3B.
+ *
+ * <p>You can acquire this kind of connection with {@link #get}.
+ * Use this class to send and receive data with {@link #transceive transceive()}.
+ *
+ * <p>Applications must implement their own protocol stack on top of
+ * {@link #transceive transceive()}.
+ *
+ * <p class="note"><strong>Note:</strong>
+ * Use of this class requires the {@link android.Manifest.permission#NFC}
+ * permission.
+ */
+public final class NfcB extends BasicTagTechnology {
+ /** @hide */
+ public static final String EXTRA_APPDATA = "appdata";
+ /** @hide */
+ public static final String EXTRA_PROTINFO = "protinfo";
+
+ private byte[] mAppData;
+ private byte[] mProtInfo;
+
+ /**
+ * Returns an instance of this tech for the given tag. If the tag doesn't support
+ * this tech type null is returned.
+ *
+ * @param tag The tag to get the tech from
+ */
+ public static NfcB get(Tag tag) {
+ if (!tag.hasTech(TagTechnology.NFC_B)) return null;
+ try {
+ return new NfcB(tag);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /** @hide */
+ public NfcB(Tag tag) throws RemoteException {
+ super(tag, TagTechnology.NFC_B);
+ Bundle extras = tag.getTechExtras(TagTechnology.NFC_B);
+ mAppData = extras.getByteArray(EXTRA_APPDATA);
+ mProtInfo = extras.getByteArray(EXTRA_PROTINFO);
+ }
+
+ /**
+ * Returns the Application Data bytes from the ATQB/SENSB_RES
+ * bytes discovered at tag discovery.
+ */
+ public byte[] getApplicationData() {
+ return mAppData;
+ }
+
+ /**
+ * Returns the Protocol Info bytes from the ATQB/SENSB_RES
+ * bytes discovered at tag discovery.
+ */
+ public byte[] getProtocolInfo() {
+ return mProtInfo;
+ }
+
+ /**
+ * Send data to a tag and receive the response.
+ * <p>
+ * This method will block until the response is received. It can be canceled
+ * with {@link #close}.
+ * <p>Requires {@link android.Manifest.permission#NFC} permission.
+ *
+ * @param data bytes to send
+ * @return bytes received in response
+ * @throws IOException if the target is lost or connection closed
+ */
+ public byte[] transceive(byte[] data) throws IOException {
+ return transceive(data, true);
+ }
+}
diff --git a/core/java/android/nfc/tech/NfcF.java b/core/java/android/nfc/tech/NfcF.java
new file mode 100644
index 0000000..f617739
--- /dev/null
+++ b/core/java/android/nfc/tech/NfcF.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc.tech;
+
+import android.nfc.Tag;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import java.io.IOException;
+
+/**
+ * A low-level connection to a {@link Tag} using the NFC-F technology, also known as
+ * JIS6319-4.
+ *
+ * <p>You can acquire this kind of connection with {@link #get}.
+ * Use this class to send and receive data with {@link #transceive transceive()}.
+ *
+ * <p>Applications must implement their own protocol stack on top of
+ * {@link #transceive transceive()}.
+ *
+ * <p class="note"><strong>Note:</strong>
+ * Use of this class requires the {@link android.Manifest.permission#NFC}
+ * permission.
+ */
+public final class NfcF extends BasicTagTechnology {
+ /** @hide */
+ public static final String EXTRA_SC = "systemcode";
+ /** @hide */
+ public static final String EXTRA_PMM = "pmm";
+
+ private byte[] mSystemCode = null;
+ private byte[] mManufacturer = null;
+
+ /**
+ * Returns an instance of this tech for the given tag. If the tag doesn't support
+ * this tech type null is returned.
+ *
+ * @param tag The tag to get the tech from
+ */
+ public static NfcF get(Tag tag) {
+ if (!tag.hasTech(TagTechnology.NFC_F)) return null;
+ try {
+ return new NfcF(tag);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /** @hide */
+ public NfcF(Tag tag) throws RemoteException {
+ super(tag, TagTechnology.NFC_F);
+ Bundle extras = tag.getTechExtras(TagTechnology.NFC_F);
+ if (extras != null) {
+ mSystemCode = extras.getByteArray(EXTRA_SC);
+ mManufacturer = extras.getByteArray(EXTRA_PMM);
+ }
+ }
+
+ public byte[] getSystemCode() {
+ return mSystemCode;
+ }
+
+ public byte[] getManufacturer() {
+ return mManufacturer;
+ }
+
+ /**
+ * Send data to a tag and receive the response.
+ * <p>
+ * This method will block until the response is received. It can be canceled
+ * with {@link #close}.
+ * <p>Requires {@link android.Manifest.permission#NFC} permission.
+ *
+ * @param data bytes to send
+ * @return bytes received in response
+ * @throws IOException if the target is lost or connection closed
+ */
+ public byte[] transceive(byte[] data) throws IOException {
+ return transceive(data, true);
+ }
+}
diff --git a/core/java/android/nfc/tech/NfcV.java b/core/java/android/nfc/tech/NfcV.java
new file mode 100644
index 0000000..8e1f066
--- /dev/null
+++ b/core/java/android/nfc/tech/NfcV.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc.tech;
+
+import android.nfc.Tag;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import java.io.IOException;
+
+/**
+ * A low-level connection to a {@link Tag} using NFC vicinity technology, also known as
+ * ISO15693.
+ *
+ * <p>You can acquire this kind of connection with {@link #get}.
+ * Use this class to send and receive data with {@link #transceive transceive()}.
+ *
+ * <p>Applications must implement their own protocol stack on top of
+ * {@link #transceive transceive()}.
+ *
+ * <p class="note"><strong>Note:</strong>
+ * Use of this class requires the {@link android.Manifest.permission#NFC}
+ * permission.
+ */
+public final class NfcV extends BasicTagTechnology {
+ /** @hide */
+ public static final String EXTRA_RESP_FLAGS = "respflags";
+
+ /** @hide */
+ public static final String EXTRA_DSFID = "dsfid";
+
+ private byte mRespFlags;
+ private byte mDsfId;
+
+ /**
+ * Returns an instance of this tech for the given tag. If the tag doesn't support
+ * this tech type null is returned.
+ *
+ * @param tag The tag to get the tech from
+ */
+ public static NfcV get(Tag tag) {
+ if (!tag.hasTech(TagTechnology.NFC_V)) return null;
+ try {
+ return new NfcV(tag);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /** @hide */
+ public NfcV(Tag tag) throws RemoteException {
+ super(tag, TagTechnology.NFC_V);
+ Bundle extras = tag.getTechExtras(TagTechnology.NFC_V);
+ mRespFlags = extras.getByte(EXTRA_RESP_FLAGS);
+ mDsfId = extras.getByte(EXTRA_DSFID);
+ }
+
+ public byte getResponseFlags() {
+ return mRespFlags;
+ }
+
+ public byte getDsfId() {
+ return mDsfId;
+ }
+
+ /**
+ * Send data to a tag and receive the response.
+ * <p>
+ * This method will block until the response is received. It can be canceled
+ * with {@link #close}.
+ * <p>Requires {@link android.Manifest.permission#NFC} permission.
+ *
+ * @param data bytes to send
+ * @return bytes received in response
+ * @throws IOException if the target is lost or connection closed
+ */
+ public byte[] transceive(byte[] data) throws IOException {
+ return transceive(data, true);
+ }
+}
diff --git a/core/java/android/nfc/tech/TagTechnology.java b/core/java/android/nfc/tech/TagTechnology.java
new file mode 100644
index 0000000..1331bae
--- /dev/null
+++ b/core/java/android/nfc/tech/TagTechnology.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc.tech;
+
+import android.nfc.Tag;
+
+import java.io.IOException;
+
+public interface TagTechnology {
+ /**
+ * This technology is an instance of {@link NfcA}.
+ * <p>Support for this technology type is mandatory.
+ * @hide
+ */
+ public static final int NFC_A = 1;
+
+ /**
+ * This technology is an instance of {@link NfcB}.
+ * <p>Support for this technology type is mandatory.
+ * @hide
+ */
+ public static final int NFC_B = 2;
+
+ /**
+ * This technology is an instance of {@link IsoDep}.
+ * <p>Support for this technology type is mandatory.
+ * @hide
+ */
+ public static final int ISO_DEP = 3;
+
+ /**
+ * This technology is an instance of {@link NfcF}.
+ * <p>Support for this technology type is mandatory.
+ * @hide
+ */
+ public static final int NFC_F = 4;
+
+ /**
+ * This technology is an instance of {@link NfcV}.
+ * <p>Support for this technology type is mandatory.
+ * @hide
+ */
+ public static final int NFC_V = 5;
+
+ /**
+ * This technology is an instance of {@link Ndef}.
+ * <p>Support for this technology type is mandatory.
+ * @hide
+ */
+ public static final int NDEF = 6;
+
+ /**
+ * This technology is an instance of {@link NdefFormatable}.
+ * <p>Support for this technology type is mandatory.
+ * @hide
+ */
+ public static final int NDEF_FORMATABLE = 7;
+
+ /**
+ * This technology is an instance of {@link MifareClassic}.
+ * <p>Support for this technology type is optional. If a stack doesn't support this technology
+ * type tags using it must still be discovered and present the lower level radio interface
+ * technologies in use.
+ * @hide
+ */
+ public static final int MIFARE_CLASSIC = 8;
+
+ /**
+ * This technology is an instance of {@link MifareUltralight}.
+ * <p>Support for this technology type is optional. If a stack doesn't support this technology
+ * type tags using it must still be discovered and present the lower level radio interface
+ * technologies in use.
+ * @hide
+ */
+ public static final int MIFARE_ULTRALIGHT = 9;
+
+ /**
+ * Returns the technology type for this tag connection.
+ * @hide
+ */
+ public int getTechnologyId();
+
+ /**
+ * Get the {@link Tag} object this technology came from.
+ */
+ public Tag getTag();
+
+ /**
+ * Opens a connection to the {@link Tag} enabling interactive commands. The command set
+ * varies by the technology type.
+ *
+ * <p>This method blocks until the connection has been established.
+ *
+ * <p>A call to {@link #close} from another thread will cancel a blocked call and cause an
+ * IOException to be thrown on the thread that is blocked.
+ *
+ * @see #reconnect()
+ * @see #close()
+ * @throws IOException if the target is lost, or connect canceled
+ */
+ public void connect() throws IOException;
+
+ /**
+ * Re-connect to the {@link Tag} associated with this connection. Reconnecting to a tag can be
+ * used to reset the state of the tag itself.
+ *
+ * <p>This method blocks until the connection is re-established.
+ *
+ * <p>A call to {@link #close} from another thread will cancel a blocked call and cause an
+ * IOException to be thrown on the thread that is blocked.
+ *
+ * @see #connect()
+ * @see #close()
+ * @throws IOException
+ */
+ public void reconnect() throws IOException;
+
+ /**
+ * Closes the connection to the {@link Tag}. This call is non-blocking and causes all blocking
+ * operations such as {@link #connect} to be canceled and immediately throw
+ * {@link java.io.IOException} on the thread that is blocked.
+ *
+ * <p>
+ * Once this method is called, this object cannot be re-used and should be discarded. Further
+ * calls to {@link #connect} will fail.
+ *
+ * @see #connect()
+ * @see #reconnect()
+ */
+ public void close();
+}