blob: 8c92288d00479c2a5a1f62e3aa3ddae5d452d947 [file] [log] [blame]
Jeff Hamilton6be655c2010-11-12 12:28:16 -06001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
Jeff Hamilton4e21e1d2011-01-21 01:13:06 -060017package android.nfc.tech;
Jeff Hamilton6be655c2010-11-12 12:28:16 -060018
Martijn Coenen112fdf62011-06-09 16:57:49 +020019import android.nfc.ErrorCodes;
Jeff Hamilton6be655c2010-11-12 12:28:16 -060020import android.nfc.Tag;
Jeff Hamilton4e21e1d2011-01-21 01:13:06 -060021import android.nfc.TagLostException;
Jeff Hamilton6be655c2010-11-12 12:28:16 -060022import android.os.RemoteException;
Martijn Coenen112fdf62011-06-09 16:57:49 +020023import android.util.Log;
Jeff Hamilton6be655c2010-11-12 12:28:16 -060024
25import java.io.IOException;
Nick Pelly1e233af2011-01-23 22:11:35 -080026import java.nio.ByteBuffer;
Nick Pellyb1342232011-01-25 07:45:07 -080027import java.nio.ByteOrder;
Jeff Hamilton6be655c2010-11-12 12:28:16 -060028
29/**
Nick Pelly74fe6c62011-02-02 22:37:40 -080030 * Provides access to MIFARE Classic properties and I/O operations on a {@link Tag}.
Jeff Hamilton6be655c2010-11-12 12:28:16 -060031 *
Nick Pelly74fe6c62011-02-02 22:37:40 -080032 * <p>Acquire a {@link MifareClassic} object using {@link #get}.
Jeff Hamilton6be655c2010-11-12 12:28:16 -060033 *
Nick Pelly74fe6c62011-02-02 22:37:40 -080034 * <p>MIFARE Classic is also known as MIFARE Standard.
35 * <p>MIFARE Classic tags are divided into sectors, and each sector is sub-divided into
36 * blocks. Block size is always 16 bytes ({@link #BLOCK_SIZE}. Sector size varies.
37 * <ul>
38 * <li>MIFARE Classic Mini are 320 bytes ({@link #SIZE_MINI}), with 5 sectors each of 4 blocks.
39 * <li>MIFARE Classic 1k are 1024 bytes ({@link #SIZE_1K}), with 16 sectors each of 4 blocks.
40 * <li>MIFARE Classic 2k are 2048 bytes ({@link #SIZE_2K}), with 32 sectors each of 4 blocks.
41 * <li>MIFARE Classic 4k} are 4096 bytes ({@link #SIZE_4K}). The first 32 sectors contain 4 blocks
42 * and the last 8 sectors contain 16 blocks.
43 * </ul>
44 *
45 * <p>MIFARE Classic tags require authentication on a per-sector basis before any
46 * other I/O operations on that sector can be performed. There are two keys per sector,
47 * and ACL bits determine what I/O operations are allowed on that sector after
48 * authenticating with a key. {@see #authenticateSectorWithKeyA} and
49 * {@see #authenticateSectorWithKeyB}.
50 *
51 * <p>Three well-known authentication keys are defined in this class:
52 * {@link #KEY_DEFAULT}, {@link #KEY_MIFARE_APPLICATION_DIRECTORY},
53 * {@link #KEY_NFC_FORUM}.
54 * <ul>
55 * <li>{@link #KEY_DEFAULT} is the default factory key for MIFARE Classic.
56 * <li>{@link #KEY_MIFARE_APPLICATION_DIRECTORY} is the well-known key for
57 * MIFARE Classic cards that have been formatted according to the
58 * MIFARE Application Directory (MAD) specification.
59 * <li>{@link #KEY_NFC_FORUM} is the well-known key for MIFARE Classic cards that
Nick Pelly39cf3a42011-02-07 17:04:21 +090060 * have been formatted according to the NXP specification for NDEF on MIFARE Classic.
Nick Pelly74fe6c62011-02-02 22:37:40 -080061 *
62 * <p>Implementation of this class on a Android NFC device is optional.
63 * If it is not implemented, then
64 * {@link MifareClassic} will never be enumerated in {@link Tag#getTechList}.
65 * If it is enumerated, then all {@link MifareClassic} I/O operations will be supported,
66 * and {@link Ndef#MIFARE_CLASSIC} NDEF tags will also be supported. In either case,
67 * {@link NfcA} will also be enumerated on the tag, because all MIFARE Classic tags are also
68 * {@link NfcA}.
Nick Pelly39cf3a42011-02-07 17:04:21 +090069 *
70 * <p class="note"><strong>Note:</strong> Methods that perform I/O operations
71 * require the {@link android.Manifest.permission#NFC} permission.
Jeff Hamilton6be655c2010-11-12 12:28:16 -060072 */
73public final class MifareClassic extends BasicTagTechnology {
Martijn Coenen112fdf62011-06-09 16:57:49 +020074 private static final String TAG = "NFC";
75
Jeff Hamilton6be655c2010-11-12 12:28:16 -060076 /**
Nick Pelly74fe6c62011-02-02 22:37:40 -080077 * The default factory key.
Jeff Hamilton6be655c2010-11-12 12:28:16 -060078 */
Jan Brands65c3f982010-12-06 21:05:52 +010079 public static final byte[] KEY_DEFAULT =
Jeff Hamilton6be655c2010-11-12 12:28:16 -060080 {(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF};
Jeff Hamilton6be655c2010-11-12 12:28:16 -060081 /**
Nick Pelly74fe6c62011-02-02 22:37:40 -080082 * The well-known key for tags formatted according to the
83 * MIFARE Application Directory (MAD) specification.
Jeff Hamilton6be655c2010-11-12 12:28:16 -060084 */
Jan Brands65c3f982010-12-06 21:05:52 +010085 public static final byte[] KEY_MIFARE_APPLICATION_DIRECTORY =
Jeff Hamilton6be655c2010-11-12 12:28:16 -060086 {(byte)0xA0,(byte)0xA1,(byte)0xA2,(byte)0xA3,(byte)0xA4,(byte)0xA5};
87 /**
Nick Pelly74fe6c62011-02-02 22:37:40 -080088 * The well-known key for tags formatted according to the
Jeff Hamilton734e9b02011-05-25 17:37:51 -050089 * NDEF on MIFARE Classic specification.
Jeff Hamilton6be655c2010-11-12 12:28:16 -060090 */
Jan Brands65c3f982010-12-06 21:05:52 +010091 public static final byte[] KEY_NFC_FORUM =
Jeff Hamilton6be655c2010-11-12 12:28:16 -060092 {(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7};
93
Jeff Hamilton734e9b02011-05-25 17:37:51 -050094 /** A MIFARE Classic compatible card of unknown type */
Nick Pelly4a5e2532011-01-27 10:04:04 -080095 public static final int TYPE_UNKNOWN = -1;
Jeff Hamiltonce3224c2011-01-17 11:05:03 -080096 /** A MIFARE Classic tag */
Jeff Hamilton6be655c2010-11-12 12:28:16 -060097 public static final int TYPE_CLASSIC = 0;
Jeff Hamiltonce3224c2011-01-17 11:05:03 -080098 /** A MIFARE Plus tag */
Jeff Hamilton6be655c2010-11-12 12:28:16 -060099 public static final int TYPE_PLUS = 1;
Jeff Hamiltonce3224c2011-01-17 11:05:03 -0800100 /** A MIFARE Pro tag */
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600101 public static final int TYPE_PRO = 2;
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600102
Nick Pelly74fe6c62011-02-02 22:37:40 -0800103 /** Tag contains 16 sectors, each with 4 blocks. */
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600104 public static final int SIZE_1K = 1024;
Nick Pelly74fe6c62011-02-02 22:37:40 -0800105 /** Tag contains 32 sectors, each with 4 blocks. */
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600106 public static final int SIZE_2K = 2048;
Jeff Hamiltonce3224c2011-01-17 11:05:03 -0800107 /**
Nick Pelly74fe6c62011-02-02 22:37:40 -0800108 * Tag contains 40 sectors. The first 32 sectors contain 4 blocks and the last 8 sectors
Jeff Hamiltonce3224c2011-01-17 11:05:03 -0800109 * contain 16 blocks.
110 */
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600111 public static final int SIZE_4K = 4096;
Nick Pelly74fe6c62011-02-02 22:37:40 -0800112 /** Tag contains 5 sectors, each with 4 blocks. */
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600113 public static final int SIZE_MINI = 320;
Nick Pellye45083b2011-01-21 22:11:29 -0800114
Nick Pelly74fe6c62011-02-02 22:37:40 -0800115 /** Size of a MIFARE Classic block (in bytes) */
Nick Pellye45083b2011-01-21 22:11:29 -0800116 public static final int BLOCK_SIZE = 16;
117
118 private static final int MAX_BLOCK_COUNT = 256;
119 private static final int MAX_SECTOR_COUNT = 40;
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600120
121 private boolean mIsEmulated;
122 private int mType;
123 private int mSize;
124
Jeff Hamilton4e21e1d2011-01-21 01:13:06 -0600125 /**
Nick Pelly74fe6c62011-02-02 22:37:40 -0800126 * Get an instance of {@link MifareClassic} for the given tag.
127 * <p>Does not cause any RF activity and does not block.
128 * <p>Returns null if {@link MifareClassic} was not enumerated in {@link Tag#getTechList}.
129 * This indicates the tag is not MIFARE Classic compatible, or this Android
130 * device does not support MIFARE Classic.
Jeff Hamilton4e21e1d2011-01-21 01:13:06 -0600131 *
Nick Pelly74fe6c62011-02-02 22:37:40 -0800132 * @param tag an MIFARE Classic compatible tag
133 * @return MIFARE Classic object
Jeff Hamilton4e21e1d2011-01-21 01:13:06 -0600134 */
135 public static MifareClassic get(Tag tag) {
136 if (!tag.hasTech(TagTechnology.MIFARE_CLASSIC)) return null;
137 try {
138 return new MifareClassic(tag);
139 } catch (RemoteException e) {
140 return null;
141 }
142 }
143
Jeff Hamiltonce3224c2011-01-17 11:05:03 -0800144 /** @hide */
Jeff Hamilton4e21e1d2011-01-21 01:13:06 -0600145 public MifareClassic(Tag tag) throws RemoteException {
146 super(tag, TagTechnology.MIFARE_CLASSIC);
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600147
Jeff Hamilton734e9b02011-05-25 17:37:51 -0500148 NfcA a = NfcA.get(tag); // MIFARE Classic is always based on NFC a
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600149
150 mIsEmulated = false;
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600151
152 switch (a.getSak()) {
Sunil Jogi72677c92011-11-23 15:03:16 -0800153 case 0x01:
Nick Pellye45083b2011-01-21 22:11:29 -0800154 case 0x08:
155 mType = TYPE_CLASSIC;
156 mSize = SIZE_1K;
157 break;
158 case 0x09:
159 mType = TYPE_CLASSIC;
160 mSize = SIZE_MINI;
161 break;
162 case 0x10:
163 mType = TYPE_PLUS;
164 mSize = SIZE_2K;
165 // SecLevel = SL2
166 break;
167 case 0x11:
168 mType = TYPE_PLUS;
169 mSize = SIZE_4K;
170 // Seclevel = SL2
171 break;
172 case 0x18:
173 mType = TYPE_CLASSIC;
174 mSize = SIZE_4K;
175 break;
176 case 0x28:
177 mType = TYPE_CLASSIC;
178 mSize = SIZE_1K;
179 mIsEmulated = true;
180 break;
181 case 0x38:
182 mType = TYPE_CLASSIC;
183 mSize = SIZE_4K;
184 mIsEmulated = true;
185 break;
186 case 0x88:
187 mType = TYPE_CLASSIC;
188 mSize = SIZE_1K;
189 // NXP-tag: false
190 break;
191 case 0x98:
192 case 0xB8:
193 mType = TYPE_PRO;
194 mSize = SIZE_4K;
195 break;
196 default:
197 // Stack incorrectly reported a MifareClassic. We cannot handle this
198 // gracefully - we have no idea of the memory layout. Bail.
199 throw new RuntimeException(
Jeff Hamilton734e9b02011-05-25 17:37:51 -0500200 "Tag incorrectly enumerated as MIFARE Classic, SAK = " + a.getSak());
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600201 }
202 }
203
Nick Pelly74fe6c62011-02-02 22:37:40 -0800204 /**
205 * Return the type of this MIFARE Classic compatible tag.
206 * <p>One of {@link #TYPE_UNKNOWN}, {@link #TYPE_CLASSIC}, {@link #TYPE_PLUS} or
207 * {@link #TYPE_PRO}.
208 * <p>Does not cause any RF activity and does not block.
209 *
210 * @return type
211 */
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600212 public int getType() {
213 return mType;
214 }
215
Nick Pelly74fe6c62011-02-02 22:37:40 -0800216 /**
217 * Return the size of the tag in bytes
218 * <p>One of {@link #SIZE_MINI}, {@link #SIZE_1K}, {@link #SIZE_2K}, {@link #SIZE_4K}.
219 * These constants are equal to their respective size in bytes.
220 * <p>Does not cause any RF activity and does not block.
221 * @return size in bytes
222 */
Nick Pellye45083b2011-01-21 22:11:29 -0800223 public int getSize() {
224 return mSize;
225 }
226
Nick Pelly74fe6c62011-02-02 22:37:40 -0800227 /**
228 * Return true if the tag is emulated, determined at discovery time.
Jeff Hamilton734e9b02011-05-25 17:37:51 -0500229 * These are actually smart-cards that emulate a MIFARE Classic interface.
230 * They can be treated identically to a MIFARE Classic tag.
Nick Pellye45083b2011-01-21 22:11:29 -0800231 * @hide
232 */
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600233 public boolean isEmulated() {
234 return mIsEmulated;
235 }
236
Nick Pelly74fe6c62011-02-02 22:37:40 -0800237 /**
238 * Return the number of MIFARE Classic sectors.
239 * <p>Does not cause any RF activity and does not block.
240 * @return number of sectors
241 */
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600242 public int getSectorCount() {
243 switch (mSize) {
Nick Pellye45083b2011-01-21 22:11:29 -0800244 case SIZE_1K:
245 return 16;
246 case SIZE_2K:
247 return 32;
248 case SIZE_4K:
249 return 40;
250 case SIZE_MINI:
251 return 5;
252 default:
253 return 0;
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600254 }
255 }
256
Nick Pelly74fe6c62011-02-02 22:37:40 -0800257 /**
258 * Return the total number of MIFARE Classic blocks.
259 * <p>Does not cause any RF activity and does not block.
260 * @return total number of blocks
Nick Pelly46797ac2011-02-03 16:06:53 -0800261 */
Nick Pellye45083b2011-01-21 22:11:29 -0800262 public int getBlockCount() {
263 return mSize / BLOCK_SIZE;
Martijn Coenena42b3522010-12-18 12:59:53 +0100264 }
265
Nick Pelly74fe6c62011-02-02 22:37:40 -0800266 /**
267 * Return the number of blocks in the given sector.
268 * <p>Does not cause any RF activity and does not block.
269 *
270 * @param sectorIndex index of sector, starting from 0
271 * @return number of blocks in the sector
272 */
Nick Pellye45083b2011-01-21 22:11:29 -0800273 public int getBlockCountInSector(int sectorIndex) {
274 validateSector(sectorIndex);
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600275
Nick Pellye45083b2011-01-21 22:11:29 -0800276 if (sectorIndex < 32) {
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600277 return 4;
278 } else {
279 return 16;
280 }
281 }
282
Nick Pelly74fe6c62011-02-02 22:37:40 -0800283 /**
284 * Return the sector that contains a given block.
285 * <p>Does not cause any RF activity and does not block.
286 *
287 * @param blockIndex index of block to lookup, starting from 0
288 * @return sector index that contains the block
289 */
Nick Pellye45083b2011-01-21 22:11:29 -0800290 public int blockToSector(int blockIndex) {
291 validateBlock(blockIndex);
292
293 if (blockIndex < 32 * 4) {
294 return blockIndex / 4;
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600295 } else {
Nick Pellye45083b2011-01-21 22:11:29 -0800296 return 32 + (blockIndex - 32 * 4) / 16;
297 }
298 }
299
Nick Pelly74fe6c62011-02-02 22:37:40 -0800300 /**
301 * Return the first block of a given sector.
302 * <p>Does not cause any RF activity and does not block.
303 *
304 * @param sectorIndex index of sector to lookup, starting from 0
305 * @return block index of first block in sector
306 */
Nick Pellye45083b2011-01-21 22:11:29 -0800307 public int sectorToBlock(int sectorIndex) {
308 if (sectorIndex < 32) {
309 return sectorIndex * 4;
310 } else {
311 return 32 * 4 + (sectorIndex - 32) * 16;
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600312 }
313 }
314
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600315 /**
Nick Pelly74fe6c62011-02-02 22:37:40 -0800316 * Authenticate a sector with key A.
317 *
318 * <p>Successful authentication of a sector with key A enables other
319 * I/O operations on that sector. The set of operations granted by key A
320 * key depends on the ACL bits set in that sector. For more information
321 * see the MIFARE Classic specification on {@see http://www.nxp.com}.
322 *
323 * <p>A failed authentication attempt causes an implicit reconnection to the
324 * tag, so authentication to other sectors will be lost.
325 *
326 * <p>This is an I/O operation and will block until complete. It must
327 * not be called from the main application thread. A blocked call will be canceled with
328 * {@link IOException} if {@link #close} is called from another thread.
329 *
Nick Pelly39cf3a42011-02-07 17:04:21 +0900330 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
331 *
Nick Pelly74fe6c62011-02-02 22:37:40 -0800332 * @param sectorIndex index of sector to authenticate, starting from 0
333 * @param key 6-byte authentication key
334 * @return true on success, false on authentication failure
335 * @throws TagLostException if the tag leaves the field
336 * @throws IOException if there is an I/O failure, or the operation is canceled
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600337 */
Nick Pellye45083b2011-01-21 22:11:29 -0800338 public boolean authenticateSectorWithKeyA(int sectorIndex, byte[] key) throws IOException {
339 return authenticate(sectorIndex, key, true);
340 }
341
342 /**
Nick Pelly74fe6c62011-02-02 22:37:40 -0800343 * Authenticate a sector with key B.
344 *
345 * <p>Successful authentication of a sector with key B enables other
346 * I/O operations on that sector. The set of operations granted by key B
347 * depends on the ACL bits set in that sector. For more information
348 * see the MIFARE Classic specification on {@see http://www.nxp.com}.
349 *
350 * <p>A failed authentication attempt causes an implicit reconnection to the
351 * tag, so authentication to other sectors will be lost.
352 *
353 * <p>This is an I/O operation and will block until complete. It must
354 * not be called from the main application thread. A blocked call will be canceled with
355 * {@link IOException} if {@link #close} is called from another thread.
356 *
Nick Pelly39cf3a42011-02-07 17:04:21 +0900357 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
358 *
Nick Pelly74fe6c62011-02-02 22:37:40 -0800359 * @param sectorIndex index of sector to authenticate, starting from 0
360 * @param key 6-byte authentication key
361 * @return true on success, false on authentication failure
362 * @throws TagLostException if the tag leaves the field
363 * @throws IOException if there is an I/O failure, or the operation is canceled
Nick Pellye45083b2011-01-21 22:11:29 -0800364 */
365 public boolean authenticateSectorWithKeyB(int sectorIndex, byte[] key) throws IOException {
366 return authenticate(sectorIndex, key, false);
367 }
368
369 private boolean authenticate(int sector, byte[] key, boolean keyA) throws IOException {
370 validateSector(sector);
Martijn Coenen4049f9d02010-12-14 16:58:27 +0100371 checkConnected();
372
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600373 byte[] cmd = new byte[12];
374
375 // First byte is the command
376 if (keyA) {
377 cmd[0] = 0x60; // phHal_eMifareAuthentA
378 } else {
379 cmd[0] = 0x61; // phHal_eMifareAuthentB
380 }
381
382 // Second byte is block address
Nick Pellye45083b2011-01-21 22:11:29 -0800383 // Authenticate command takes a block address. Authenticating a block
384 // of a sector will authenticate the entire sector.
385 cmd[1] = (byte) sectorToBlock(sector);
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600386
387 // Next 4 bytes are last 4 bytes of UID
388 byte[] uid = getTag().getId();
389 System.arraycopy(uid, uid.length - 4, cmd, 2, 4);
390
391 // Next 6 bytes are key
392 System.arraycopy(key, 0, cmd, 6, 6);
393
394 try {
Nick Pellye45083b2011-01-21 22:11:29 -0800395 if (transceive(cmd, false) != null) {
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600396 return true;
397 }
Martijn Coenenbf340612011-01-19 00:57:17 +0100398 } catch (TagLostException e) {
399 throw e;
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600400 } catch (IOException e) {
401 // No need to deal with, will return false anyway
402 }
403 return false;
404 }
405
406 /**
Nick Pellye45083b2011-01-21 22:11:29 -0800407 * Read 16-byte block.
Nick Pelly74fe6c62011-02-02 22:37:40 -0800408 *
409 * <p>This is an I/O operation and will block until complete. It must
410 * not be called from the main application thread. A blocked call will be canceled with
411 * {@link IOException} if {@link #close} is called from another thread.
412 *
Nick Pelly39cf3a42011-02-07 17:04:21 +0900413 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
414 *
Nick Pelly74fe6c62011-02-02 22:37:40 -0800415 * @param blockIndex index of block to read, starting from 0
416 * @return 16 byte block
417 * @throws TagLostException if the tag leaves the field
418 * @throws IOException if there is an I/O failure, or the operation is canceled
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600419 */
Nick Pellye45083b2011-01-21 22:11:29 -0800420 public byte[] readBlock(int blockIndex) throws IOException {
421 validateBlock(blockIndex);
Martijn Coenen4049f9d02010-12-14 16:58:27 +0100422 checkConnected();
423
Nick Pellye45083b2011-01-21 22:11:29 -0800424 byte[] cmd = { 0x30, (byte) blockIndex };
425 return transceive(cmd, false);
Martijn Coenenab82a5b2010-12-17 19:31:39 +0100426 }
427
428 /**
Nick Pellye45083b2011-01-21 22:11:29 -0800429 * Write 16-byte block.
Nick Pelly74fe6c62011-02-02 22:37:40 -0800430 *
431 * <p>This is an I/O operation and will block until complete. It must
432 * not be called from the main application thread. A blocked call will be canceled with
433 * {@link IOException} if {@link #close} is called from another thread.
434 *
Nick Pelly39cf3a42011-02-07 17:04:21 +0900435 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
436 *
Nick Pelly74fe6c62011-02-02 22:37:40 -0800437 * @param blockIndex index of block to write, starting from 0
438 * @param data 16 bytes of data to write
439 * @throws TagLostException if the tag leaves the field
440 * @throws IOException if there is an I/O failure, or the operation is canceled
Martijn Coenenab82a5b2010-12-17 19:31:39 +0100441 */
Nick Pellye45083b2011-01-21 22:11:29 -0800442 public void writeBlock(int blockIndex, byte[] data) throws IOException {
443 validateBlock(blockIndex);
Martijn Coenenab82a5b2010-12-17 19:31:39 +0100444 checkConnected();
Nick Pellye45083b2011-01-21 22:11:29 -0800445 if (data.length != 16) {
446 throw new IllegalArgumentException("must write 16-bytes");
447 }
Martijn Coenenab82a5b2010-12-17 19:31:39 +0100448
Nick Pellye45083b2011-01-21 22:11:29 -0800449 byte[] cmd = new byte[data.length + 2];
450 cmd[0] = (byte) 0xA0; // MF write command
451 cmd[1] = (byte) blockIndex;
452 System.arraycopy(data, 0, cmd, 2, data.length);
Martijn Coenenab82a5b2010-12-17 19:31:39 +0100453
Nick Pellye45083b2011-01-21 22:11:29 -0800454 transceive(cmd, false);
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600455 }
456
Martijn Coenenab82a5b2010-12-17 19:31:39 +0100457 /**
Nick Pelly74fe6c62011-02-02 22:37:40 -0800458 * Increment a value block, storing the result in the temporary block on the tag.
459 *
460 * <p>This is an I/O operation and will block until complete. It must
461 * not be called from the main application thread. A blocked call will be canceled with
462 * {@link IOException} if {@link #close} is called from another thread.
463 *
Nick Pelly39cf3a42011-02-07 17:04:21 +0900464 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
465 *
Nick Pelly74fe6c62011-02-02 22:37:40 -0800466 * @param blockIndex index of block to increment, starting from 0
467 * @param value non-negative to increment by
468 * @throws TagLostException if the tag leaves the field
469 * @throws IOException if there is an I/O failure, or the operation is canceled
Martijn Coenenab82a5b2010-12-17 19:31:39 +0100470 */
Nick Pelly1e233af2011-01-23 22:11:35 -0800471 public void increment(int blockIndex, int value) throws IOException {
Nick Pellye45083b2011-01-21 22:11:29 -0800472 validateBlock(blockIndex);
Nick Pellyb1342232011-01-25 07:45:07 -0800473 validateValueOperand(value);
Martijn Coenenab82a5b2010-12-17 19:31:39 +0100474 checkConnected();
475
Nick Pelly1e233af2011-01-23 22:11:35 -0800476 ByteBuffer cmd = ByteBuffer.allocate(6);
Nick Pellyb1342232011-01-25 07:45:07 -0800477 cmd.order(ByteOrder.LITTLE_ENDIAN);
Nick Pelly1e233af2011-01-23 22:11:35 -0800478 cmd.put( (byte) 0xC1 );
479 cmd.put( (byte) blockIndex );
Nick Pellyb1342232011-01-25 07:45:07 -0800480 cmd.putInt(value);
Martijn Coenenab82a5b2010-12-17 19:31:39 +0100481
Nick Pelly1e233af2011-01-23 22:11:35 -0800482 transceive(cmd.array(), false);
Martijn Coenenab82a5b2010-12-17 19:31:39 +0100483 }
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600484
485 /**
Nick Pelly74fe6c62011-02-02 22:37:40 -0800486 * Decrement a value block, storing the result in the temporary block on the tag.
487 *
488 * <p>This is an I/O operation and will block until complete. It must
489 * not be called from the main application thread. A blocked call will be canceled with
490 * {@link IOException} if {@link #close} is called from another thread.
491 *
Nick Pelly39cf3a42011-02-07 17:04:21 +0900492 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
493 *
Nick Pelly74fe6c62011-02-02 22:37:40 -0800494 * @param blockIndex index of block to decrement, starting from 0
495 * @param value non-negative to decrement by
496 * @throws TagLostException if the tag leaves the field
497 * @throws IOException if there is an I/O failure, or the operation is canceled
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600498 */
Nick Pelly1e233af2011-01-23 22:11:35 -0800499 public void decrement(int blockIndex, int value) throws IOException {
Nick Pellye45083b2011-01-21 22:11:29 -0800500 validateBlock(blockIndex);
Nick Pellyb1342232011-01-25 07:45:07 -0800501 validateValueOperand(value);
Martijn Coenenab82a5b2010-12-17 19:31:39 +0100502 checkConnected();
Martijn Coenenfc5a3b62010-12-10 10:46:56 -0800503
Nick Pelly1e233af2011-01-23 22:11:35 -0800504 ByteBuffer cmd = ByteBuffer.allocate(6);
Nick Pellyb1342232011-01-25 07:45:07 -0800505 cmd.order(ByteOrder.LITTLE_ENDIAN);
Nick Pelly1e233af2011-01-23 22:11:35 -0800506 cmd.put( (byte) 0xC0 );
507 cmd.put( (byte) blockIndex );
Nick Pellyb1342232011-01-25 07:45:07 -0800508 cmd.putInt(value);
Martijn Coenenab82a5b2010-12-17 19:31:39 +0100509
Nick Pelly1e233af2011-01-23 22:11:35 -0800510 transceive(cmd.array(), false);
Martijn Coenenab82a5b2010-12-17 19:31:39 +0100511 }
512
Nick Pellye45083b2011-01-21 22:11:29 -0800513 /**
Nick Pelly74fe6c62011-02-02 22:37:40 -0800514 * Copy from the temporary block to a value block.
515 *
516 * <p>This is an I/O operation and will block until complete. It must
517 * not be called from the main application thread. A blocked call will be canceled with
518 * {@link IOException} if {@link #close} is called from another thread.
519 *
Nick Pelly39cf3a42011-02-07 17:04:21 +0900520 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
521 *
Nick Pelly74fe6c62011-02-02 22:37:40 -0800522 * @param blockIndex index of block to copy to
523 * @throws TagLostException if the tag leaves the field
524 * @throws IOException if there is an I/O failure, or the operation is canceled
Nick Pellye45083b2011-01-21 22:11:29 -0800525 */
526 public void transfer(int blockIndex) throws IOException {
527 validateBlock(blockIndex);
Martijn Coenenab82a5b2010-12-17 19:31:39 +0100528 checkConnected();
529
Nick Pellye45083b2011-01-21 22:11:29 -0800530 byte[] cmd = { (byte) 0xB0, (byte) blockIndex };
Martijn Coenenab82a5b2010-12-17 19:31:39 +0100531
Nick Pellye45083b2011-01-21 22:11:29 -0800532 transceive(cmd, false);
Martijn Coenenab82a5b2010-12-17 19:31:39 +0100533 }
534
Nick Pellye45083b2011-01-21 22:11:29 -0800535 /**
Nick Pelly74fe6c62011-02-02 22:37:40 -0800536 * Copy from a value block to the temporary block.
537 *
538 * <p>This is an I/O operation and will block until complete. It must
539 * not be called from the main application thread. A blocked call will be canceled with
540 * {@link IOException} if {@link #close} is called from another thread.
541 *
Nick Pelly39cf3a42011-02-07 17:04:21 +0900542 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
543 *
Nick Pelly74fe6c62011-02-02 22:37:40 -0800544 * @param blockIndex index of block to copy from
545 * @throws TagLostException if the tag leaves the field
546 * @throws IOException if there is an I/O failure, or the operation is canceled
Nick Pellye45083b2011-01-21 22:11:29 -0800547 */
548 public void restore(int blockIndex) throws IOException {
549 validateBlock(blockIndex);
Martijn Coenenab82a5b2010-12-17 19:31:39 +0100550 checkConnected();
551
Nick Pellye45083b2011-01-21 22:11:29 -0800552 byte[] cmd = { (byte) 0xC2, (byte) blockIndex };
Martijn Coenenab82a5b2010-12-17 19:31:39 +0100553
Nick Pellye45083b2011-01-21 22:11:29 -0800554 transceive(cmd, false);
Martijn Coenenfc5a3b62010-12-10 10:46:56 -0800555 }
Jeff Hamiltonce3224c2011-01-17 11:05:03 -0800556
557 /**
558 * Send raw NfcA data to a tag and receive the response.
Jeff Hamiltonce3224c2011-01-17 11:05:03 -0800559 *
Nick Pelly74fe6c62011-02-02 22:37:40 -0800560 * <p>This is equivalent to connecting to this tag via {@link NfcA}
561 * and calling {@link NfcA#transceive}. Note that all MIFARE Classic
562 * tags are based on {@link NfcA} technology.
563 *
Martijn Coenenfaca12a2011-08-19 14:07:52 +0200564 * <p>Use {@link #getMaxTransceiveLength} to retrieve the maximum number of bytes
565 * that can be sent with {@link #transceive}.
566 *
Nick Pelly74fe6c62011-02-02 22:37:40 -0800567 * <p>This is an I/O operation and will block until complete. It must
568 * not be called from the main application thread. A blocked call will be canceled with
569 * {@link IOException} if {@link #close} is called from another thread.
570 *
Nick Pelly39cf3a42011-02-07 17:04:21 +0900571 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
572 *
Nick Pelly74fe6c62011-02-02 22:37:40 -0800573 * @see NfcA#transceive
Jeff Hamiltonce3224c2011-01-17 11:05:03 -0800574 */
575 public byte[] transceive(byte[] data) throws IOException {
576 return transceive(data, true);
577 }
Nick Pellye45083b2011-01-21 22:11:29 -0800578
Martijn Coenen112fdf62011-06-09 16:57:49 +0200579 /**
Martijn Coenenfaca12a2011-08-19 14:07:52 +0200580 * Return the maximum number of bytes that can be sent with {@link #transceive}.
581 * @return the maximum number of bytes that can be sent with {@link #transceive}.
582 */
583 public int getMaxTransceiveLength() {
584 return getMaxTransceiveLengthInternal();
585 }
586
587 /**
Nick Pelly82328bf2011-08-30 09:37:25 -0700588 * Set the {@link #transceive} timeout in milliseconds.
589 *
590 * <p>The timeout only applies to {@link #transceive} on this object,
Martijn Coenen112fdf62011-06-09 16:57:49 +0200591 * and is reset to a default value when {@link #close} is called.
Nick Pelly82328bf2011-08-30 09:37:25 -0700592 *
Martijn Coenen112fdf62011-06-09 16:57:49 +0200593 * <p>Setting a longer timeout may be useful when performing
594 * transactions that require a long processing time on the tag
595 * such as key generation.
596 *
597 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
598 *
599 * @param timeout timeout value in milliseconds
Martijn Coenen112fdf62011-06-09 16:57:49 +0200600 */
Martijn Coenen112fdf62011-06-09 16:57:49 +0200601 public void setTimeout(int timeout) {
602 try {
603 int err = mTag.getTagService().setTimeout(TagTechnology.MIFARE_CLASSIC, timeout);
604 if (err != ErrorCodes.SUCCESS) {
605 throw new IllegalArgumentException("The supplied timeout is not valid");
606 }
607 } catch (RemoteException e) {
608 Log.e(TAG, "NFC service dead", e);
609 }
610 }
611
Martijn Coenen20e62c92011-07-20 16:06:34 +0200612 /**
Nick Pelly82328bf2011-08-30 09:37:25 -0700613 * Get the current {@link #transceive} timeout in milliseconds.
Martijn Coenen20e62c92011-07-20 16:06:34 +0200614 *
615 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
616 *
617 * @return timeout value in milliseconds
Martijn Coenen20e62c92011-07-20 16:06:34 +0200618 */
Martijn Coenen20e62c92011-07-20 16:06:34 +0200619 public int getTimeout() {
620 try {
621 return mTag.getTagService().getTimeout(TagTechnology.MIFARE_CLASSIC);
622 } catch (RemoteException e) {
623 Log.e(TAG, "NFC service dead", e);
624 return 0;
625 }
626 }
627
Nick Pelly4a5e2532011-01-27 10:04:04 -0800628 private static void validateSector(int sector) {
Nick Pellye45083b2011-01-21 22:11:29 -0800629 // Do not be too strict on upper bounds checking, since some cards
630 // have more addressable memory than they report. For example,
Jeff Hamilton734e9b02011-05-25 17:37:51 -0500631 // MIFARE Plus 2k cards will appear as MIFARE Classic 1k cards when in
632 // MIFARE Classic compatibility mode.
Nick Pellye45083b2011-01-21 22:11:29 -0800633 // Note that issuing a command to an out-of-bounds block is safe - the
634 // tag should report error causing IOException. This validation is a
635 // helper to guard against obvious programming mistakes.
636 if (sector < 0 || sector >= MAX_SECTOR_COUNT) {
637 throw new IndexOutOfBoundsException("sector out of bounds: " + sector);
638 }
639 }
640
Nick Pelly4a5e2532011-01-27 10:04:04 -0800641 private static void validateBlock(int block) {
Nick Pellye45083b2011-01-21 22:11:29 -0800642 // Just looking for obvious out of bounds...
643 if (block < 0 || block >= MAX_BLOCK_COUNT) {
644 throw new IndexOutOfBoundsException("block out of bounds: " + block);
645 }
646 }
Nick Pelly4a5e2532011-01-27 10:04:04 -0800647
648 private static void validateValueOperand(int value) {
649 if (value < 0) {
650 throw new IllegalArgumentException("value operand negative");
651 }
652 }
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600653}