Loading api/current.xml +43 −81 Original line number Original line Diff line number Diff line Loading @@ -103266,7 +103266,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" Loading @@ -103276,16 +103276,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" Loading @@ -103295,17 +103293,15 @@ 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="decrement" <method name="blockToSector" return="void" return="int" abstract="false" abstract="false" native="false" native="false" synchronized="false" synchronized="false" Loading @@ -103314,38 +103310,38 @@ 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> </method> </method> <method name="get" <method name="decrement" return="android.nfc.tech.MifareClassic" return="void" abstract="false" abstract="false" native="false" native="false" synchronized="false" synchronized="false" static="true" static="false" final="false" final="false" deprecated="not deprecated" deprecated="not deprecated" visibility="public" visibility="public" > > <parameter name="tag" type="android.nfc.Tag"> <parameter name="blockIndex" type="int"> </parameter> </parameter> <exception name="IOException" type="java.io.IOException"> </exception> </method> </method> <method name="getBlockCount" <method name="get" return="int" return="android.nfc.tech.MifareClassic" abstract="false" abstract="false" native="false" native="false" synchronized="false" synchronized="false" static="false" static="true" final="false" final="false" deprecated="not deprecated" deprecated="not deprecated" visibility="public" visibility="public" > > <parameter name="sector" type="int"> <parameter name="tag" type="android.nfc.Tag"> </parameter> </parameter> </method> </method> <method name="getSectorCount" <method name="getBlockCount" return="int" return="int" abstract="false" abstract="false" native="false" native="false" Loading @@ -103356,7 +103352,7 @@ visibility="public" visibility="public" > > </method> </method> <method name="getSectorSize" <method name="getBlockCountInSector" return="int" return="int" abstract="false" abstract="false" native="false" native="false" Loading @@ -103366,10 +103362,10 @@ deprecated="not deprecated" deprecated="not deprecated" visibility="public" visibility="public" > > <parameter name="sector" type="int"> <parameter name="sectorIndex" type="int"> </parameter> </parameter> </method> </method> <method name="getSize" <method name="getSectorCount" return="int" return="int" abstract="false" abstract="false" native="false" native="false" Loading @@ -103380,7 +103376,7 @@ visibility="public" visibility="public" > > </method> </method> <method name="getTotalBlockCount" <method name="getSize" return="int" return="int" abstract="false" abstract="false" native="false" native="false" Loading Loading @@ -103412,22 +103408,11 @@ 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="isEmulated" return="boolean" abstract="false" native="false" synchronized="false" static="false" final="false" deprecated="not deprecated" visibility="public" > </method> <method name="readBlock" <method name="readBlock" return="byte[]" return="byte[]" abstract="false" abstract="false" Loading @@ -103438,15 +103423,13 @@ deprecated="not deprecated" deprecated="not deprecated" visibility="public" visibility="public" > > <parameter name="sector" type="int"> <parameter name="blockIndex" 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> </method> </method> <method name="readBlock" <method name="restore" return="byte[]" return="void" abstract="false" abstract="false" native="false" native="false" synchronized="false" synchronized="false" Loading @@ -103455,13 +103438,13 @@ 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="restore" <method name="sectorToBlock" return="void" return="int" abstract="false" abstract="false" native="false" native="false" synchronized="false" synchronized="false" Loading @@ -103470,10 +103453,8 @@ deprecated="not deprecated" deprecated="not deprecated" visibility="public" visibility="public" > > <parameter name="block" type="int"> <parameter name="sectorIndex" type="int"> </parameter> </parameter> <exception name="IOException" type="java.io.IOException"> </exception> </method> </method> <method name="transceive" <method name="transceive" return="byte[]" return="byte[]" Loading @@ -103500,7 +103481,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> Loading @@ -103515,32 +103496,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" Loading Loading @@ -103618,7 +103591,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" Loading @@ -103629,11 +103602,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" Loading Loading @@ -103662,17 +103635,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" core/java/android/nfc/tech/MifareClassic.java +186 −189 Original line number Original line Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; } } Loading @@ -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]; Loading @@ -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(); Loading @@ -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) { Loading @@ -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. * <p>This requires a that the tag be connected. * @throws IOException */ */ public boolean authenticateSector(int sector, byte[] key, boolean keyA) throws IOException { public byte[] readBlock(int blockIndex) throws IOException { validateBlock(blockIndex); checkConnected(); checkConnected(); byte addr = (byte) ((firstBlockInSector(sector)) & 0xff); byte[] cmd = { 0x30, (byte) blockIndex }; return transceive(cmd, false); // Note that authenticating a block of a sector, will authenticate // the entire sector. return authenticateBlock(addr, key, keyA); } } /** /** * Sector indexing starts at 0. * Write 16-byte block. * 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 void writeBlock(int blockIndex, byte[] data) throws IOException { validateBlock(blockIndex); checkConnected(); 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); byte addr = (byte) ((firstBlockInSector(sector) + block) & 0xff); transceive(cmd, false); return readBlock(addr); } } /** /** * Reads 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 byte[] readBlock(int block) throws IOException { public void increment(int blockIndex) throws IOException { validateBlock(blockIndex); checkConnected(); checkConnected(); byte addr = (byte) block; byte[] cmd = { (byte) 0xC1, (byte) blockIndex }; byte[] blockread_cmd = { 0x30, addr }; return transceive(blockread_cmd, false); transceive(cmd, false); } } /** /** * Writes absolute block index. * 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 block, byte[] data) throws IOException { public void decrement(int blockIndex) throws IOException { validateBlock(blockIndex); checkConnected(); checkConnected(); byte addr = (byte) block; byte[] cmd = { (byte) 0xC0, (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. * Copy from temporary memory to value block. * <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 transfer(int blockIndex) throws IOException { validateBlock(blockIndex); checkConnected(); checkConnected(); byte addr = (byte) ((firstBlockInSector(sector) + block) & 0xff); byte[] cmd = { (byte) 0xB0, (byte) blockIndex }; writeBlock(addr, data); transceive(cmd, false); } } public void increment(int block) throws IOException { /** checkConnected(); * Copy from value block to temporary memory. * @param block byte[] incr_cmd = { (byte) 0xC1, (byte) block }; * @throws IOException */ transceive(incr_cmd, false); public void restore(int blockIndex) throws IOException { } validateBlock(blockIndex); 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(); checkConnected(); byte[] rest_cmd = { (byte) 0xC2, (byte) block }; byte[] cmd = { (byte) 0xC2, (byte) blockIndex }; transceive(rest_cmd, false); transceive(cmd, false); } } /** /** Loading @@ -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); } } } } Loading
api/current.xml +43 −81 Original line number Original line Diff line number Diff line Loading @@ -103266,7 +103266,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" Loading @@ -103276,16 +103276,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" Loading @@ -103295,17 +103293,15 @@ 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="decrement" <method name="blockToSector" return="void" return="int" abstract="false" abstract="false" native="false" native="false" synchronized="false" synchronized="false" Loading @@ -103314,38 +103310,38 @@ 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> </method> </method> <method name="get" <method name="decrement" return="android.nfc.tech.MifareClassic" return="void" abstract="false" abstract="false" native="false" native="false" synchronized="false" synchronized="false" static="true" static="false" final="false" final="false" deprecated="not deprecated" deprecated="not deprecated" visibility="public" visibility="public" > > <parameter name="tag" type="android.nfc.Tag"> <parameter name="blockIndex" type="int"> </parameter> </parameter> <exception name="IOException" type="java.io.IOException"> </exception> </method> </method> <method name="getBlockCount" <method name="get" return="int" return="android.nfc.tech.MifareClassic" abstract="false" abstract="false" native="false" native="false" synchronized="false" synchronized="false" static="false" static="true" final="false" final="false" deprecated="not deprecated" deprecated="not deprecated" visibility="public" visibility="public" > > <parameter name="sector" type="int"> <parameter name="tag" type="android.nfc.Tag"> </parameter> </parameter> </method> </method> <method name="getSectorCount" <method name="getBlockCount" return="int" return="int" abstract="false" abstract="false" native="false" native="false" Loading @@ -103356,7 +103352,7 @@ visibility="public" visibility="public" > > </method> </method> <method name="getSectorSize" <method name="getBlockCountInSector" return="int" return="int" abstract="false" abstract="false" native="false" native="false" Loading @@ -103366,10 +103362,10 @@ deprecated="not deprecated" deprecated="not deprecated" visibility="public" visibility="public" > > <parameter name="sector" type="int"> <parameter name="sectorIndex" type="int"> </parameter> </parameter> </method> </method> <method name="getSize" <method name="getSectorCount" return="int" return="int" abstract="false" abstract="false" native="false" native="false" Loading @@ -103380,7 +103376,7 @@ visibility="public" visibility="public" > > </method> </method> <method name="getTotalBlockCount" <method name="getSize" return="int" return="int" abstract="false" abstract="false" native="false" native="false" Loading Loading @@ -103412,22 +103408,11 @@ 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="isEmulated" return="boolean" abstract="false" native="false" synchronized="false" static="false" final="false" deprecated="not deprecated" visibility="public" > </method> <method name="readBlock" <method name="readBlock" return="byte[]" return="byte[]" abstract="false" abstract="false" Loading @@ -103438,15 +103423,13 @@ deprecated="not deprecated" deprecated="not deprecated" visibility="public" visibility="public" > > <parameter name="sector" type="int"> <parameter name="blockIndex" 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> </method> </method> <method name="readBlock" <method name="restore" return="byte[]" return="void" abstract="false" abstract="false" native="false" native="false" synchronized="false" synchronized="false" Loading @@ -103455,13 +103438,13 @@ 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="restore" <method name="sectorToBlock" return="void" return="int" abstract="false" abstract="false" native="false" native="false" synchronized="false" synchronized="false" Loading @@ -103470,10 +103453,8 @@ deprecated="not deprecated" deprecated="not deprecated" visibility="public" visibility="public" > > <parameter name="block" type="int"> <parameter name="sectorIndex" type="int"> </parameter> </parameter> <exception name="IOException" type="java.io.IOException"> </exception> </method> </method> <method name="transceive" <method name="transceive" return="byte[]" return="byte[]" Loading @@ -103500,7 +103481,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> Loading @@ -103515,32 +103496,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" Loading Loading @@ -103618,7 +103591,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" Loading @@ -103629,11 +103602,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" Loading Loading @@ -103662,17 +103635,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"
core/java/android/nfc/tech/MifareClassic.java +186 −189 Original line number Original line Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; } } Loading @@ -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]; Loading @@ -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(); Loading @@ -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) { Loading @@ -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. * <p>This requires a that the tag be connected. * @throws IOException */ */ public boolean authenticateSector(int sector, byte[] key, boolean keyA) throws IOException { public byte[] readBlock(int blockIndex) throws IOException { validateBlock(blockIndex); checkConnected(); checkConnected(); byte addr = (byte) ((firstBlockInSector(sector)) & 0xff); byte[] cmd = { 0x30, (byte) blockIndex }; return transceive(cmd, false); // Note that authenticating a block of a sector, will authenticate // the entire sector. return authenticateBlock(addr, key, keyA); } } /** /** * Sector indexing starts at 0. * Write 16-byte block. * 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 void writeBlock(int blockIndex, byte[] data) throws IOException { validateBlock(blockIndex); checkConnected(); 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); byte addr = (byte) ((firstBlockInSector(sector) + block) & 0xff); transceive(cmd, false); return readBlock(addr); } } /** /** * Reads 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 byte[] readBlock(int block) throws IOException { public void increment(int blockIndex) throws IOException { validateBlock(blockIndex); checkConnected(); checkConnected(); byte addr = (byte) block; byte[] cmd = { (byte) 0xC1, (byte) blockIndex }; byte[] blockread_cmd = { 0x30, addr }; return transceive(blockread_cmd, false); transceive(cmd, false); } } /** /** * Writes absolute block index. * 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 block, byte[] data) throws IOException { public void decrement(int blockIndex) throws IOException { validateBlock(blockIndex); checkConnected(); checkConnected(); byte addr = (byte) block; byte[] cmd = { (byte) 0xC0, (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. * Copy from temporary memory to value block. * <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 transfer(int blockIndex) throws IOException { validateBlock(blockIndex); checkConnected(); checkConnected(); byte addr = (byte) ((firstBlockInSector(sector) + block) & 0xff); byte[] cmd = { (byte) 0xB0, (byte) blockIndex }; writeBlock(addr, data); transceive(cmd, false); } } public void increment(int block) throws IOException { /** checkConnected(); * Copy from value block to temporary memory. * @param block byte[] incr_cmd = { (byte) 0xC1, (byte) block }; * @throws IOException */ transceive(incr_cmd, false); public void restore(int blockIndex) throws IOException { } validateBlock(blockIndex); 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(); checkConnected(); byte[] rest_cmd = { (byte) 0xC2, (byte) block }; byte[] cmd = { (byte) 0xC2, (byte) blockIndex }; transceive(rest_cmd, false); transceive(cmd, false); } } /** /** Loading @@ -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); } } } }