Merge "Make MifareClassic methods more consistent." into gingerbread

This commit is contained in:
Nick Pelly
2011-01-23 21:36:14 -08:00
committed by Android (Google) Code Review
2 changed files with 246 additions and 287 deletions

View File

@@ -101246,7 +101246,7 @@
deprecated="not deprecated" deprecated="not deprecated"
visibility="public" visibility="public"
> >
<method name="authenticateBlock" <method name="authenticateSectorWithKeyA"
return="boolean" return="boolean"
abstract="false" abstract="false"
native="false" native="false"
@@ -101256,16 +101256,14 @@
deprecated="not deprecated" deprecated="not deprecated"
visibility="public" visibility="public"
> >
<parameter name="block" type="int"> <parameter name="sectorIndex" type="int">
</parameter> </parameter>
<parameter name="key" type="byte[]"> <parameter name="key" type="byte[]">
</parameter> </parameter>
<parameter name="keyA" type="boolean">
</parameter>
<exception name="IOException" type="java.io.IOException"> <exception name="IOException" type="java.io.IOException">
</exception> </exception>
</method> </method>
<method name="authenticateSector" <method name="authenticateSectorWithKeyB"
return="boolean" return="boolean"
abstract="false" abstract="false"
native="false" native="false"
@@ -101275,15 +101273,26 @@
deprecated="not deprecated" deprecated="not deprecated"
visibility="public" visibility="public"
> >
<parameter name="sector" type="int"> <parameter name="sectorIndex" type="int">
</parameter> </parameter>
<parameter name="key" type="byte[]"> <parameter name="key" type="byte[]">
</parameter> </parameter>
<parameter name="keyA" type="boolean">
</parameter>
<exception name="IOException" type="java.io.IOException"> <exception name="IOException" type="java.io.IOException">
</exception> </exception>
</method> </method>
<method name="blockToSector"
return="int"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="blockIndex" type="int">
</parameter>
</method>
<method name="decrement" <method name="decrement"
return="void" return="void"
abstract="false" abstract="false"
@@ -101294,7 +101303,7 @@
deprecated="not deprecated" deprecated="not deprecated"
visibility="public" visibility="public"
> >
<parameter name="block" type="int"> <parameter name="blockIndex" type="int">
</parameter> </parameter>
<exception name="IOException" type="java.io.IOException"> <exception name="IOException" type="java.io.IOException">
</exception> </exception>
@@ -101322,7 +101331,18 @@
deprecated="not deprecated" deprecated="not deprecated"
visibility="public" visibility="public"
> >
<parameter name="sector" type="int"> </method>
<method name="getBlockCountInSector"
return="int"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="sectorIndex" type="int">
</parameter> </parameter>
</method> </method>
<method name="getSectorCount" <method name="getSectorCount"
@@ -101336,19 +101356,6 @@
visibility="public" visibility="public"
> >
</method> </method>
<method name="getSectorSize"
return="int"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="sector" type="int">
</parameter>
</method>
<method name="getSize" <method name="getSize"
return="int" return="int"
abstract="false" abstract="false"
@@ -101360,17 +101367,6 @@
visibility="public" visibility="public"
> >
</method> </method>
<method name="getTotalBlockCount"
return="int"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="getType" <method name="getType"
return="int" return="int"
abstract="false" abstract="false"
@@ -101392,35 +101388,7 @@
deprecated="not deprecated" deprecated="not deprecated"
visibility="public" visibility="public"
> >
<parameter name="block" type="int"> <parameter name="blockIndex" type="int">
</parameter>
<exception name="IOException" type="java.io.IOException">
</exception>
</method>
<method name="isEmulated"
return="boolean"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="readBlock"
return="byte[]"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="sector" type="int">
</parameter>
<parameter name="block" type="int">
</parameter> </parameter>
<exception name="IOException" type="java.io.IOException"> <exception name="IOException" type="java.io.IOException">
</exception> </exception>
@@ -101435,7 +101403,7 @@
deprecated="not deprecated" deprecated="not deprecated"
visibility="public" visibility="public"
> >
<parameter name="block" type="int"> <parameter name="blockIndex" type="int">
</parameter> </parameter>
<exception name="IOException" type="java.io.IOException"> <exception name="IOException" type="java.io.IOException">
</exception> </exception>
@@ -101450,11 +101418,24 @@
deprecated="not deprecated" deprecated="not deprecated"
visibility="public" visibility="public"
> >
<parameter name="block" type="int"> <parameter name="blockIndex" type="int">
</parameter> </parameter>
<exception name="IOException" type="java.io.IOException"> <exception name="IOException" type="java.io.IOException">
</exception> </exception>
</method> </method>
<method name="sectorToBlock"
return="int"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="sectorIndex" type="int">
</parameter>
</method>
<method name="transceive" <method name="transceive"
return="byte[]" return="byte[]"
abstract="false" abstract="false"
@@ -101480,7 +101461,7 @@
deprecated="not deprecated" deprecated="not deprecated"
visibility="public" visibility="public"
> >
<parameter name="block" type="int"> <parameter name="blockIndex" type="int">
</parameter> </parameter>
<exception name="IOException" type="java.io.IOException"> <exception name="IOException" type="java.io.IOException">
</exception> </exception>
@@ -101495,32 +101476,24 @@
deprecated="not deprecated" deprecated="not deprecated"
visibility="public" visibility="public"
> >
<parameter name="block" type="int"> <parameter name="blockIndex" type="int">
</parameter> </parameter>
<parameter name="data" type="byte[]"> <parameter name="data" type="byte[]">
</parameter> </parameter>
<exception name="IOException" type="java.io.IOException"> <exception name="IOException" type="java.io.IOException">
</exception> </exception>
</method> </method>
<method name="writeBlock" <field name="BLOCK_SIZE"
return="void" type="int"
abstract="false" transient="false"
native="false" volatile="false"
synchronized="false" value="16"
static="false" static="true"
final="false" final="true"
deprecated="not deprecated" deprecated="not deprecated"
visibility="public" visibility="public"
> >
<parameter name="sector" type="int"> </field>
</parameter>
<parameter name="block" type="int">
</parameter>
<parameter name="data" type="byte[]">
</parameter>
<exception name="IOException" type="java.io.IOException">
</exception>
</method>
<field name="KEY_DEFAULT" <field name="KEY_DEFAULT"
type="byte[]" type="byte[]"
transient="false" transient="false"
@@ -101598,7 +101571,7 @@
visibility="public" visibility="public"
> >
</field> </field>
<field name="SIZE_UNKNOWN" <field name="TYPE_CLASSIC"
type="int" type="int"
transient="false" transient="false"
volatile="false" volatile="false"
@@ -101609,11 +101582,11 @@
visibility="public" visibility="public"
> >
</field> </field>
<field name="TYPE_CLASSIC" <field name="TYPE_OTHER"
type="int" type="int"
transient="false" transient="false"
volatile="false" volatile="false"
value="0" value="-1"
static="true" static="true"
final="true" final="true"
deprecated="not deprecated" deprecated="not deprecated"
@@ -101642,17 +101615,6 @@
visibility="public" visibility="public"
> >
</field> </field>
<field name="TYPE_UNKNOWN"
type="int"
transient="false"
volatile="false"
value="5"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
</class> </class>
<class name="MifareUltralight" <class name="MifareUltralight"
extends="android.nfc.tech.BasicTagTechnology" extends="android.nfc.tech.BasicTagTechnology"

View File

@@ -59,8 +59,8 @@ public final class MifareClassic extends BasicTagTechnology {
public static final int TYPE_PLUS = 1; public static final int TYPE_PLUS = 1;
/** A MIFARE Pro tag */ /** A MIFARE Pro tag */
public static final int TYPE_PRO = 2; public static final int TYPE_PRO = 2;
/** The tag type is unknown */ /** A Mifare Classic compatible card that does not match the other types */
public static final int TYPE_UNKNOWN = 5; public static final int TYPE_OTHER = -1;
/** The tag contains 16 sectors, each holding 4 blocks. */ /** The tag contains 16 sectors, each holding 4 blocks. */
public static final int SIZE_1K = 1024; public static final int SIZE_1K = 1024;
@@ -73,8 +73,12 @@ public final class MifareClassic extends BasicTagTechnology {
public static final int SIZE_4K = 4096; public static final int SIZE_4K = 4096;
/** The tag contains 5 sectors, each holding 4 blocks. */ /** The tag contains 5 sectors, each holding 4 blocks. */
public static final int SIZE_MINI = 320; public static final int SIZE_MINI = 320;
/** The capacity is unknown */
public static final int SIZE_UNKNOWN = 0; /** Size of a Mifare Classic block (in bytes) */
public static final int BLOCK_SIZE = 16;
private static final int MAX_BLOCK_COUNT = 256;
private static final int MAX_SECTOR_COUNT = 40;
private boolean mIsEmulated; private boolean mIsEmulated;
private int mType; private int mType;
@@ -99,102 +103,76 @@ public final class MifareClassic extends BasicTagTechnology {
public MifareClassic(Tag tag) throws RemoteException { public MifareClassic(Tag tag) throws RemoteException {
super(tag, TagTechnology.MIFARE_CLASSIC); super(tag, TagTechnology.MIFARE_CLASSIC);
// Check if this could actually be a MIFARE Classic NfcA a = NfcA.get(tag); // Mifare Classic is always based on NFC a
NfcA a = NfcA.get(tag);
mIsEmulated = false; mIsEmulated = false;
mType = TYPE_UNKNOWN;
mSize = SIZE_UNKNOWN;
switch (a.getSak()) { switch (a.getSak()) {
case 0x08: case 0x08:
// Type == classic
// Size = 1K
mType = TYPE_CLASSIC; mType = TYPE_CLASSIC;
mSize = SIZE_1K; mSize = SIZE_1K;
break; break;
case 0x09: case 0x09:
// Type == classic mini
// Size == ?
mType = TYPE_CLASSIC; mType = TYPE_CLASSIC;
mSize = SIZE_MINI; mSize = SIZE_MINI;
break; break;
case 0x10: case 0x10:
// Type == MF+
// Size == 2K
// SecLevel = SL2
mType = TYPE_PLUS; mType = TYPE_PLUS;
mSize = SIZE_2K; mSize = SIZE_2K;
// SecLevel = SL2
break; break;
case 0x11: case 0x11:
// Type == MF+
// Size == 4K
// Seclevel = SL2
mType = TYPE_PLUS; mType = TYPE_PLUS;
mSize = SIZE_4K; mSize = SIZE_4K;
// Seclevel = SL2
break; break;
case 0x18: case 0x18:
// Type == classic
// Size == 4k
mType = TYPE_CLASSIC; mType = TYPE_CLASSIC;
mSize = SIZE_4K; mSize = SIZE_4K;
break; 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: case 0x28:
// Type == MF Classic
// Size == 1K
// Emulated == true
mType = TYPE_CLASSIC; mType = TYPE_CLASSIC;
mSize = SIZE_1K; mSize = SIZE_1K;
mIsEmulated = true; mIsEmulated = true;
break; break;
case 0x38: case 0x38:
// Type == MF Classic
// Size == 4K
// Emulated == true
mType = TYPE_CLASSIC; mType = TYPE_CLASSIC;
mSize = SIZE_4K; mSize = SIZE_4K;
mIsEmulated = true; mIsEmulated = true;
break; break;
case 0x88: case 0x88:
// Type == MF Classic
// Size == 1K
// NXP-tag: false
mType = TYPE_CLASSIC; mType = TYPE_CLASSIC;
mSize = SIZE_1K; mSize = SIZE_1K;
// NXP-tag: false
break; break;
case 0x98: case 0x98:
case 0xB8: case 0xB8:
// Type == MF Pro
// Size == 4K
mType = TYPE_PRO; mType = TYPE_PRO;
mSize = SIZE_4K; mSize = SIZE_4K;
break; break;
default:
// Stack incorrectly reported a MifareClassic. We cannot handle this
// gracefully - we have no idea of the memory layout. Bail.
throw new RuntimeException(
"Tag incorrectly enumerated as Mifare Classic, SAK = " + a.getSak());
} }
} }
/** Returns the size of the tag, determined at discovery time */ /** Returns the type 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() { public int getType() {
return mType; return mType;
} }
/** Returns true if the tag is emulated, determined at discovery time */ /** Returns the size of the tag in bytes, determined at discovery time */
public int getSize() {
return mSize;
}
/** Returns 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.
* @hide
*/
public boolean isEmulated() { public boolean isEmulated() {
return mIsEmulated; return mIsEmulated;
} }
@@ -202,67 +180,78 @@ public final class MifareClassic extends BasicTagTechnology {
/** Returns the number of sectors on this tag, determined at discovery time */ /** Returns the number of sectors on this tag, determined at discovery time */
public int getSectorCount() { public int getSectorCount() {
switch (mSize) { switch (mSize) {
case SIZE_1K: { case SIZE_1K:
return 16; return 16;
} case SIZE_2K:
case SIZE_2K: {
return 32; return 32;
} case SIZE_4K:
case SIZE_4K: {
return 40; return 40;
} case SIZE_MINI:
case SIZE_MINI: {
return 5; return 5;
} default:
default: {
return 0; 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 */ /** Returns the total block count, determined at discovery time */
public int getTotalBlockCount() { public int getBlockCount() {
int totalBlocks = 0; return mSize / BLOCK_SIZE;
for (int sec = 0; sec < getSectorCount(); sec++) {
totalBlocks += getSectorSize(sec);
}
return totalBlocks;
} }
/** Returns the block count for the given sector, determined at discovery time */ /** Returns the block count for the given sector, determined at discovery time */
public int getBlockCount(int sector) { public int getBlockCountInSector(int sectorIndex) {
if (sector >= getSectorCount()) { validateSector(sectorIndex);
throw new IllegalArgumentException("this card only has " + getSectorCount() +
" sectors");
}
if (sector <= 32) { if (sectorIndex < 32) {
return 4; return 4;
} else { } else {
return 16; return 16;
} }
} }
private byte firstBlockInSector(int sector) { /** Return the sector index of a given block */
if (sector < 32) { public int blockToSector(int blockIndex) {
return (byte) ((sector * 4) & 0xff); validateBlock(blockIndex);
if (blockIndex < 32 * 4) {
return blockIndex / 4;
} else { } else {
return (byte) ((32 * 4 + ((sector - 32) * 16)) & 0xff); return 32 + (blockIndex - 32 * 4) / 16;
}
}
/** Return the first block of a given sector */
public int sectorToBlock(int sectorIndex) {
if (sectorIndex < 32) {
return sectorIndex * 4;
} else {
return 32 * 4 + (sectorIndex - 32) * 16;
} }
} }
// Methods that require connect() // Methods that require connect()
/** /**
* Authenticate the entire sector that the given block resides in. * Authenticate a sector.
* <p>Every sector has an A and B key with different access privileges,
* this method attempts to authenticate against the A key.
* <p>This requires a that the tag be connected. * <p>This requires a that the tag be connected.
*/ */
public boolean authenticateBlock(int block, byte[] key, boolean keyA) throws IOException { public boolean authenticateSectorWithKeyA(int sectorIndex, byte[] key) throws IOException {
return authenticate(sectorIndex, key, true);
}
/**
* Authenticate a sector.
* <p>Every sector has an A and B key with different access privileges,
* this method attempts to authenticate against the B key.
* <p>This requires a that the tag be connected.
*/
public boolean authenticateSectorWithKeyB(int sectorIndex, byte[] key) throws IOException {
return authenticate(sectorIndex, key, false);
}
private boolean authenticate(int sector, byte[] key, boolean keyA) throws IOException {
validateSector(sector);
checkConnected(); checkConnected();
byte[] cmd = new byte[12]; byte[] cmd = new byte[12];
@@ -275,7 +264,9 @@ public final class MifareClassic extends BasicTagTechnology {
} }
// Second byte is block address // Second byte is block address
cmd[1] = (byte) block; // 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 // Next 4 bytes are last 4 bytes of UID
byte[] uid = getTag().getId(); byte[] uid = getTag().getId();
@@ -285,7 +276,7 @@ public final class MifareClassic extends BasicTagTechnology {
System.arraycopy(key, 0, cmd, 6, 6); System.arraycopy(key, 0, cmd, 6, 6);
try { try {
if ((transceive(cmd, false) != null)) { if (transceive(cmd, false) != null) {
return true; return true;
} }
} catch (TagLostException e) { } catch (TagLostException e) {
@@ -297,106 +288,92 @@ public final class MifareClassic extends BasicTagTechnology {
} }
/** /**
* Authenticate for a given sector. * Read 16-byte block.
* <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. * <p>This requires a that the tag be connected.
* @throws IOException * @throws IOException
*/ */
public byte[] readBlock(int sector, int block) throws IOException { public byte[] readBlock(int blockIndex) throws IOException {
validateBlock(blockIndex);
checkConnected(); checkConnected();
byte addr = (byte) ((firstBlockInSector(sector) + block) & 0xff); byte[] cmd = { 0x30, (byte) blockIndex };
return readBlock(addr); return transceive(cmd, false);
} }
/** /**
* Reads absolute block index. * Write 16-byte block.
* <p>This requires a that the tag be connected. * <p>This requires a that the tag be connected.
* @throws IOException * @throws IOException
*/ */
public byte[] readBlock(int block) throws IOException { public void writeBlock(int blockIndex, byte[] data) throws IOException {
validateBlock(blockIndex);
checkConnected(); checkConnected();
if (data.length != 16) {
throw new IllegalArgumentException("must write 16-bytes");
}
byte addr = (byte) block; byte[] cmd = new byte[data.length + 2];
byte[] blockread_cmd = { 0x30, addr }; cmd[0] = (byte) 0xA0; // MF write command
cmd[1] = (byte) blockIndex;
System.arraycopy(data, 0, cmd, 2, data.length);
return transceive(blockread_cmd, false); transceive(cmd, false);
} }
/** /**
* Writes absolute block index. * Increment a value block, and store the result in temporary memory.
* <p>This requires a that the tag be connected. * @param block
* @throws IOException * @throws IOException
*/ */
public void writeBlock(int block, byte[] data) throws IOException { public void increment(int blockIndex) throws IOException {
validateBlock(blockIndex);
checkConnected(); checkConnected();
byte addr = (byte) block; byte[] cmd = { (byte) 0xC1, (byte) blockIndex };
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); transceive(cmd, false);
} }
/** /**
* Writes relative block in sector. * Decrement a value block, and store the result in temporary memory.
* <p>This requires a that the tag be connected. * @param block
* @throws IOException * @throws IOException
*/ */
public void writeBlock(int sector, int block, byte[] data) throws IOException { public void decrement(int blockIndex) throws IOException {
validateBlock(blockIndex);
checkConnected(); checkConnected();
byte addr = (byte) ((firstBlockInSector(sector) + block) & 0xff); byte[] cmd = { (byte) 0xC0, (byte) blockIndex };
writeBlock(addr, data); transceive(cmd, false);
} }
public void increment(int block) throws IOException { /**
* Copy from temporary memory to value block.
* @param block
* @throws IOException
*/
public void transfer(int blockIndex) throws IOException {
validateBlock(blockIndex);
checkConnected(); checkConnected();
byte[] incr_cmd = { (byte) 0xC1, (byte) block }; byte[] cmd = { (byte) 0xB0, (byte) blockIndex };
transceive(incr_cmd, false); transceive(cmd, false);
} }
public void decrement(int block) throws IOException { /**
* Copy from value block to temporary memory.
* @param block
* @throws IOException
*/
public void restore(int blockIndex) throws IOException {
validateBlock(blockIndex);
checkConnected(); checkConnected();
byte[] decr_cmd = { (byte) 0xC0, (byte) block }; byte[] cmd = { (byte) 0xC2, (byte) blockIndex };
transceive(decr_cmd, false); transceive(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);
} }
/** /**
@@ -414,4 +391,24 @@ public final class MifareClassic extends BasicTagTechnology {
public byte[] transceive(byte[] data) throws IOException { public byte[] transceive(byte[] data) throws IOException {
return transceive(data, true); return transceive(data, true);
} }
private 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 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);
}
}
} }