Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 1 | /* |
| 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 Hamilton | 4e21e1d | 2011-01-21 01:13:06 -0600 | [diff] [blame] | 17 | package android.nfc.tech; |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 18 | |
Martijn Coenen | 112fdf6 | 2011-06-09 16:57:49 +0200 | [diff] [blame] | 19 | import android.nfc.ErrorCodes; |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 20 | import android.nfc.Tag; |
Jeff Hamilton | 4e21e1d | 2011-01-21 01:13:06 -0600 | [diff] [blame] | 21 | import android.nfc.TagLostException; |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 22 | import android.os.RemoteException; |
Martijn Coenen | 112fdf6 | 2011-06-09 16:57:49 +0200 | [diff] [blame] | 23 | import android.util.Log; |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 24 | |
| 25 | import java.io.IOException; |
Nick Pelly | 1e233af | 2011-01-23 22:11:35 -0800 | [diff] [blame] | 26 | import java.nio.ByteBuffer; |
Nick Pelly | b134223 | 2011-01-25 07:45:07 -0800 | [diff] [blame] | 27 | import java.nio.ByteOrder; |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 28 | |
| 29 | /** |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 30 | * Provides access to MIFARE Classic properties and I/O operations on a {@link Tag}. |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 31 | * |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 32 | * <p>Acquire a {@link MifareClassic} object using {@link #get}. |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 33 | * |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 34 | * <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 Pelly | 39cf3a4 | 2011-02-07 17:04:21 +0900 | [diff] [blame] | 60 | * have been formatted according to the NXP specification for NDEF on MIFARE Classic. |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 61 | * |
| 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 Pelly | 39cf3a4 | 2011-02-07 17:04:21 +0900 | [diff] [blame] | 69 | * |
| 70 | * <p class="note"><strong>Note:</strong> Methods that perform I/O operations |
| 71 | * require the {@link android.Manifest.permission#NFC} permission. |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 72 | */ |
| 73 | public final class MifareClassic extends BasicTagTechnology { |
Martijn Coenen | 112fdf6 | 2011-06-09 16:57:49 +0200 | [diff] [blame] | 74 | private static final String TAG = "NFC"; |
| 75 | |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 76 | /** |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 77 | * The default factory key. |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 78 | */ |
Jan Brands | 65c3f98 | 2010-12-06 21:05:52 +0100 | [diff] [blame] | 79 | public static final byte[] KEY_DEFAULT = |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 80 | {(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF}; |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 81 | /** |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 82 | * The well-known key for tags formatted according to the |
| 83 | * MIFARE Application Directory (MAD) specification. |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 84 | */ |
Jan Brands | 65c3f98 | 2010-12-06 21:05:52 +0100 | [diff] [blame] | 85 | public static final byte[] KEY_MIFARE_APPLICATION_DIRECTORY = |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 86 | {(byte)0xA0,(byte)0xA1,(byte)0xA2,(byte)0xA3,(byte)0xA4,(byte)0xA5}; |
| 87 | /** |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 88 | * The well-known key for tags formatted according to the |
Jeff Hamilton | 734e9b0 | 2011-05-25 17:37:51 -0500 | [diff] [blame] | 89 | * NDEF on MIFARE Classic specification. |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 90 | */ |
Jan Brands | 65c3f98 | 2010-12-06 21:05:52 +0100 | [diff] [blame] | 91 | public static final byte[] KEY_NFC_FORUM = |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 92 | {(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7}; |
| 93 | |
Jeff Hamilton | 734e9b0 | 2011-05-25 17:37:51 -0500 | [diff] [blame] | 94 | /** A MIFARE Classic compatible card of unknown type */ |
Nick Pelly | 4a5e253 | 2011-01-27 10:04:04 -0800 | [diff] [blame] | 95 | public static final int TYPE_UNKNOWN = -1; |
Jeff Hamilton | ce3224c | 2011-01-17 11:05:03 -0800 | [diff] [blame] | 96 | /** A MIFARE Classic tag */ |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 97 | public static final int TYPE_CLASSIC = 0; |
Jeff Hamilton | ce3224c | 2011-01-17 11:05:03 -0800 | [diff] [blame] | 98 | /** A MIFARE Plus tag */ |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 99 | public static final int TYPE_PLUS = 1; |
Jeff Hamilton | ce3224c | 2011-01-17 11:05:03 -0800 | [diff] [blame] | 100 | /** A MIFARE Pro tag */ |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 101 | public static final int TYPE_PRO = 2; |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 102 | |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 103 | /** Tag contains 16 sectors, each with 4 blocks. */ |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 104 | public static final int SIZE_1K = 1024; |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 105 | /** Tag contains 32 sectors, each with 4 blocks. */ |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 106 | public static final int SIZE_2K = 2048; |
Jeff Hamilton | ce3224c | 2011-01-17 11:05:03 -0800 | [diff] [blame] | 107 | /** |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 108 | * Tag contains 40 sectors. The first 32 sectors contain 4 blocks and the last 8 sectors |
Jeff Hamilton | ce3224c | 2011-01-17 11:05:03 -0800 | [diff] [blame] | 109 | * contain 16 blocks. |
| 110 | */ |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 111 | public static final int SIZE_4K = 4096; |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 112 | /** Tag contains 5 sectors, each with 4 blocks. */ |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 113 | public static final int SIZE_MINI = 320; |
Nick Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 114 | |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 115 | /** Size of a MIFARE Classic block (in bytes) */ |
Nick Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 116 | 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 Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 120 | |
| 121 | private boolean mIsEmulated; |
| 122 | private int mType; |
| 123 | private int mSize; |
| 124 | |
Jeff Hamilton | 4e21e1d | 2011-01-21 01:13:06 -0600 | [diff] [blame] | 125 | /** |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 126 | * 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 Hamilton | 4e21e1d | 2011-01-21 01:13:06 -0600 | [diff] [blame] | 131 | * |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 132 | * @param tag an MIFARE Classic compatible tag |
| 133 | * @return MIFARE Classic object |
Jeff Hamilton | 4e21e1d | 2011-01-21 01:13:06 -0600 | [diff] [blame] | 134 | */ |
| 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 Hamilton | ce3224c | 2011-01-17 11:05:03 -0800 | [diff] [blame] | 144 | /** @hide */ |
Jeff Hamilton | 4e21e1d | 2011-01-21 01:13:06 -0600 | [diff] [blame] | 145 | public MifareClassic(Tag tag) throws RemoteException { |
| 146 | super(tag, TagTechnology.MIFARE_CLASSIC); |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 147 | |
Jeff Hamilton | 734e9b0 | 2011-05-25 17:37:51 -0500 | [diff] [blame] | 148 | NfcA a = NfcA.get(tag); // MIFARE Classic is always based on NFC a |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 149 | |
| 150 | mIsEmulated = false; |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 151 | |
| 152 | switch (a.getSak()) { |
Sunil Jogi | 72677c9 | 2011-11-23 15:03:16 -0800 | [diff] [blame] | 153 | case 0x01: |
Nick Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 154 | 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 Hamilton | 734e9b0 | 2011-05-25 17:37:51 -0500 | [diff] [blame] | 200 | "Tag incorrectly enumerated as MIFARE Classic, SAK = " + a.getSak()); |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 201 | } |
| 202 | } |
| 203 | |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 204 | /** |
| 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 Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 212 | public int getType() { |
| 213 | return mType; |
| 214 | } |
| 215 | |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 216 | /** |
| 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 Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 223 | public int getSize() { |
| 224 | return mSize; |
| 225 | } |
| 226 | |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 227 | /** |
| 228 | * Return true if the tag is emulated, determined at discovery time. |
Jeff Hamilton | 734e9b0 | 2011-05-25 17:37:51 -0500 | [diff] [blame] | 229 | * These are actually smart-cards that emulate a MIFARE Classic interface. |
| 230 | * They can be treated identically to a MIFARE Classic tag. |
Nick Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 231 | * @hide |
| 232 | */ |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 233 | public boolean isEmulated() { |
| 234 | return mIsEmulated; |
| 235 | } |
| 236 | |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 237 | /** |
| 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 Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 242 | public int getSectorCount() { |
| 243 | switch (mSize) { |
Nick Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 244 | 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 Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 254 | } |
| 255 | } |
| 256 | |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 257 | /** |
| 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 Pelly | 46797ac | 2011-02-03 16:06:53 -0800 | [diff] [blame] | 261 | */ |
Nick Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 262 | public int getBlockCount() { |
| 263 | return mSize / BLOCK_SIZE; |
Martijn Coenen | a42b352 | 2010-12-18 12:59:53 +0100 | [diff] [blame] | 264 | } |
| 265 | |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 266 | /** |
| 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 Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 273 | public int getBlockCountInSector(int sectorIndex) { |
| 274 | validateSector(sectorIndex); |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 275 | |
Nick Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 276 | if (sectorIndex < 32) { |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 277 | return 4; |
| 278 | } else { |
| 279 | return 16; |
| 280 | } |
| 281 | } |
| 282 | |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 283 | /** |
| 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 Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 290 | public int blockToSector(int blockIndex) { |
| 291 | validateBlock(blockIndex); |
| 292 | |
| 293 | if (blockIndex < 32 * 4) { |
| 294 | return blockIndex / 4; |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 295 | } else { |
Nick Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 296 | return 32 + (blockIndex - 32 * 4) / 16; |
| 297 | } |
| 298 | } |
| 299 | |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 300 | /** |
| 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 Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 307 | public int sectorToBlock(int sectorIndex) { |
| 308 | if (sectorIndex < 32) { |
| 309 | return sectorIndex * 4; |
| 310 | } else { |
| 311 | return 32 * 4 + (sectorIndex - 32) * 16; |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 312 | } |
| 313 | } |
| 314 | |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 315 | /** |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 316 | * 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 Pelly | 39cf3a4 | 2011-02-07 17:04:21 +0900 | [diff] [blame] | 330 | * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. |
| 331 | * |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 332 | * @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 Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 337 | */ |
Nick Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 338 | public boolean authenticateSectorWithKeyA(int sectorIndex, byte[] key) throws IOException { |
| 339 | return authenticate(sectorIndex, key, true); |
| 340 | } |
| 341 | |
| 342 | /** |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 343 | * 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 Pelly | 39cf3a4 | 2011-02-07 17:04:21 +0900 | [diff] [blame] | 357 | * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. |
| 358 | * |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 359 | * @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 Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 364 | */ |
| 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 Coenen | 4049f9d0 | 2010-12-14 16:58:27 +0100 | [diff] [blame] | 371 | checkConnected(); |
| 372 | |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 373 | 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 Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 383 | // 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 Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 386 | |
| 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 Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 395 | if (transceive(cmd, false) != null) { |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 396 | return true; |
| 397 | } |
Martijn Coenen | bf34061 | 2011-01-19 00:57:17 +0100 | [diff] [blame] | 398 | } catch (TagLostException e) { |
| 399 | throw e; |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 400 | } catch (IOException e) { |
| 401 | // No need to deal with, will return false anyway |
| 402 | } |
| 403 | return false; |
| 404 | } |
| 405 | |
| 406 | /** |
Nick Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 407 | * Read 16-byte block. |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 408 | * |
| 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 Pelly | 39cf3a4 | 2011-02-07 17:04:21 +0900 | [diff] [blame] | 413 | * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. |
| 414 | * |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 415 | * @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 Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 419 | */ |
Nick Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 420 | public byte[] readBlock(int blockIndex) throws IOException { |
| 421 | validateBlock(blockIndex); |
Martijn Coenen | 4049f9d0 | 2010-12-14 16:58:27 +0100 | [diff] [blame] | 422 | checkConnected(); |
| 423 | |
Nick Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 424 | byte[] cmd = { 0x30, (byte) blockIndex }; |
| 425 | return transceive(cmd, false); |
Martijn Coenen | ab82a5b | 2010-12-17 19:31:39 +0100 | [diff] [blame] | 426 | } |
| 427 | |
| 428 | /** |
Nick Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 429 | * Write 16-byte block. |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 430 | * |
| 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 Pelly | 39cf3a4 | 2011-02-07 17:04:21 +0900 | [diff] [blame] | 435 | * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. |
| 436 | * |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 437 | * @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 Coenen | ab82a5b | 2010-12-17 19:31:39 +0100 | [diff] [blame] | 441 | */ |
Nick Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 442 | public void writeBlock(int blockIndex, byte[] data) throws IOException { |
| 443 | validateBlock(blockIndex); |
Martijn Coenen | ab82a5b | 2010-12-17 19:31:39 +0100 | [diff] [blame] | 444 | checkConnected(); |
Nick Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 445 | if (data.length != 16) { |
| 446 | throw new IllegalArgumentException("must write 16-bytes"); |
| 447 | } |
Martijn Coenen | ab82a5b | 2010-12-17 19:31:39 +0100 | [diff] [blame] | 448 | |
Nick Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 449 | 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 Coenen | ab82a5b | 2010-12-17 19:31:39 +0100 | [diff] [blame] | 453 | |
Nick Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 454 | transceive(cmd, false); |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 455 | } |
| 456 | |
Martijn Coenen | ab82a5b | 2010-12-17 19:31:39 +0100 | [diff] [blame] | 457 | /** |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 458 | * 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 Pelly | 39cf3a4 | 2011-02-07 17:04:21 +0900 | [diff] [blame] | 464 | * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. |
| 465 | * |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 466 | * @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 Coenen | ab82a5b | 2010-12-17 19:31:39 +0100 | [diff] [blame] | 470 | */ |
Nick Pelly | 1e233af | 2011-01-23 22:11:35 -0800 | [diff] [blame] | 471 | public void increment(int blockIndex, int value) throws IOException { |
Nick Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 472 | validateBlock(blockIndex); |
Nick Pelly | b134223 | 2011-01-25 07:45:07 -0800 | [diff] [blame] | 473 | validateValueOperand(value); |
Martijn Coenen | ab82a5b | 2010-12-17 19:31:39 +0100 | [diff] [blame] | 474 | checkConnected(); |
| 475 | |
Nick Pelly | 1e233af | 2011-01-23 22:11:35 -0800 | [diff] [blame] | 476 | ByteBuffer cmd = ByteBuffer.allocate(6); |
Nick Pelly | b134223 | 2011-01-25 07:45:07 -0800 | [diff] [blame] | 477 | cmd.order(ByteOrder.LITTLE_ENDIAN); |
Nick Pelly | 1e233af | 2011-01-23 22:11:35 -0800 | [diff] [blame] | 478 | cmd.put( (byte) 0xC1 ); |
| 479 | cmd.put( (byte) blockIndex ); |
Nick Pelly | b134223 | 2011-01-25 07:45:07 -0800 | [diff] [blame] | 480 | cmd.putInt(value); |
Martijn Coenen | ab82a5b | 2010-12-17 19:31:39 +0100 | [diff] [blame] | 481 | |
Nick Pelly | 1e233af | 2011-01-23 22:11:35 -0800 | [diff] [blame] | 482 | transceive(cmd.array(), false); |
Martijn Coenen | ab82a5b | 2010-12-17 19:31:39 +0100 | [diff] [blame] | 483 | } |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 484 | |
| 485 | /** |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 486 | * 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 Pelly | 39cf3a4 | 2011-02-07 17:04:21 +0900 | [diff] [blame] | 492 | * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. |
| 493 | * |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 494 | * @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 Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 498 | */ |
Nick Pelly | 1e233af | 2011-01-23 22:11:35 -0800 | [diff] [blame] | 499 | public void decrement(int blockIndex, int value) throws IOException { |
Nick Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 500 | validateBlock(blockIndex); |
Nick Pelly | b134223 | 2011-01-25 07:45:07 -0800 | [diff] [blame] | 501 | validateValueOperand(value); |
Martijn Coenen | ab82a5b | 2010-12-17 19:31:39 +0100 | [diff] [blame] | 502 | checkConnected(); |
Martijn Coenen | fc5a3b6 | 2010-12-10 10:46:56 -0800 | [diff] [blame] | 503 | |
Nick Pelly | 1e233af | 2011-01-23 22:11:35 -0800 | [diff] [blame] | 504 | ByteBuffer cmd = ByteBuffer.allocate(6); |
Nick Pelly | b134223 | 2011-01-25 07:45:07 -0800 | [diff] [blame] | 505 | cmd.order(ByteOrder.LITTLE_ENDIAN); |
Nick Pelly | 1e233af | 2011-01-23 22:11:35 -0800 | [diff] [blame] | 506 | cmd.put( (byte) 0xC0 ); |
| 507 | cmd.put( (byte) blockIndex ); |
Nick Pelly | b134223 | 2011-01-25 07:45:07 -0800 | [diff] [blame] | 508 | cmd.putInt(value); |
Martijn Coenen | ab82a5b | 2010-12-17 19:31:39 +0100 | [diff] [blame] | 509 | |
Nick Pelly | 1e233af | 2011-01-23 22:11:35 -0800 | [diff] [blame] | 510 | transceive(cmd.array(), false); |
Martijn Coenen | ab82a5b | 2010-12-17 19:31:39 +0100 | [diff] [blame] | 511 | } |
| 512 | |
Nick Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 513 | /** |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 514 | * 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 Pelly | 39cf3a4 | 2011-02-07 17:04:21 +0900 | [diff] [blame] | 520 | * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. |
| 521 | * |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 522 | * @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 Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 525 | */ |
| 526 | public void transfer(int blockIndex) throws IOException { |
| 527 | validateBlock(blockIndex); |
Martijn Coenen | ab82a5b | 2010-12-17 19:31:39 +0100 | [diff] [blame] | 528 | checkConnected(); |
| 529 | |
Nick Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 530 | byte[] cmd = { (byte) 0xB0, (byte) blockIndex }; |
Martijn Coenen | ab82a5b | 2010-12-17 19:31:39 +0100 | [diff] [blame] | 531 | |
Nick Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 532 | transceive(cmd, false); |
Martijn Coenen | ab82a5b | 2010-12-17 19:31:39 +0100 | [diff] [blame] | 533 | } |
| 534 | |
Nick Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 535 | /** |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 536 | * 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 Pelly | 39cf3a4 | 2011-02-07 17:04:21 +0900 | [diff] [blame] | 542 | * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. |
| 543 | * |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 544 | * @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 Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 547 | */ |
| 548 | public void restore(int blockIndex) throws IOException { |
| 549 | validateBlock(blockIndex); |
Martijn Coenen | ab82a5b | 2010-12-17 19:31:39 +0100 | [diff] [blame] | 550 | checkConnected(); |
| 551 | |
Nick Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 552 | byte[] cmd = { (byte) 0xC2, (byte) blockIndex }; |
Martijn Coenen | ab82a5b | 2010-12-17 19:31:39 +0100 | [diff] [blame] | 553 | |
Nick Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 554 | transceive(cmd, false); |
Martijn Coenen | fc5a3b6 | 2010-12-10 10:46:56 -0800 | [diff] [blame] | 555 | } |
Jeff Hamilton | ce3224c | 2011-01-17 11:05:03 -0800 | [diff] [blame] | 556 | |
| 557 | /** |
| 558 | * Send raw NfcA data to a tag and receive the response. |
Jeff Hamilton | ce3224c | 2011-01-17 11:05:03 -0800 | [diff] [blame] | 559 | * |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 560 | * <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 Coenen | faca12a | 2011-08-19 14:07:52 +0200 | [diff] [blame] | 564 | * <p>Use {@link #getMaxTransceiveLength} to retrieve the maximum number of bytes |
| 565 | * that can be sent with {@link #transceive}. |
| 566 | * |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 567 | * <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 Pelly | 39cf3a4 | 2011-02-07 17:04:21 +0900 | [diff] [blame] | 571 | * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. |
| 572 | * |
Nick Pelly | 74fe6c6 | 2011-02-02 22:37:40 -0800 | [diff] [blame] | 573 | * @see NfcA#transceive |
Jeff Hamilton | ce3224c | 2011-01-17 11:05:03 -0800 | [diff] [blame] | 574 | */ |
| 575 | public byte[] transceive(byte[] data) throws IOException { |
| 576 | return transceive(data, true); |
| 577 | } |
Nick Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 578 | |
Martijn Coenen | 112fdf6 | 2011-06-09 16:57:49 +0200 | [diff] [blame] | 579 | /** |
Martijn Coenen | faca12a | 2011-08-19 14:07:52 +0200 | [diff] [blame] | 580 | * 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 Pelly | 82328bf | 2011-08-30 09:37:25 -0700 | [diff] [blame] | 588 | * Set the {@link #transceive} timeout in milliseconds. |
| 589 | * |
| 590 | * <p>The timeout only applies to {@link #transceive} on this object, |
Martijn Coenen | 112fdf6 | 2011-06-09 16:57:49 +0200 | [diff] [blame] | 591 | * and is reset to a default value when {@link #close} is called. |
Nick Pelly | 82328bf | 2011-08-30 09:37:25 -0700 | [diff] [blame] | 592 | * |
Martijn Coenen | 112fdf6 | 2011-06-09 16:57:49 +0200 | [diff] [blame] | 593 | * <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 Coenen | 112fdf6 | 2011-06-09 16:57:49 +0200 | [diff] [blame] | 600 | */ |
Martijn Coenen | 112fdf6 | 2011-06-09 16:57:49 +0200 | [diff] [blame] | 601 | 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 Coenen | 20e62c9 | 2011-07-20 16:06:34 +0200 | [diff] [blame] | 612 | /** |
Nick Pelly | 82328bf | 2011-08-30 09:37:25 -0700 | [diff] [blame] | 613 | * Get the current {@link #transceive} timeout in milliseconds. |
Martijn Coenen | 20e62c9 | 2011-07-20 16:06:34 +0200 | [diff] [blame] | 614 | * |
| 615 | * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. |
| 616 | * |
| 617 | * @return timeout value in milliseconds |
Martijn Coenen | 20e62c9 | 2011-07-20 16:06:34 +0200 | [diff] [blame] | 618 | */ |
Martijn Coenen | 20e62c9 | 2011-07-20 16:06:34 +0200 | [diff] [blame] | 619 | 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 Pelly | 4a5e253 | 2011-01-27 10:04:04 -0800 | [diff] [blame] | 628 | private static void validateSector(int sector) { |
Nick Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 629 | // Do not be too strict on upper bounds checking, since some cards |
| 630 | // have more addressable memory than they report. For example, |
Jeff Hamilton | 734e9b0 | 2011-05-25 17:37:51 -0500 | [diff] [blame] | 631 | // MIFARE Plus 2k cards will appear as MIFARE Classic 1k cards when in |
| 632 | // MIFARE Classic compatibility mode. |
Nick Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 633 | // 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 Pelly | 4a5e253 | 2011-01-27 10:04:04 -0800 | [diff] [blame] | 641 | private static void validateBlock(int block) { |
Nick Pelly | e45083b | 2011-01-21 22:11:29 -0800 | [diff] [blame] | 642 | // 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 Pelly | 4a5e253 | 2011-01-27 10:04:04 -0800 | [diff] [blame] | 647 | |
| 648 | private static void validateValueOperand(int value) { |
| 649 | if (value < 0) { |
| 650 | throw new IllegalArgumentException("value operand negative"); |
| 651 | } |
| 652 | } |
Jeff Hamilton | 6be655c | 2010-11-12 12:28:16 -0600 | [diff] [blame] | 653 | } |