Push Tag/NdefTag implementation details into the service.

Tag/NdefTag objects should just be simple data objects. Push the mapping of
internal tag type to public rawTarget/ndefTarget into Nfc Service.

This gives an oppurtunity to clean up some Tag/NdefTag API methods. Most
significantly, adding createMockTag() and createMockNdefTag() to help with
application testing.

There will probably be some more tweaking of the types/targets in
Tag/NdefTag to come, this commit makes that a lot easier.

Also:
- Introduce getActivationBytes() and getPollBytes(). These are just stubs
for NFC service to implement, we have feedback these are really important
to help identify NFC tags.
- Based on outside advice, remove 3B_PRIME (roll into 3B) and TOPAZ (roll
into 3A).

Change-Id: I3e6789c047f6ee5c298bf76c65e0885cf3c15d97
Signed-off-by: Nick Pelly <npelly@google.com>
This commit is contained in:
Nick Pelly
2010-10-21 21:42:24 -07:00
parent 67496e591d
commit 07f3bee2db
6 changed files with 370 additions and 256 deletions

View File

@ -100172,6 +100172,29 @@
>
<implements name="android.os.Parcelable">
</implements>
<method name="createMockNdefTag"
return="android.nfc.NdefTag"
abstract="false"
native="false"
synchronized="false"
static="true"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="id" type="byte[]">
</parameter>
<parameter name="rawTargets" type="java.lang.String[]">
</parameter>
<parameter name="pollBytes" type="byte[]">
</parameter>
<parameter name="activationBytes" type="byte[]">
</parameter>
<parameter name="ndefTargets" type="java.lang.String[]">
</parameter>
<parameter name="messages" type="android.nfc.NdefMessage[][]">
</parameter>
</method>
<method name="getNdefMessages"
return="android.nfc.NdefMessage[]"
abstract="false"
@ -100641,6 +100664,25 @@
>
<implements name="android.os.Parcelable">
</implements>
<method name="createMockTag"
return="android.nfc.Tag"
abstract="false"
native="false"
synchronized="false"
static="true"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="id" type="byte[]">
</parameter>
<parameter name="rawTargets" type="java.lang.String[]">
</parameter>
<parameter name="pollBytes" type="byte[]">
</parameter>
<parameter name="activationBytes" type="byte[]">
</parameter>
</method>
<method name="describeContents"
return="int"
abstract="false"
@ -100652,6 +100694,17 @@
visibility="public"
>
</method>
<method name="getActivationBytes"
return="byte[]"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="getId"
return="byte[]"
abstract="false"
@ -100663,6 +100716,17 @@
visibility="public"
>
</method>
<method name="getPollBytes"
return="byte[]"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="getRawTargets"
return="java.lang.String[]"
abstract="false"
@ -100721,17 +100785,6 @@
visibility="public"
>
</field>
<field name="TARGET_ISO_14443_3B_PRIME"
type="java.lang.String"
transient="false"
volatile="false"
value="&quot;iso14443_3b&quot;"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="TARGET_ISO_14443_4"
type="java.lang.String"
transient="false"
@ -100776,17 +100829,6 @@
visibility="public"
>
</field>
<field name="TARGET_TOPAZ"
type="java.lang.String"
transient="false"
volatile="false"
value="&quot;topaz&quot;"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
</class>
</package>
<package name="android.opengl"

View File

@ -16,8 +16,6 @@
package android.nfc;
import java.util.HashMap;
import android.os.Parcel;
import android.os.Parcelable;
@ -33,19 +31,92 @@ import android.os.Parcelable;
* is possible for {@link NdefTag}s to contain multiple {@link NdefMessage}s.
* <p>{@link NfcAdapter#createNdefTagConnection createNdefTagConnection()} can be used to modify the
* contents of some tags.
* <p>This is an immutable data class.
* <p>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.
* <p>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.
* <p>This is based on Mifare Ultralight technology.
*/
public static final String TARGET_TYPE_2 = "type_2";
/**
* Target for NFC Forum Type 3 compliant tag.
* <p>This is based on Felica technology.
*/
public static final String TARGET_TYPE_3 = "type_3";
/**
* Target for NFC Forum Type 4 compliant tag.
* <p>This is based on Mifare Desfire technology.
*/
public static final String TARGET_TYPE_4 = "type_4";
/**
* Target for NFC Forum Enabled: Mifare Classic tag.
* <p>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.
* <p>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.
* <p>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 {
* <p>
* 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<mNdefTargets.length; i++) {
if (target.equals(mNdefTargets[i])) {
return mMessages[i];
}
}
return getNdefMessages();
}
/** TODO(npelly):
* - check that any single tag can only have one of each NDEF type
* - ok to include mifare_classic?
*/
public static final String TARGET_TYPE_1 = "type_1";
public static final String TARGET_TYPE_2 = "type_2";
public static final String TARGET_TYPE_3 = "type_3";
public static final String TARGET_TYPE_4 = "type_4";
public static final String TARGET_MIFARE_CLASSIC = "type_mifare_classic";
public static final String TARGET_OTHER = "other";
private static final HashMap<String, String[]> NDEF_TYPES_CONVERTION_TABLE = new HashMap<String, String[]>() {
{
// TODO: handle multiprotocol
// TODO: move INTERNAL_TARGET_Type to TARGET_TYPE mapping to NFC service
put(Tag.INTERNAL_TARGET_TYPE_JEWEL, new String[] { NdefTag.TARGET_TYPE_1 });
put(Tag.INTERNAL_TARGET_TYPE_MIFARE_UL, new String[] { NdefTag.TARGET_TYPE_2 });
put(Tag.INTERNAL_TARGET_TYPE_MIFARE_1K, new String[] { NdefTag.TARGET_MIFARE_CLASSIC });
put(Tag.INTERNAL_TARGET_TYPE_MIFARE_4K, new String[] { NdefTag.TARGET_MIFARE_CLASSIC });
put(Tag.INTERNAL_TARGET_TYPE_FELICA, new String[] { NdefTag.TARGET_TYPE_3 });
put(Tag.INTERNAL_TARGET_TYPE_ISO14443_4, new String[] { NdefTag.TARGET_TYPE_4 });
put(Tag.INTERNAL_TARGET_TYPE_MIFARE_DESFIRE, new String[] { NdefTag.TARGET_TYPE_4 });
}
};
private String[] convertToNdefType(String internalTypeName) {
String[] result = NDEF_TYPES_CONVERTION_TABLE.get(internalTypeName);
if (result == null) {
return new String[] { NdefTag.TARGET_OTHER };
}
return result;
throw new IllegalArgumentException("target (" + target + ") not found");
}
/**
* Return the
* Return the NDEF targets on this Tag that support NDEF messages.
*
* @return
*/
public String[] getNdefTargets() {
return convertToNdefType(mTypeName);
return mNdefTargets;
}
@Override
@ -131,19 +191,50 @@ public class NdefTag extends Tag implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
// Tag fields
dest.writeInt(mIsNdef ? 1 : 0);
writeBytesWithNull(dest, mId);
dest.writeInt(mRawTargets.length);
dest.writeStringArray(mRawTargets);
writeBytesWithNull(dest, mPollBytes);
writeBytesWithNull(dest, mActivationBytes);
dest.writeInt(mServiceHandle);
// NdefTag fields
dest.writeInt(mNdefTargets.length);
dest.writeStringArray(mNdefTargets);
dest.writeInt(mMessages.length);
dest.writeTypedArray(mMessages, flags);
for (NdefMessage[] ms : mMessages) {
dest.writeTypedArray(ms, flags);
}
}
public static final Parcelable.Creator<NdefTag> CREATOR =
new Parcelable.Creator<NdefTag>() {
public NdefTag createFromParcel(Parcel in) {
Tag tag = Tag.CREATOR.createFromParcel(in);
int messagesLength = in.readInt();
NdefMessage[] messages = new NdefMessage[messagesLength];
in.readTypedArray(messages, NdefMessage.CREATOR);
return new NdefTag(tag.mTypeName, tag.mUid, tag.mNativeHandle, messages);
boolean isNdef = (in.readInt() == 1);
if (!isNdef) {
throw new IllegalArgumentException("Creating NdefTag from Tag parcel");
}
// Tag fields
byte[] id = readBytesWithNull(in);
String[] rawTargets = new String[in.readInt()];
in.readStringArray(rawTargets);
byte[] pollBytes = readBytesWithNull(in);
byte[] activationBytes = readBytesWithNull(in);
int serviceHandle = in.readInt();
// NdefTag fields
String[] ndefTargets = new String[in.readInt()];
in.readStringArray(ndefTargets);
NdefMessage[][] messages = new NdefMessage[in.readInt()][];
for (int i=0; i<messages.length; i++) {
messages[i] = new NdefMessage[in.readInt()];
in.readTypedArray(messages[i], NdefMessage.CREATOR);
}
return new NdefTag(id, rawTargets, pollBytes, activationBytes, serviceHandle,
ndefTargets, messages);
}
public NdefTag[] newArray(int size) {
return new NdefTag[size];

View File

@ -81,9 +81,9 @@ public class NdefTagConnection extends RawTagConnection {
//TODO(nxp): do not use getLastError(), it is racy
try {
NdefMessage[] msgArray = new NdefMessage[1];
NdefMessage msg = mTagService.read(mTag.mNativeHandle);
NdefMessage msg = mTagService.read(mTag.mServiceHandle);
if (msg == null) {
int errorCode = mTagService.getLastError(mTag.mNativeHandle);
int errorCode = mTagService.getLastError(mTag.mServiceHandle);
switch (errorCode) {
case ErrorCodes.ERROR_IO:
throw new IOException();
@ -121,7 +121,7 @@ public class NdefTagConnection extends RawTagConnection {
*/
public void writeNdefMessage(NdefMessage message) throws IOException, FormatException {
try {
int errorCode = mTagService.write(mTag.mNativeHandle, message);
int errorCode = mTagService.write(mTag.mServiceHandle, message);
switch (errorCode) {
case ErrorCodes.SUCCESS:
break;
@ -148,7 +148,7 @@ public class NdefTagConnection extends RawTagConnection {
*/
public boolean makeReadOnly() throws IOException {
try {
int errorCode = mTagService.makeReadOnly(mTag.mNativeHandle);
int errorCode = mTagService.makeReadOnly(mTag.mServiceHandle);
switch (errorCode) {
case ErrorCodes.SUCCESS:
return true;
@ -175,7 +175,7 @@ public class NdefTagConnection extends RawTagConnection {
*/
public int getModeHint() throws IOException {
try {
int result = mTagService.getModeHint(mTag.mNativeHandle);
int result = mTagService.getModeHint(mTag.mServiceHandle);
if (ErrorCodes.isError(result)) {
switch (result) {
case ErrorCodes.ERROR_IO:

View File

@ -327,6 +327,9 @@ public final class NfcAdapter {
* <p>Requires {@link android.Manifest.permission#NFC} permission.
*/
public RawTagConnection createRawTagConnection(Tag tag) {
if (tag.mServiceHandle == 0) {
throw new IllegalArgumentException("mock tag cannot be used for connections");
}
try {
return new RawTagConnection(mService, tag);
} catch (RemoteException e) {
@ -340,6 +343,9 @@ public final class NfcAdapter {
* <p>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 {
* <p>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 {
* <p>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) {

View File

@ -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");
}

View File

@ -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}.
* <p>This is an immutable data class.
* <p>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.
* <p>
* 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<String, Integer> INT_TYPES_CONVERTION_TABLE = new HashMap<String, Integer>() {
{
put(Tag.INTERNAL_TARGET_TYPE_ISO14443_3A, Tag.NFC_TAG_ISO14443_A );
put(Tag.INTERNAL_TARGET_TYPE_ISO14443_3B, Tag.NFC_TAG_ISO14443_B );
put(Tag.INTERNAL_TARGET_TYPE_MIFARE_UL, Tag.NFC_TAG_MIFARE );
put(Tag.INTERNAL_TARGET_TYPE_MIFARE_1K, Tag.NFC_TAG_MIFARE );
put(Tag.INTERNAL_TARGET_TYPE_MIFARE_4K, Tag.NFC_TAG_MIFARE );
put(Tag.INTERNAL_TARGET_TYPE_MIFARE_DESFIRE, Tag.NFC_TAG_MIFARE );
put(Tag.INTERNAL_TARGET_TYPE_FELICA, Tag.NFC_TAG_FELICA );
put(Tag.INTERNAL_TARGET_TYPE_JEWEL, Tag.NFC_TAG_JEWEL );
}
};
private int convertToInt(String internalTypeName) {
Integer result = INT_TYPES_CONVERTION_TABLE.get(internalTypeName);
if (result == null) {
return Tag.NFC_TAG_OTHER;
}
return result;
}
private static final HashMap<String, String[]> RAW_TYPES_CONVERTION_TABLE = new HashMap<String, String[]>() {
{
/* TODO: handle multiprotocol */
put(Tag.INTERNAL_TARGET_TYPE_ISO14443_3A, new String[] { Tag.TARGET_ISO_14443_3A });
put(Tag.INTERNAL_TARGET_TYPE_ISO14443_3B, new String[] { Tag.TARGET_ISO_14443_3B });
put(Tag.INTERNAL_TARGET_TYPE_MIFARE_UL, new String[] { Tag.TARGET_ISO_14443_3A });
put(Tag.INTERNAL_TARGET_TYPE_MIFARE_1K, new String[] { Tag.TARGET_ISO_14443_3A });
put(Tag.INTERNAL_TARGET_TYPE_MIFARE_4K, new String[] { Tag.TARGET_ISO_14443_3A });
put(Tag.INTERNAL_TARGET_TYPE_MIFARE_DESFIRE, new String[] { Tag.TARGET_ISO_14443_3A });
put(Tag.INTERNAL_TARGET_TYPE_MIFARE_UNKNOWN, new String[] { Tag.TARGET_ISO_14443_3A });
put(Tag.INTERNAL_TARGET_TYPE_FELICA, new String[] { Tag.TARGET_JIS_X_6319_4 });
put(Tag.INTERNAL_TARGET_TYPE_JEWEL, new String[] { Tag.TARGET_TOPAZ });
}
};
private String[] convertToRaw(String internalTypeName) {
String[] result = RAW_TYPES_CONVERTION_TABLE.get(internalTypeName);
if (result == null) {
return new String[] { Tag.TARGET_OTHER };
}
return result;
}
/*package*/ final byte[] mId;
/*package*/ final String[] mRawTargets;
/*package*/ final byte[] mPollBytes;
/*package*/ final byte[] mActivationBytes;
/*package*/ final int mServiceHandle; // for use by NFC service, 0 indicates a mock
/**
* Hidden constructor to be used by NFC service only.
* Hidden constructor to be used by NFC service and internal classes.
* @hide
*/
public Tag(String typeName, boolean isNdef, byte[] uid, int nativeHandle) {
mTypeName = typeName;
public Tag(byte[] id, boolean isNdef, String[] rawTargets, byte[] pollBytes,
byte[] activationBytes, int serviceHandle) {
if (rawTargets == null) {
throw new IllegalArgumentException("rawTargets cannot be null");
}
mIsNdef = isNdef;
mUid = uid.clone();
mNativeHandle = nativeHandle;
mId = id;
mRawTargets = rawTargets;
mPollBytes = pollBytes;
mActivationBytes = activationBytes;
mServiceHandle = serviceHandle;
}
/**
* Construct a mock Tag.
* <p>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.
* <p>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.
* <p>
* 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).
* <p>
* Tag ID is usually a serial number for the tag.
* <p>
* The Tag ID is read at discovery time and this method does not cause any
* further RF activity and does not block.
* <p>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.
* <p>These can be used to help with advanced identification of a Tag.
* <p>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.
* <p>These can be used to help with advanced identification of a Tag.
* <p>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<Tag> CREATOR =
new Parcelable.Creator<Tag>() {
public Tag createFromParcel(Parcel in) {
boolean[] booleans = new boolean[1];
String type = in.readString();
in.readBooleanArray(booleans);
boolean isNdef = booleans[0];
int uidLength = in.readInt();
byte[] uid = new byte[uidLength];
in.readByteArray(uid);
int nativeHandle = in.readInt();
boolean isNdef = (in.readInt() == 1);
if (isNdef) {
throw new IllegalArgumentException("Creating Tag from NdefTag parcel");
}
// Tag fields
byte[] id = Tag.readBytesWithNull(in);
String[] rawTargets = new String[in.readInt()];
in.readStringArray(rawTargets);
byte[] pollBytes = Tag.readBytesWithNull(in);
byte[] activationBytes = Tag.readBytesWithNull(in);
int serviceHandle = in.readInt();
return new Tag(type, isNdef, uid, nativeHandle);
return new Tag(id, isNdef, rawTargets, pollBytes, activationBytes, serviceHandle);
}
public Tag[] newArray(int size) {
return new Tag[size];