This is an immutable data class. + *
This is an immutable data class. All properties are set at Tag discovery + * time and calls on this class will retrieve those read-only properties, and + * not cause any further RF activity or block. Note however that arrays passed to and + * returned by this class are *not* cloned, so be careful not to modify them. */ public class NdefTag extends Tag implements Parcelable { - private final NdefMessage[] mMessages; + /** + * Target for NFC Forum Type 1 compliant tag. + *
This is based on Jewel/Topaz technology + */ + public static final String TARGET_TYPE_1 = "type_1"; /** - * Hidden constructor to be used by NFC service when a - * tag is discovered and by Parcelable methods. + * Target for NFC Forum Type 2 compliant tag. + *
This is based on Mifare Ultralight technology. + */ + public static final String TARGET_TYPE_2 = "type_2"; + + /** + * Target for NFC Forum Type 3 compliant tag. + *
This is based on Felica technology. + */ + public static final String TARGET_TYPE_3 = "type_3"; + + /** + * Target for NFC Forum Type 4 compliant tag. + *
This is based on Mifare Desfire technology. + */ + public static final String TARGET_TYPE_4 = "type_4"; + + /** + * Target for NFC Forum Enabled: Mifare Classic tag. + *
This is not strictly a NFC Forum tag type, but is a common + * NDEF message container. + */ + public static final String TARGET_MIFARE_CLASSIC = "type_mifare_classic"; + + /** + * Any other target. + */ + public static final String TARGET_OTHER = "other"; + + private final String[] mNdefTargets; + private final NdefMessage[][] mMessages; // one NdefMessage[] per NDEF target + private NdefMessage[] mFlatMessages; // collapsed mMessages, built lazily, protected by (this) + + /** + * Hidden constructor to be used by NFC service only. * @hide */ - public NdefTag(String typeName, byte[] uid, int nativeHandle, NdefMessage[] messages) { - super(typeName, true, uid, nativeHandle); - mMessages = messages.clone(); + public NdefTag(byte[] id, String[] rawTargets, byte[] pollBytes, byte[] activationBytes, + int serviceHandle, String[] ndefTargets, NdefMessage[][] messages) { + super(id, true, rawTargets, pollBytes, activationBytes, serviceHandle); + if (ndefTargets == null || messages == null) { + throw new IllegalArgumentException("ndefTargets or messages cannot be null"); + } + if (ndefTargets.length != messages.length){ + throw new IllegalArgumentException("ndefTargets and messages arrays must match"); + } + for (NdefMessage[] ms : messages) { + if (ms == null) { + throw new IllegalArgumentException("messages elements cannot be null"); + } + } + mNdefTargets = ndefTargets; + mMessages = messages; + } + + /** + * Construct a mock NdefTag. + *
This is an application constructed tag, so NfcAdapter methods on this + * Tag such as {@link NfcAdapter#createRawTagConnection} will fail with + * {@link IllegalArgumentException} since it does not represent a physical Tag. + *
This constructor might be useful for mock testing. + * @param id The tag identifier, can be null + * @param rawTargets must not be null + * @param pollBytes can be null + * @param activationBytes can be null + * @param ndefTargets NDEF target array, such as {TARGET_TYPE_2}, cannot be null + * @param messages messages, one array per NDEF target, cannot be null + * @return freshly constructed NdefTag + */ + public static NdefTag createMockNdefTag(byte[] id, String[] rawTargets, byte[] pollBytes, + byte[] activationBytes, String[] ndefTargets, NdefMessage[][] messages) { + // set serviceHandle to 0 to indicate mock tag + return new NdefTag(id, rawTargets, pollBytes, activationBytes, 0, ndefTargets, messages); } /** @@ -59,7 +130,29 @@ public class NdefTag extends Tag implements Parcelable { * @return NDEF Messages found at Tag discovery */ public NdefMessage[] getNdefMessages() { - return mMessages.clone(); + // common-case optimization + if (mMessages.length == 1) { + return mMessages[0]; + } + + // return cached flat array + synchronized(this) { + if (mFlatMessages != null) { + return mFlatMessages; + } + // not cached - build a flat array + int sz = 0; + for (NdefMessage[] ms : mMessages) { + sz += ms.length; + } + mFlatMessages = new NdefMessage[sz]; + int i = 0; + for (NdefMessage[] ms : mMessages) { + System.arraycopy(ms, 0, mFlatMessages, i, ms.length); + i += ms.length; + } + return mFlatMessages; + } } /** @@ -70,58 +163,25 @@ public class NdefTag extends Tag implements Parcelable { *
* Most tags only contain a single NDEF message.
*
- * @param target One of targets strings provided by getNdefTargets()
+ * @param target one of targets strings provided by getNdefTargets()
* @return NDEF Messages found at Tag discovery
*/
public NdefMessage[] getNdefMessages(String target) {
- // TODO: handle multiprotocol
- String[] localTypes = convertToNdefType(mTypeName);
- if (!target.equals(localTypes[0])) {
- throw new IllegalArgumentException();
+ for (int i=0; i Requires {@link android.Manifest.permission#NFC} permission.
*/
public RawTagConnection createRawTagConnection(Tag tag, String target) {
+ if (tag.mServiceHandle == 0) {
+ throw new IllegalArgumentException("mock tag cannot be used for connections");
+ }
try {
return new RawTagConnection(mService, tag, target);
} catch (RemoteException e) {
@@ -353,6 +359,9 @@ public final class NfcAdapter {
* Requires {@link android.Manifest.permission#NFC} permission.
*/
public NdefTagConnection createNdefTagConnection(NdefTag tag) {
+ if (tag.mServiceHandle == 0) {
+ throw new IllegalArgumentException("mock tag cannot be used for connections");
+ }
try {
return new NdefTagConnection(mService, tag);
} catch (RemoteException e) {
@@ -366,6 +375,9 @@ public final class NfcAdapter {
* Requires {@link android.Manifest.permission#NFC} permission.
*/
public NdefTagConnection createNdefTagConnection(NdefTag tag, String target) {
+ if (tag.mServiceHandle == 0) {
+ throw new IllegalArgumentException("mock tag cannot be used for connections");
+ }
try {
return new NdefTagConnection(mService, tag, target);
} catch (RemoteException e) {
diff --git a/core/java/android/nfc/RawTagConnection.java b/core/java/android/nfc/RawTagConnection.java
index 1261db10a86c5..cf8283b762be4 100644
--- a/core/java/android/nfc/RawTagConnection.java
+++ b/core/java/android/nfc/RawTagConnection.java
@@ -100,7 +100,7 @@ public class RawTagConnection {
}
try {
- return mTagService.isPresent(mTag.mNativeHandle);
+ return mTagService.isPresent(mTag.mServiceHandle);
} catch (RemoteException e) {
Log.e(TAG, "NFC service died", e);
return false;
@@ -135,7 +135,7 @@ public class RawTagConnection {
public void close() {
mIsConnected = false;
try {
- mTagService.close(mTag.mNativeHandle);
+ mTagService.close(mTag.mServiceHandle);
} catch (RemoteException e) {
Log.e(TAG, "NFC service died", e);
}
@@ -154,7 +154,7 @@ public class RawTagConnection {
*/
public byte[] transceive(byte[] data) throws IOException {
try {
- byte[] response = mTagService.transceive(mTag.mNativeHandle, data);
+ byte[] response = mTagService.transceive(mTag.mServiceHandle, data);
if (response == null) {
throw new IOException("transcieve failed");
}
diff --git a/core/java/android/nfc/Tag.java b/core/java/android/nfc/Tag.java
index abf02b5024f2b..7741ad2ae462c 100644
--- a/core/java/android/nfc/Tag.java
+++ b/core/java/android/nfc/Tag.java
@@ -16,8 +16,6 @@
package android.nfc;
-import java.util.HashMap;
-
import android.os.Parcel;
import android.os.Parcelable;
@@ -39,202 +37,168 @@ import android.os.Parcelable;
* range. If it is removed and then returned to range, then the most recent
* {@link Tag} object (in {@link NfcAdapter#ACTION_TAG_DISCOVERED}) should be used to create a
* {@link RawTagConnection}.
- * This is an immutable data class.
+ * This is an immutable data class. All properties are set at Tag discovery
+ * time and calls on this class will retrieve those read-only properties, and
+ * not cause any further RF activity or block. Note however that arrays passed to and
+ * returned by this class are *not* cloned, so be careful not to modify them.
*/
public class Tag implements Parcelable {
-
/**
- * @hide
+ * ISO 14443-3A technology.
+ *
+ * Includes Topaz (which is -3A compatible)
*/
- public static final int NFC_TAG_ISO14443_A = 1; /* phNfc_eISO14443_A_PICC */
-
- /**
- * @hide
- */
- public static final int NFC_TAG_ISO14443_4A = 2; /* phNfc_eISO14443_4A_PICC */
-
- /**
- * @hide
- */
- public static final int NFC_TAG_ISO14443_3A = 3; /* phNfc_eISO14443_3A_PICC */
-
- /**
- * @hide
- */
- public static final int NFC_TAG_MIFARE = 4; /* phNfc_eMifare_PICC */
-
- /**
- * @hide
- */
- public static final int NFC_TAG_ISO14443_B = 5; /* phNfc_eISO14443_B_PICC */
-
- /**
- * @hide
- */
- public static final int NFC_TAG_ISO14443_4B = 6; /* phNfc_eISO14443_4B_PICC */
-
- /**
- * @hide
- */
- public static final int NFC_TAG_ISO14443_B_PRIME = 7; /* phNfc_eISO14443_BPrime_PICC */
-
- /**
- * @hide
- */
- public static final int NFC_TAG_FELICA = 8; /* phNfc_eFelica_PICC */
-
- /**
- * @hide
- */
- public static final int NFC_TAG_JEWEL = 9; /* phNfc_eJewel_PICC */
-
- /**
- * @hide
- */
- public static final int NFC_TAG_ISO15693 = 10; /* phNfc_eISO15693_PICC */
-
- /**
- * @hide
- */
- public static final int NFC_TAG_OTHER = 11; /* phNfc_ePICC_DevType */
-
-
public static final String TARGET_ISO_14443_3A = "iso14443_3a";
+ /**
+ * ISO 14443-3B technology.
+ */
public static final String TARGET_ISO_14443_3B = "iso14443_3b";
- public static final String TARGET_ISO_14443_3B_PRIME = "iso14443_3b";
-
+ /**
+ * ISO 14443-4 technology.
+ */
public static final String TARGET_ISO_14443_4 = "iso14443_4";
+ /**
+ * ISO 15693 technology, commonly known as RFID.
+ */
public static final String TARGET_ISO_15693 = "iso15693";
+ /**
+ * JIS X-6319-4 technology, commonly known as Felica.
+ */
public static final String TARGET_JIS_X_6319_4 = "jis_x_6319_4";
- public static final String TARGET_TOPAZ = "topaz";
-
+ /**
+ * Any other technology.
+ */
public static final String TARGET_OTHER = "other";
- /*package*/ final String mTypeName;
/*package*/ final boolean mIsNdef;
- /*package*/ final byte[] mUid;
- /*package*/ final int mNativeHandle;
-
- /*package*/ static final String INTERNAL_TARGET_TYPE_ISO14443_3A = "Iso14443-3A";
- /*package*/ static final String INTERNAL_TARGET_TYPE_ISO14443_3B = "Iso14443-3B";
- /*package*/ static final String INTERNAL_TARGET_TYPE_ISO14443_4 = "Iso14443-4";
- /*package*/ static final String INTERNAL_TARGET_TYPE_MIFARE_UL = "MifareUL";
- /*package*/ static final String INTERNAL_TARGET_TYPE_MIFARE_1K = "Mifare1K";
- /*package*/ static final String INTERNAL_TARGET_TYPE_MIFARE_4K = "Mifare4K";
- /*package*/ static final String INTERNAL_TARGET_TYPE_MIFARE_DESFIRE = "MifareDESFIRE";
- /*package*/ static final String INTERNAL_TARGET_TYPE_MIFARE_UNKNOWN = "Unknown Mifare";
- /*package*/ static final String INTERNAL_TARGET_TYPE_FELICA = "Felica";
- /*package*/ static final String INTERNAL_TARGET_TYPE_JEWEL = "Jewel";
- /*package*/ static final String INTERNAL_TARGET_TYPE_UNKNOWN = "Unknown Type";
-
- private static final HashMap This is an application constructed tag, so NfcAdapter methods on this
+ * Tag such as {@link NfcAdapter#createRawTagConnection} will fail with
+ * {@link IllegalArgumentException} since it does not represent a physical Tag.
+ * This constructor might be useful for mock testing.
+ * @param id The tag identifier, can be null
+ * @param rawTargets must not be null
+ * @param pollBytes can be null
+ * @param activationBytes can be null
+ * @return freshly constructed tag
+ */
+ public static Tag createMockTag(byte[] id, String[] rawTargets, byte[] pollBytes,
+ byte[] activationBytes) {
+ // set serviceHandle to 0 to indicate mock tag
+ return new Tag(id, false, rawTargets, pollBytes, activationBytes, 0);
}
/**
* For use by NfcService only.
* @hide
*/
- public int getHandle() {
- return mNativeHandle;
+ public int getServiceHandle() {
+ return mServiceHandle;
}
/**
* Return the available targets that this NFC adapter can use to create
* a RawTagConnection.
*
- * @return
+ * @return raw targets, will not be null
*/
public String[] getRawTargets() {
- return convertToRaw(mTypeName);
- }
-
- /**
- * Get the Tag type.
- *
- * The Tag type is one of the NFC_TAG constants. It is read at discovery
- * time and this method does not cause any further RF activity and does not
- * block.
- *
- * @return a NFC_TAG constant
- * @hide
- */
- public int getType() {
- return convertToInt(mTypeName);
+ return mRawTargets;
}
/**
* Get the Tag Identifier (if it has one).
- *
- * Tag ID is usually a serial number for the tag.
- *
- * The Tag ID is read at discovery time and this method does not cause any
- * further RF activity and does not block.
+ * Tag ID is usually a serial number for the tag.
*
* @return ID, or null if it does not exist
*/
public byte[] getId() {
- if (mUid.length > 0) {
- return mUid.clone();
- } else {
- return null;
+ return mId;
+ }
+
+ /**
+ * Get the low-level bytes returned by this Tag at poll-time.
+ * These can be used to help with advanced identification of a Tag.
+ * The meaning of these bytes depends on the Tag technology.
+ * @return poll bytes, or null if they do not exist for this Tag technology
+ */
+ public byte[] getPollBytes() {
+ return mPollBytes;
+ }
+
+ /**
+ * Get the low-level bytes returned by this Tag at activation-time.
+ * These can be used to help with advanced identification of a Tag.
+ * The meaning of these bytes depends on the Tag technology.
+ * @return activation bytes, or null if they do not exist for this Tag technology
+ */
+ public byte[] getActivationBytes() {
+ return mActivationBytes;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("TAG ")
+ .append("uid = ")
+ .append(mId)
+ .append(" poll ")
+ .append(mPollBytes)
+ .append(" activation ")
+ .append(mActivationBytes)
+ .append(" Raw [");
+ for (String s : mRawTargets) {
+ sb.append(s)
+ .append(", ");
}
+ return sb.toString();
+ }
+
+ /*package*/ static byte[] readBytesWithNull(Parcel in) {
+ int len = in.readInt();
+ byte[] result = null;
+ if (len > 0) {
+ result = new byte[len];
+ in.readByteArray(result);
+ }
+ return result;
+ }
+
+ /*package*/ static void writeBytesWithNull(Parcel out, byte[] b) {
+ if (b == null) {
+ out.writeInt(-1);
+ return;
+ }
+ out.writeInt(b.length);
+ out.writeByteArray(b);
}
@Override
@@ -242,29 +206,34 @@ public class Tag implements Parcelable {
return 0;
}
+
@Override
public void writeToParcel(Parcel dest, int flags) {
- boolean[] booleans = new boolean[] {mIsNdef};
- dest.writeString(mTypeName);
- dest.writeBooleanArray(booleans);
- dest.writeInt(mUid.length);
- dest.writeByteArray(mUid);
- dest.writeInt(mNativeHandle);
+ dest.writeInt(mIsNdef ? 1 : 0);
+ writeBytesWithNull(dest, mId);
+ dest.writeInt(mRawTargets.length);
+ dest.writeStringArray(mRawTargets);
+ writeBytesWithNull(dest, mPollBytes);
+ writeBytesWithNull(dest, mActivationBytes);
+ dest.writeInt(mServiceHandle);
}
public static final Parcelable.Creator