Fields Summary |
---|
private static final String | TAG |
public static final byte[] | KEY_DEFAULTThe default factory key. |
public static final byte[] | KEY_MIFARE_APPLICATION_DIRECTORYThe well-known key for tags formatted according to the
MIFARE Application Directory (MAD) specification. |
public static final byte[] | KEY_NFC_FORUMThe well-known key for tags formatted according to the
NDEF on MIFARE Classic specification. |
public static final int | TYPE_UNKNOWNA MIFARE Classic compatible card of unknown type |
public static final int | TYPE_CLASSICA MIFARE Classic tag |
public static final int | TYPE_PLUSA MIFARE Plus tag |
public static final int | TYPE_PROA MIFARE Pro tag |
public static final int | SIZE_1KTag contains 16 sectors, each with 4 blocks. |
public static final int | SIZE_2KTag contains 32 sectors, each with 4 blocks. |
public static final int | SIZE_4KTag contains 40 sectors. The first 32 sectors contain 4 blocks and the last 8 sectors
contain 16 blocks. |
public static final int | SIZE_MINITag contains 5 sectors, each with 4 blocks. |
public static final int | BLOCK_SIZESize of a MIFARE Classic block (in bytes) |
private static final int | MAX_BLOCK_COUNT |
private static final int | MAX_SECTOR_COUNT |
private boolean | mIsEmulated |
private int | mType |
private int | mSize |
Methods Summary |
---|
private boolean | authenticate(int sector, byte[] key, boolean keyA)
validateSector(sector);
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
// Authenticate command takes a block address. Authenticating a block
// of a sector will authenticate the entire sector.
cmd[1] = (byte) sectorToBlock(sector);
// 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;
|
public boolean | authenticateSectorWithKeyA(int sectorIndex, byte[] key)Authenticate a sector with key A.
Successful authentication of a sector with key A enables other
I/O operations on that sector. The set of operations granted by key A
key depends on the ACL bits set in that sector. For more information
see the MIFARE Classic specification on {@see http://www.nxp.com}.
A failed authentication attempt causes an implicit reconnection to the
tag, so authentication to other sectors will be lost.
This is an I/O operation and will block until complete. It must
not be called from the main application thread. A blocked call will be canceled with
{@link IOException} if {@link #close} is called from another thread.
Requires the {@link android.Manifest.permission#NFC} permission.
return authenticate(sectorIndex, key, true);
|
public boolean | authenticateSectorWithKeyB(int sectorIndex, byte[] key)Authenticate a sector with key B.
Successful authentication of a sector with key B enables other
I/O operations on that sector. The set of operations granted by key B
depends on the ACL bits set in that sector. For more information
see the MIFARE Classic specification on {@see http://www.nxp.com}.
A failed authentication attempt causes an implicit reconnection to the
tag, so authentication to other sectors will be lost.
This is an I/O operation and will block until complete. It must
not be called from the main application thread. A blocked call will be canceled with
{@link IOException} if {@link #close} is called from another thread.
Requires the {@link android.Manifest.permission#NFC} permission.
return authenticate(sectorIndex, key, false);
|
public int | blockToSector(int blockIndex)Return the sector that contains a given block.
Does not cause any RF activity and does not block.
validateBlock(blockIndex);
if (blockIndex < 32 * 4) {
return blockIndex / 4;
} else {
return 32 + (blockIndex - 32 * 4) / 16;
}
|
public void | decrement(int blockIndex, int value)Decrement a value block, storing the result in the temporary block on the tag.
This is an I/O operation and will block until complete. It must
not be called from the main application thread. A blocked call will be canceled with
{@link IOException} if {@link #close} is called from another thread.
Requires the {@link android.Manifest.permission#NFC} permission.
validateBlock(blockIndex);
validateValueOperand(value);
checkConnected();
ByteBuffer cmd = ByteBuffer.allocate(6);
cmd.order(ByteOrder.LITTLE_ENDIAN);
cmd.put( (byte) 0xC0 );
cmd.put( (byte) blockIndex );
cmd.putInt(value);
transceive(cmd.array(), false);
|
public static android.nfc.tech.MifareClassic | get(android.nfc.Tag tag)Get an instance of {@link MifareClassic} for the given tag.
Does not cause any RF activity and does not block.
Returns null if {@link MifareClassic} was not enumerated in {@link Tag#getTechList}.
This indicates the tag is not MIFARE Classic compatible, or this Android
device does not support MIFARE Classic.
if (!tag.hasTech(TagTechnology.MIFARE_CLASSIC)) return null;
try {
return new MifareClassic(tag);
} catch (RemoteException e) {
return null;
}
|
public int | getBlockCount()Return the total number of MIFARE Classic blocks.
Does not cause any RF activity and does not block.
return mSize / BLOCK_SIZE;
|
public int | getBlockCountInSector(int sectorIndex)Return the number of blocks in the given sector.
Does not cause any RF activity and does not block.
validateSector(sectorIndex);
if (sectorIndex < 32) {
return 4;
} else {
return 16;
}
|
public int | getMaxTransceiveLength()Return the maximum number of bytes that can be sent with {@link #transceive}.
return getMaxTransceiveLengthInternal();
|
public int | getSectorCount()Return the number of MIFARE Classic sectors.
Does not cause any RF activity and does not block.
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;
}
|
public int | getSize()Return the size of the tag in bytes
One of {@link #SIZE_MINI}, {@link #SIZE_1K}, {@link #SIZE_2K}, {@link #SIZE_4K}.
These constants are equal to their respective size in bytes.
Does not cause any RF activity and does not block.
return mSize;
|
public int | getTimeout()Get the current {@link #transceive} timeout in milliseconds.
Requires the {@link android.Manifest.permission#NFC} permission.
try {
return mTag.getTagService().getTimeout(TagTechnology.MIFARE_CLASSIC);
} catch (RemoteException e) {
Log.e(TAG, "NFC service dead", e);
return 0;
}
|
public int | getType()Return the type of this MIFARE Classic compatible tag.
One of {@link #TYPE_UNKNOWN}, {@link #TYPE_CLASSIC}, {@link #TYPE_PLUS} or
{@link #TYPE_PRO}.
Does not cause any RF activity and does not block.
return mType;
|
public void | increment(int blockIndex, int value)Increment a value block, storing the result in the temporary block on the tag.
This is an I/O operation and will block until complete. It must
not be called from the main application thread. A blocked call will be canceled with
{@link IOException} if {@link #close} is called from another thread.
Requires the {@link android.Manifest.permission#NFC} permission.
validateBlock(blockIndex);
validateValueOperand(value);
checkConnected();
ByteBuffer cmd = ByteBuffer.allocate(6);
cmd.order(ByteOrder.LITTLE_ENDIAN);
cmd.put( (byte) 0xC1 );
cmd.put( (byte) blockIndex );
cmd.putInt(value);
transceive(cmd.array(), false);
|
public boolean | isEmulated()Return true if the tag is emulated, determined at discovery time.
These are actually smart-cards that emulate a MIFARE Classic interface.
They can be treated identically to a MIFARE Classic tag.
return mIsEmulated;
|
public byte[] | readBlock(int blockIndex)Read 16-byte block.
This is an I/O operation and will block until complete. It must
not be called from the main application thread. A blocked call will be canceled with
{@link IOException} if {@link #close} is called from another thread.
Requires the {@link android.Manifest.permission#NFC} permission.
validateBlock(blockIndex);
checkConnected();
byte[] cmd = { 0x30, (byte) blockIndex };
return transceive(cmd, false);
|
public void | restore(int blockIndex)Copy from a value block to the temporary block.
This is an I/O operation and will block until complete. It must
not be called from the main application thread. A blocked call will be canceled with
{@link IOException} if {@link #close} is called from another thread.
Requires the {@link android.Manifest.permission#NFC} permission.
validateBlock(blockIndex);
checkConnected();
byte[] cmd = { (byte) 0xC2, (byte) blockIndex };
transceive(cmd, false);
|
public int | sectorToBlock(int sectorIndex)Return the first block of a given sector.
Does not cause any RF activity and does not block.
if (sectorIndex < 32) {
return sectorIndex * 4;
} else {
return 32 * 4 + (sectorIndex - 32) * 16;
}
|
public void | setTimeout(int timeout)Set the {@link #transceive} timeout in milliseconds.
The timeout only applies to {@link #transceive} on this object,
and is reset to a default value when {@link #close} is called.
Setting a longer timeout may be useful when performing
transactions that require a long processing time on the tag
such as key generation.
Requires the {@link android.Manifest.permission#NFC} permission.
try {
int err = mTag.getTagService().setTimeout(TagTechnology.MIFARE_CLASSIC, timeout);
if (err != ErrorCodes.SUCCESS) {
throw new IllegalArgumentException("The supplied timeout is not valid");
}
} catch (RemoteException e) {
Log.e(TAG, "NFC service dead", e);
}
|
public byte[] | transceive(byte[] data)Send raw NfcA data to a tag and receive the response.
This is equivalent to connecting to this tag via {@link NfcA}
and calling {@link NfcA#transceive}. Note that all MIFARE Classic
tags are based on {@link NfcA} technology.
Use {@link #getMaxTransceiveLength} to retrieve the maximum number of bytes
that can be sent with {@link #transceive}.
This is an I/O operation and will block until complete. It must
not be called from the main application thread. A blocked call will be canceled with
{@link IOException} if {@link #close} is called from another thread.
Requires the {@link android.Manifest.permission#NFC} permission.
return transceive(data, true);
|
public void | transfer(int blockIndex)Copy from the temporary block to a value block.
This is an I/O operation and will block until complete. It must
not be called from the main application thread. A blocked call will be canceled with
{@link IOException} if {@link #close} is called from another thread.
Requires the {@link android.Manifest.permission#NFC} permission.
validateBlock(blockIndex);
checkConnected();
byte[] cmd = { (byte) 0xB0, (byte) blockIndex };
transceive(cmd, false);
|
private static void | validateBlock(int block)
// Just looking for obvious out of bounds...
if (block < 0 || block >= MAX_BLOCK_COUNT) {
throw new IndexOutOfBoundsException("block out of bounds: " + block);
}
|
private static void | validateSector(int sector)
// Do not be too strict on upper bounds checking, since some cards
// have more addressable memory than they report. For example,
// MIFARE Plus 2k cards will appear as MIFARE Classic 1k cards when in
// MIFARE Classic compatibility mode.
// Note that issuing a command to an out-of-bounds block is safe - the
// tag should report error causing IOException. This validation is a
// helper to guard against obvious programming mistakes.
if (sector < 0 || sector >= MAX_SECTOR_COUNT) {
throw new IndexOutOfBoundsException("sector out of bounds: " + sector);
}
|
private static void | validateValueOperand(int value)
if (value < 0) {
throw new IllegalArgumentException("value operand negative");
}
|
public void | writeBlock(int blockIndex, byte[] data)Write 16-byte block.
This is an I/O operation and will block until complete. It must
not be called from the main application thread. A blocked call will be canceled with
{@link IOException} if {@link #close} is called from another thread.
Requires the {@link android.Manifest.permission#NFC} permission.
validateBlock(blockIndex);
checkConnected();
if (data.length != 16) {
throw new IllegalArgumentException("must write 16-bytes");
}
byte[] cmd = new byte[data.length + 2];
cmd[0] = (byte) 0xA0; // MF write command
cmd[1] = (byte) blockIndex;
System.arraycopy(data, 0, cmd, 2, data.length);
transceive(cmd, false);
|