2 * Copyright (C) 2010 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package android.nfc.tech;
19 import android.nfc.Tag;
20 import android.nfc.TagLostException;
21 import android.os.RemoteException;
23 import java.io.IOException;
24 import java.nio.ByteBuffer;
25 import java.nio.ByteOrder;
28 * Technology class representing MIFARE Classic tags (also known as MIFARE Standard).
30 * <p>Support for this technology type is optional. If the NFC stack doesn't support this technology
31 * MIFARE Classic tags will still be scanned, but will only show the NfcA technology.
33 * <p>MIFARE Classic tags have sectors that each contain blocks. The block size is constant at
34 * 16 bytes, but the number of sectors and the sector size varies by product. MIFARE has encryption
35 * built in and each sector has two keys associated with it, as well as ACLs to determine what
36 * level acess each key grants. Before operating on a sector you must call either
37 * {@link #authenticateSectorWithKeyA(int, byte[])} or
38 * {@link #authenticateSectorWithKeyB(int, byte[])} to gain authorization for your request.
40 public final class MifareClassic extends BasicTagTechnology {
42 * The well-known default MIFARE read key. All keys are set to this at the factory.
43 * Using this key will effectively make the payload in the sector public.
45 public static final byte[] KEY_DEFAULT =
46 {(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF};
48 * The well-known, default MIFARE Application Directory read key.
50 public static final byte[] KEY_MIFARE_APPLICATION_DIRECTORY =
51 {(byte)0xA0,(byte)0xA1,(byte)0xA2,(byte)0xA3,(byte)0xA4,(byte)0xA5};
53 * The well-known, default read key for NDEF data on a MIFARE Classic
55 public static final byte[] KEY_NFC_FORUM =
56 {(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7};
58 /** A Mifare Classic compatible card of unknown type */
59 public static final int TYPE_UNKNOWN = -1;
60 /** A MIFARE Classic tag */
61 public static final int TYPE_CLASSIC = 0;
62 /** A MIFARE Plus tag */
63 public static final int TYPE_PLUS = 1;
64 /** A MIFARE Pro tag */
65 public static final int TYPE_PRO = 2;
67 /** The tag contains 16 sectors, each holding 4 blocks. */
68 public static final int SIZE_1K = 1024;
69 /** The tag contains 32 sectors, each holding 4 blocks. */
70 public static final int SIZE_2K = 2048;
72 * The tag contains 40 sectors. The first 32 sectors contain 4 blocks and the last 8 sectors
75 public static final int SIZE_4K = 4096;
76 /** The tag contains 5 sectors, each holding 4 blocks. */
77 public static final int SIZE_MINI = 320;
79 /** Size of a Mifare Classic block (in bytes) */
80 public static final int BLOCK_SIZE = 16;
82 private static final int MAX_BLOCK_COUNT = 256;
83 private static final int MAX_SECTOR_COUNT = 40;
85 private boolean mIsEmulated;
90 * Returns an instance of this tech for the given tag. If the tag doesn't support
91 * this tech type null is returned.
93 * @param tag The tag to get the tech from
95 public static MifareClassic get(Tag tag) {
96 if (!tag.hasTech(TagTechnology.MIFARE_CLASSIC)) return null;
98 return new MifareClassic(tag);
99 } catch (RemoteException e) {
105 public MifareClassic(Tag tag) throws RemoteException {
106 super(tag, TagTechnology.MIFARE_CLASSIC);
108 NfcA a = NfcA.get(tag); // Mifare Classic is always based on NFC a
112 switch (a.getSak()) {
114 mType = TYPE_CLASSIC;
118 mType = TYPE_CLASSIC;
132 mType = TYPE_CLASSIC;
136 mType = TYPE_CLASSIC;
141 mType = TYPE_CLASSIC;
146 mType = TYPE_CLASSIC;
156 // Stack incorrectly reported a MifareClassic. We cannot handle this
157 // gracefully - we have no idea of the memory layout. Bail.
158 throw new RuntimeException(
159 "Tag incorrectly enumerated as Mifare Classic, SAK = " + a.getSak());
163 /** Returns the type of the tag, determined at discovery time */
164 public int getType() {
168 /** Returns the size of the tag in bytes, determined at discovery time */
169 public int getSize() {
173 /** Returns true if the tag is emulated, determined at discovery time.
174 * These are actually smart-cards that emulate a Mifare Classic interface.
175 * They can be treated identically to a Mifare Classic tag.
178 public boolean isEmulated() {
182 /** Returns the number of sectors on this tag, determined at discovery time */
183 public int getSectorCount() {
198 /** Returns the total block count, determined at discovery time */
199 public int getBlockCount() {
200 return mSize / BLOCK_SIZE;
203 /** Returns the block count for the given sector, determined at discovery time */
204 public int getBlockCountInSector(int sectorIndex) {
205 validateSector(sectorIndex);
207 if (sectorIndex < 32) {
214 /** Return the sector index of a given block */
215 public int blockToSector(int blockIndex) {
216 validateBlock(blockIndex);
218 if (blockIndex < 32 * 4) {
219 return blockIndex / 4;
221 return 32 + (blockIndex - 32 * 4) / 16;
225 /** Return the first block of a given sector */
226 public int sectorToBlock(int sectorIndex) {
227 if (sectorIndex < 32) {
228 return sectorIndex * 4;
230 return 32 * 4 + (sectorIndex - 32) * 16;
234 // Methods that require connect()
236 * Authenticate a sector.
237 * <p>Every sector has an A and B key with different access privileges,
238 * this method attempts to authenticate against the A key.
239 * <p>This requires a that the tag be connected.
241 public boolean authenticateSectorWithKeyA(int sectorIndex, byte[] key) throws IOException {
242 return authenticate(sectorIndex, key, true);
246 * Authenticate a sector.
247 * <p>Every sector has an A and B key with different access privileges,
248 * this method attempts to authenticate against the B key.
249 * <p>This requires a that the tag be connected.
251 public boolean authenticateSectorWithKeyB(int sectorIndex, byte[] key) throws IOException {
252 return authenticate(sectorIndex, key, false);
255 private boolean authenticate(int sector, byte[] key, boolean keyA) throws IOException {
256 validateSector(sector);
259 byte[] cmd = new byte[12];
261 // First byte is the command
263 cmd[0] = 0x60; // phHal_eMifareAuthentA
265 cmd[0] = 0x61; // phHal_eMifareAuthentB
268 // Second byte is block address
269 // Authenticate command takes a block address. Authenticating a block
270 // of a sector will authenticate the entire sector.
271 cmd[1] = (byte) sectorToBlock(sector);
273 // Next 4 bytes are last 4 bytes of UID
274 byte[] uid = getTag().getId();
275 System.arraycopy(uid, uid.length - 4, cmd, 2, 4);
277 // Next 6 bytes are key
278 System.arraycopy(key, 0, cmd, 6, 6);
281 if (transceive(cmd, false) != null) {
284 } catch (TagLostException e) {
286 } catch (IOException e) {
287 // No need to deal with, will return false anyway
293 * Read 16-byte block.
294 * <p>This requires a that the tag be connected.
295 * @throws IOException
297 public byte[] readBlock(int blockIndex) throws IOException {
298 validateBlock(blockIndex);
301 byte[] cmd = { 0x30, (byte) blockIndex };
302 return transceive(cmd, false);
306 * Write 16-byte block.
307 * <p>This requires a that the tag be connected.
308 * @throws IOException
310 public void writeBlock(int blockIndex, byte[] data) throws IOException {
311 validateBlock(blockIndex);
313 if (data.length != 16) {
314 throw new IllegalArgumentException("must write 16-bytes");
317 byte[] cmd = new byte[data.length + 2];
318 cmd[0] = (byte) 0xA0; // MF write command
319 cmd[1] = (byte) blockIndex;
320 System.arraycopy(data, 0, cmd, 2, data.length);
322 transceive(cmd, false);
326 * Increment a value block, and store the result in temporary memory.
328 * @throws IOException
330 public void increment(int blockIndex, int value) throws IOException {
331 validateBlock(blockIndex);
332 validateValueOperand(value);
335 ByteBuffer cmd = ByteBuffer.allocate(6);
336 cmd.order(ByteOrder.LITTLE_ENDIAN);
337 cmd.put( (byte) 0xC1 );
338 cmd.put( (byte) blockIndex );
341 transceive(cmd.array(), false);
345 * Decrement a value block, and store the result in temporary memory.
347 * @throws IOException
349 public void decrement(int blockIndex, int value) throws IOException {
350 validateBlock(blockIndex);
351 validateValueOperand(value);
354 ByteBuffer cmd = ByteBuffer.allocate(6);
355 cmd.order(ByteOrder.LITTLE_ENDIAN);
356 cmd.put( (byte) 0xC0 );
357 cmd.put( (byte) blockIndex );
360 transceive(cmd.array(), false);
364 * Copy from temporary memory to value block.
366 * @throws IOException
368 public void transfer(int blockIndex) throws IOException {
369 validateBlock(blockIndex);
372 byte[] cmd = { (byte) 0xB0, (byte) blockIndex };
374 transceive(cmd, false);
378 * Copy from value block to temporary memory.
380 * @throws IOException
382 public void restore(int blockIndex) throws IOException {
383 validateBlock(blockIndex);
386 byte[] cmd = { (byte) 0xC2, (byte) blockIndex };
388 transceive(cmd, false);
392 * Send raw NfcA data to a tag and receive the response.
394 * This method will block until the response is received. It can be canceled
395 * with {@link #close}.
396 * <p>Requires {@link android.Manifest.permission#NFC} permission.
397 * <p>This requires a that the tag be connected.
399 * @param data bytes to send
400 * @return bytes received in response
401 * @throws IOException if the target is lost or connection closed
403 public byte[] transceive(byte[] data) throws IOException {
404 return transceive(data, true);
407 private static void validateSector(int sector) {
408 // Do not be too strict on upper bounds checking, since some cards
409 // have more addressable memory than they report. For example,
410 // Mifare Plus 2k cards will appear as Mifare Classic 1k cards when in
411 // Mifare Classic compatibility mode.
412 // Note that issuing a command to an out-of-bounds block is safe - the
413 // tag should report error causing IOException. This validation is a
414 // helper to guard against obvious programming mistakes.
415 if (sector < 0 || sector >= MAX_SECTOR_COUNT) {
416 throw new IndexOutOfBoundsException("sector out of bounds: " + sector);
420 private static void validateBlock(int block) {
421 // Just looking for obvious out of bounds...
422 if (block < 0 || block >= MAX_BLOCK_COUNT) {
423 throw new IndexOutOfBoundsException("block out of bounds: " + block);
427 private static void validateValueOperand(int value) {
429 throw new IllegalArgumentException("value operand negative");