16be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton/* 26be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton * Copyright (C) 2010 The Android Open Source Project 36be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton * 46be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton * Licensed under the Apache License, Version 2.0 (the "License"); 56be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton * you may not use this file except in compliance with the License. 66be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton * You may obtain a copy of the License at 76be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton * 86be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton * http://www.apache.org/licenses/LICENSE-2.0 96be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton * 106be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton * Unless required by applicable law or agreed to in writing, software 116be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton * distributed under the License is distributed on an "AS IS" BASIS, 126be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 136be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton * See the License for the specific language governing permissions and 146be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton * limitations under the License. 156be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton */ 166be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton 174e21e1d21a877cce4db5ec8c5786604cc10f2d7eJeff Hamiltonpackage android.nfc.tech; 186be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton 19112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenenimport android.nfc.ErrorCodes; 206be655c768a82716612c00fdd156254d8dc00f42Jeff Hamiltonimport android.nfc.Tag; 214e21e1d21a877cce4db5ec8c5786604cc10f2d7eJeff Hamiltonimport android.nfc.TagLostException; 226be655c768a82716612c00fdd156254d8dc00f42Jeff Hamiltonimport android.os.RemoteException; 23112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenenimport android.util.Log; 246be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton 256be655c768a82716612c00fdd156254d8dc00f42Jeff Hamiltonimport java.io.IOException; 261e233af3a783d44843a6f2b895d00a5d3b0c29f0Nick Pellyimport java.nio.ByteBuffer; 27b134223f91c8801d577cb72e92a37cb65fec717aNick Pellyimport java.nio.ByteOrder; 286be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton 296be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton/** 3074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * Provides access to MIFARE Classic properties and I/O operations on a {@link Tag}. 316be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton * 3274fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <p>Acquire a {@link MifareClassic} object using {@link #get}. 336be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton * 3474fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <p>MIFARE Classic is also known as MIFARE Standard. 3574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <p>MIFARE Classic tags are divided into sectors, and each sector is sub-divided into 3674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * blocks. Block size is always 16 bytes ({@link #BLOCK_SIZE}. Sector size varies. 3774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <ul> 3874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <li>MIFARE Classic Mini are 320 bytes ({@link #SIZE_MINI}), with 5 sectors each of 4 blocks. 3974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <li>MIFARE Classic 1k are 1024 bytes ({@link #SIZE_1K}), with 16 sectors each of 4 blocks. 4074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <li>MIFARE Classic 2k are 2048 bytes ({@link #SIZE_2K}), with 32 sectors each of 4 blocks. 4174fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <li>MIFARE Classic 4k} are 4096 bytes ({@link #SIZE_4K}). The first 32 sectors contain 4 blocks 4274fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * and the last 8 sectors contain 16 blocks. 4374fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * </ul> 4474fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * 4574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <p>MIFARE Classic tags require authentication on a per-sector basis before any 4674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * other I/O operations on that sector can be performed. There are two keys per sector, 4774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * and ACL bits determine what I/O operations are allowed on that sector after 4874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * authenticating with a key. {@see #authenticateSectorWithKeyA} and 4974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * {@see #authenticateSectorWithKeyB}. 5074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * 5174fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <p>Three well-known authentication keys are defined in this class: 5274fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * {@link #KEY_DEFAULT}, {@link #KEY_MIFARE_APPLICATION_DIRECTORY}, 5374fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * {@link #KEY_NFC_FORUM}. 5474fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <ul> 5574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <li>{@link #KEY_DEFAULT} is the default factory key for MIFARE Classic. 5674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <li>{@link #KEY_MIFARE_APPLICATION_DIRECTORY} is the well-known key for 5774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * MIFARE Classic cards that have been formatted according to the 5874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * MIFARE Application Directory (MAD) specification. 5974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <li>{@link #KEY_NFC_FORUM} is the well-known key for MIFARE Classic cards that 6039cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly * have been formatted according to the NXP specification for NDEF on MIFARE Classic. 6174fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * 6274fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <p>Implementation of this class on a Android NFC device is optional. 6374fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * If it is not implemented, then 6474fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * {@link MifareClassic} will never be enumerated in {@link Tag#getTechList}. 6574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * If it is enumerated, then all {@link MifareClassic} I/O operations will be supported, 6674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * and {@link Ndef#MIFARE_CLASSIC} NDEF tags will also be supported. In either case, 6774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * {@link NfcA} will also be enumerated on the tag, because all MIFARE Classic tags are also 6874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * {@link NfcA}. 6939cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly * 7039cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly * <p class="note"><strong>Note:</strong> Methods that perform I/O operations 7139cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly * require the {@link android.Manifest.permission#NFC} permission. 726be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton */ 736be655c768a82716612c00fdd156254d8dc00f42Jeff Hamiltonpublic final class MifareClassic extends BasicTagTechnology { 74112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen private static final String TAG = "NFC"; 75112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen 766be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton /** 7774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * The default factory key. 786be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton */ 7965c3f9806edd694c4db00fa2884139ea97c80962Jan Brands public static final byte[] KEY_DEFAULT = 806be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton {(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF}; 816be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton /** 8274fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * The well-known key for tags formatted according to the 8374fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * MIFARE Application Directory (MAD) specification. 846be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton */ 8565c3f9806edd694c4db00fa2884139ea97c80962Jan Brands public static final byte[] KEY_MIFARE_APPLICATION_DIRECTORY = 866be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton {(byte)0xA0,(byte)0xA1,(byte)0xA2,(byte)0xA3,(byte)0xA4,(byte)0xA5}; 876be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton /** 8874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * The well-known key for tags formatted according to the 89734e9b0c73483fdaa582c21dedc24107b1fe8838Jeff Hamilton * NDEF on MIFARE Classic specification. 906be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton */ 9165c3f9806edd694c4db00fa2884139ea97c80962Jan Brands public static final byte[] KEY_NFC_FORUM = 926be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton {(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7}; 936be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton 94734e9b0c73483fdaa582c21dedc24107b1fe8838Jeff Hamilton /** A MIFARE Classic compatible card of unknown type */ 954a5e2532205252e0b8616ebc07ca089fd3721681Nick Pelly public static final int TYPE_UNKNOWN = -1; 96ce3224cda51f946871daa1e11e3976e25c59e6faJeff Hamilton /** A MIFARE Classic tag */ 976be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton public static final int TYPE_CLASSIC = 0; 98ce3224cda51f946871daa1e11e3976e25c59e6faJeff Hamilton /** A MIFARE Plus tag */ 996be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton public static final int TYPE_PLUS = 1; 100ce3224cda51f946871daa1e11e3976e25c59e6faJeff Hamilton /** A MIFARE Pro tag */ 1016be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton public static final int TYPE_PRO = 2; 1026be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton 10374fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly /** Tag contains 16 sectors, each with 4 blocks. */ 1046be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton public static final int SIZE_1K = 1024; 10574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly /** Tag contains 32 sectors, each with 4 blocks. */ 1066be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton public static final int SIZE_2K = 2048; 107ce3224cda51f946871daa1e11e3976e25c59e6faJeff Hamilton /** 10874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * Tag contains 40 sectors. The first 32 sectors contain 4 blocks and the last 8 sectors 109ce3224cda51f946871daa1e11e3976e25c59e6faJeff Hamilton * contain 16 blocks. 110ce3224cda51f946871daa1e11e3976e25c59e6faJeff Hamilton */ 1116be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton public static final int SIZE_4K = 4096; 11274fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly /** Tag contains 5 sectors, each with 4 blocks. */ 1136be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton public static final int SIZE_MINI = 320; 114e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly 11574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly /** Size of a MIFARE Classic block (in bytes) */ 116e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly public static final int BLOCK_SIZE = 16; 117e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly 118e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly private static final int MAX_BLOCK_COUNT = 256; 119e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly private static final int MAX_SECTOR_COUNT = 40; 1206be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton 1216be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton private boolean mIsEmulated; 1226be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton private int mType; 1236be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton private int mSize; 1246be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton 1254e21e1d21a877cce4db5ec8c5786604cc10f2d7eJeff Hamilton /** 12674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * Get an instance of {@link MifareClassic} for the given tag. 12774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <p>Does not cause any RF activity and does not block. 12874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <p>Returns null if {@link MifareClassic} was not enumerated in {@link Tag#getTechList}. 12974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * This indicates the tag is not MIFARE Classic compatible, or this Android 13074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * device does not support MIFARE Classic. 1314e21e1d21a877cce4db5ec8c5786604cc10f2d7eJeff Hamilton * 13274fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @param tag an MIFARE Classic compatible tag 13374fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @return MIFARE Classic object 1344e21e1d21a877cce4db5ec8c5786604cc10f2d7eJeff Hamilton */ 1354e21e1d21a877cce4db5ec8c5786604cc10f2d7eJeff Hamilton public static MifareClassic get(Tag tag) { 1364e21e1d21a877cce4db5ec8c5786604cc10f2d7eJeff Hamilton if (!tag.hasTech(TagTechnology.MIFARE_CLASSIC)) return null; 1374e21e1d21a877cce4db5ec8c5786604cc10f2d7eJeff Hamilton try { 1384e21e1d21a877cce4db5ec8c5786604cc10f2d7eJeff Hamilton return new MifareClassic(tag); 1394e21e1d21a877cce4db5ec8c5786604cc10f2d7eJeff Hamilton } catch (RemoteException e) { 1404e21e1d21a877cce4db5ec8c5786604cc10f2d7eJeff Hamilton return null; 1414e21e1d21a877cce4db5ec8c5786604cc10f2d7eJeff Hamilton } 1424e21e1d21a877cce4db5ec8c5786604cc10f2d7eJeff Hamilton } 1434e21e1d21a877cce4db5ec8c5786604cc10f2d7eJeff Hamilton 144ce3224cda51f946871daa1e11e3976e25c59e6faJeff Hamilton /** @hide */ 1454e21e1d21a877cce4db5ec8c5786604cc10f2d7eJeff Hamilton public MifareClassic(Tag tag) throws RemoteException { 1464e21e1d21a877cce4db5ec8c5786604cc10f2d7eJeff Hamilton super(tag, TagTechnology.MIFARE_CLASSIC); 1476be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton 148734e9b0c73483fdaa582c21dedc24107b1fe8838Jeff Hamilton NfcA a = NfcA.get(tag); // MIFARE Classic is always based on NFC a 1496be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton 1506be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton mIsEmulated = false; 1516be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton 1526be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton switch (a.getSak()) { 15372677c98bd8231374ff60a2dd48886446de99f1fSunil Jogi case 0x01: 154e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly case 0x08: 155e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly mType = TYPE_CLASSIC; 156e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly mSize = SIZE_1K; 157e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly break; 158e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly case 0x09: 159e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly mType = TYPE_CLASSIC; 160e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly mSize = SIZE_MINI; 161e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly break; 162e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly case 0x10: 163e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly mType = TYPE_PLUS; 164e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly mSize = SIZE_2K; 165e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly // SecLevel = SL2 166e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly break; 167e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly case 0x11: 168e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly mType = TYPE_PLUS; 169e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly mSize = SIZE_4K; 170e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly // Seclevel = SL2 171e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly break; 172e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly case 0x18: 173e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly mType = TYPE_CLASSIC; 174e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly mSize = SIZE_4K; 175e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly break; 176e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly case 0x28: 177e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly mType = TYPE_CLASSIC; 178e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly mSize = SIZE_1K; 179e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly mIsEmulated = true; 180e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly break; 181e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly case 0x38: 182e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly mType = TYPE_CLASSIC; 183e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly mSize = SIZE_4K; 184e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly mIsEmulated = true; 185e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly break; 186e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly case 0x88: 187e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly mType = TYPE_CLASSIC; 188e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly mSize = SIZE_1K; 189e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly // NXP-tag: false 190e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly break; 191e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly case 0x98: 192e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly case 0xB8: 193e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly mType = TYPE_PRO; 194e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly mSize = SIZE_4K; 195e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly break; 196e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly default: 197e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly // Stack incorrectly reported a MifareClassic. We cannot handle this 198e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly // gracefully - we have no idea of the memory layout. Bail. 199e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly throw new RuntimeException( 200734e9b0c73483fdaa582c21dedc24107b1fe8838Jeff Hamilton "Tag incorrectly enumerated as MIFARE Classic, SAK = " + a.getSak()); 2016be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton } 2026be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton } 2036be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton 20474fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly /** 20574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * Return the type of this MIFARE Classic compatible tag. 20674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <p>One of {@link #TYPE_UNKNOWN}, {@link #TYPE_CLASSIC}, {@link #TYPE_PLUS} or 20774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * {@link #TYPE_PRO}. 20874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <p>Does not cause any RF activity and does not block. 20974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * 21074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @return type 21174fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly */ 2126be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton public int getType() { 2136be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton return mType; 2146be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton } 2156be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton 21674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly /** 21774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * Return the size of the tag in bytes 21874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <p>One of {@link #SIZE_MINI}, {@link #SIZE_1K}, {@link #SIZE_2K}, {@link #SIZE_4K}. 21974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * These constants are equal to their respective size in bytes. 22074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <p>Does not cause any RF activity and does not block. 22174fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @return size in bytes 22274fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly */ 223e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly public int getSize() { 224e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly return mSize; 225e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly } 226e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly 22774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly /** 22874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * Return true if the tag is emulated, determined at discovery time. 229734e9b0c73483fdaa582c21dedc24107b1fe8838Jeff Hamilton * These are actually smart-cards that emulate a MIFARE Classic interface. 230734e9b0c73483fdaa582c21dedc24107b1fe8838Jeff Hamilton * They can be treated identically to a MIFARE Classic tag. 231e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly * @hide 232e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly */ 2336be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton public boolean isEmulated() { 2346be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton return mIsEmulated; 2356be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton } 2366be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton 23774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly /** 23874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * Return the number of MIFARE Classic sectors. 23974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <p>Does not cause any RF activity and does not block. 24074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @return number of sectors 24174fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly */ 2426be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton public int getSectorCount() { 2436be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton switch (mSize) { 244e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly case SIZE_1K: 245e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly return 16; 246e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly case SIZE_2K: 247e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly return 32; 248e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly case SIZE_4K: 249e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly return 40; 250e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly case SIZE_MINI: 251e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly return 5; 252e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly default: 253e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly return 0; 2546be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton } 2556be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton } 2566be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton 25774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly /** 25874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * Return the total number of MIFARE Classic blocks. 25974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <p>Does not cause any RF activity and does not block. 26074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @return total number of blocks 26146797ac098e90cbef5c266b75fb37fc06e9acc80Nick Pelly */ 262e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly public int getBlockCount() { 263e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly return mSize / BLOCK_SIZE; 264a42b352594edf959302f8fc98041e76adeb6a20dMartijn Coenen } 265a42b352594edf959302f8fc98041e76adeb6a20dMartijn Coenen 26674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly /** 26774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * Return the number of blocks in the given sector. 26874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <p>Does not cause any RF activity and does not block. 26974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * 27074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @param sectorIndex index of sector, starting from 0 27174fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @return number of blocks in the sector 27274fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly */ 273e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly public int getBlockCountInSector(int sectorIndex) { 274e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly validateSector(sectorIndex); 2756be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton 276e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly if (sectorIndex < 32) { 2776be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton return 4; 2786be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton } else { 2796be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton return 16; 2806be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton } 2816be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton } 2826be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton 28374fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly /** 28474fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * Return the sector that contains a given block. 28574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <p>Does not cause any RF activity and does not block. 28674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * 28774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @param blockIndex index of block to lookup, starting from 0 28874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @return sector index that contains the block 28974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly */ 290e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly public int blockToSector(int blockIndex) { 291e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly validateBlock(blockIndex); 292e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly 293e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly if (blockIndex < 32 * 4) { 294e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly return blockIndex / 4; 295e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly } else { 296e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly return 32 + (blockIndex - 32 * 4) / 16; 297e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly } 298e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly } 299e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly 30074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly /** 30174fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * Return the first block of a given sector. 30274fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <p>Does not cause any RF activity and does not block. 30374fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * 30474fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @param sectorIndex index of sector to lookup, starting from 0 30574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @return block index of first block in sector 30674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly */ 307e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly public int sectorToBlock(int sectorIndex) { 308e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly if (sectorIndex < 32) { 309e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly return sectorIndex * 4; 3106be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton } else { 311e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly return 32 * 4 + (sectorIndex - 32) * 16; 3126be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton } 3136be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton } 3146be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton 3156be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton /** 31674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * Authenticate a sector with key A. 31774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * 31874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <p>Successful authentication of a sector with key A enables other 31974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * I/O operations on that sector. The set of operations granted by key A 32074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * key depends on the ACL bits set in that sector. For more information 32174fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * see the MIFARE Classic specification on {@see http://www.nxp.com}. 32274fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * 32374fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <p>A failed authentication attempt causes an implicit reconnection to the 32474fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * tag, so authentication to other sectors will be lost. 32574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * 32674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <p>This is an I/O operation and will block until complete. It must 32774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * not be called from the main application thread. A blocked call will be canceled with 32874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * {@link IOException} if {@link #close} is called from another thread. 32974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * 33039cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. 33139cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly * 33274fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @param sectorIndex index of sector to authenticate, starting from 0 33374fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @param key 6-byte authentication key 33474fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @return true on success, false on authentication failure 33574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @throws TagLostException if the tag leaves the field 33674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @throws IOException if there is an I/O failure, or the operation is canceled 337e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly */ 338e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly public boolean authenticateSectorWithKeyA(int sectorIndex, byte[] key) throws IOException { 339e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly return authenticate(sectorIndex, key, true); 340e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly } 341e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly 342e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly /** 34374fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * Authenticate a sector with key B. 34474fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * 34574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <p>Successful authentication of a sector with key B enables other 34674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * I/O operations on that sector. The set of operations granted by key B 34774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * depends on the ACL bits set in that sector. For more information 34874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * see the MIFARE Classic specification on {@see http://www.nxp.com}. 34974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * 35074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <p>A failed authentication attempt causes an implicit reconnection to the 35174fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * tag, so authentication to other sectors will be lost. 35274fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * 35374fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <p>This is an I/O operation and will block until complete. It must 35474fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * not be called from the main application thread. A blocked call will be canceled with 35574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * {@link IOException} if {@link #close} is called from another thread. 35674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * 35739cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. 35839cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly * 35974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @param sectorIndex index of sector to authenticate, starting from 0 36074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @param key 6-byte authentication key 36174fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @return true on success, false on authentication failure 36274fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @throws TagLostException if the tag leaves the field 36374fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @throws IOException if there is an I/O failure, or the operation is canceled 3646be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton */ 365e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly public boolean authenticateSectorWithKeyB(int sectorIndex, byte[] key) throws IOException { 366e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly return authenticate(sectorIndex, key, false); 367e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly } 368e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly 369e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly private boolean authenticate(int sector, byte[] key, boolean keyA) throws IOException { 370e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly validateSector(sector); 3714049f9d00a86f848d42d2429068496b31a6795adMartijn Coenen checkConnected(); 3724049f9d00a86f848d42d2429068496b31a6795adMartijn Coenen 3736be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton byte[] cmd = new byte[12]; 3746be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton 3756be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton // First byte is the command 3766be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton if (keyA) { 3776be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton cmd[0] = 0x60; // phHal_eMifareAuthentA 3786be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton } else { 3796be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton cmd[0] = 0x61; // phHal_eMifareAuthentB 3806be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton } 3816be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton 3826be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton // Second byte is block address 383e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly // Authenticate command takes a block address. Authenticating a block 384e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly // of a sector will authenticate the entire sector. 385e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly cmd[1] = (byte) sectorToBlock(sector); 3866be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton 3876be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton // Next 4 bytes are last 4 bytes of UID 3886be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton byte[] uid = getTag().getId(); 3896be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton System.arraycopy(uid, uid.length - 4, cmd, 2, 4); 3906be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton 3916be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton // Next 6 bytes are key 3926be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton System.arraycopy(key, 0, cmd, 6, 6); 3936be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton 3946be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton try { 395e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly if (transceive(cmd, false) != null) { 3966be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton return true; 3976be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton } 398bf34061bb4af12aa9efaab653ae413f2bce4a240Martijn Coenen } catch (TagLostException e) { 399bf34061bb4af12aa9efaab653ae413f2bce4a240Martijn Coenen throw e; 4006be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton } catch (IOException e) { 4016be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton // No need to deal with, will return false anyway 4026be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton } 4036be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton return false; 4046be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton } 4056be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton 4066be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton /** 407e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly * Read 16-byte block. 40874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * 40974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <p>This is an I/O operation and will block until complete. It must 41074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * not be called from the main application thread. A blocked call will be canceled with 41174fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * {@link IOException} if {@link #close} is called from another thread. 41274fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * 41339cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. 41439cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly * 41574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @param blockIndex index of block to read, starting from 0 41674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @return 16 byte block 41774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @throws TagLostException if the tag leaves the field 41874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @throws IOException if there is an I/O failure, or the operation is canceled 419ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen */ 420e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly public byte[] readBlock(int blockIndex) throws IOException { 421e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly validateBlock(blockIndex); 422ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen checkConnected(); 423ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen 424e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly byte[] cmd = { 0x30, (byte) blockIndex }; 425e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly return transceive(cmd, false); 426ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen } 427ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen 428ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen /** 429e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly * Write 16-byte block. 43074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * 43174fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <p>This is an I/O operation and will block until complete. It must 43274fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * not be called from the main application thread. A blocked call will be canceled with 43374fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * {@link IOException} if {@link #close} is called from another thread. 43474fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * 43539cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. 43639cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly * 43774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @param blockIndex index of block to write, starting from 0 43874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @param data 16 bytes of data to write 43974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @throws TagLostException if the tag leaves the field 44074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @throws IOException if there is an I/O failure, or the operation is canceled 4416be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton */ 442e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly public void writeBlock(int blockIndex, byte[] data) throws IOException { 443e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly validateBlock(blockIndex); 4444049f9d00a86f848d42d2429068496b31a6795adMartijn Coenen checkConnected(); 445e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly if (data.length != 16) { 446e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly throw new IllegalArgumentException("must write 16-bytes"); 447e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly } 4484049f9d00a86f848d42d2429068496b31a6795adMartijn Coenen 449e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly byte[] cmd = new byte[data.length + 2]; 450e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly cmd[0] = (byte) 0xA0; // MF write command 451e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly cmd[1] = (byte) blockIndex; 452e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly System.arraycopy(data, 0, cmd, 2, data.length); 453e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly 454e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly transceive(cmd, false); 455ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen } 456ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen 457ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen /** 45874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * Increment a value block, storing the result in the temporary block on the tag. 45974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * 46074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <p>This is an I/O operation and will block until complete. It must 46174fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * not be called from the main application thread. A blocked call will be canceled with 46274fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * {@link IOException} if {@link #close} is called from another thread. 46374fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * 46439cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. 46539cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly * 46674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @param blockIndex index of block to increment, starting from 0 46774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @param value non-negative to increment by 46874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @throws TagLostException if the tag leaves the field 46974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @throws IOException if there is an I/O failure, or the operation is canceled 470ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen */ 4711e233af3a783d44843a6f2b895d00a5d3b0c29f0Nick Pelly public void increment(int blockIndex, int value) throws IOException { 472e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly validateBlock(blockIndex); 473b134223f91c8801d577cb72e92a37cb65fec717aNick Pelly validateValueOperand(value); 474ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen checkConnected(); 475ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen 4761e233af3a783d44843a6f2b895d00a5d3b0c29f0Nick Pelly ByteBuffer cmd = ByteBuffer.allocate(6); 477b134223f91c8801d577cb72e92a37cb65fec717aNick Pelly cmd.order(ByteOrder.LITTLE_ENDIAN); 4781e233af3a783d44843a6f2b895d00a5d3b0c29f0Nick Pelly cmd.put( (byte) 0xC1 ); 4791e233af3a783d44843a6f2b895d00a5d3b0c29f0Nick Pelly cmd.put( (byte) blockIndex ); 480b134223f91c8801d577cb72e92a37cb65fec717aNick Pelly cmd.putInt(value); 4816be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton 4821e233af3a783d44843a6f2b895d00a5d3b0c29f0Nick Pelly transceive(cmd.array(), false); 4836be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton } 4846be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton 485ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen /** 48674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * Decrement a value block, storing the result in the temporary block on the tag. 48774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * 48874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <p>This is an I/O operation and will block until complete. It must 48974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * not be called from the main application thread. A blocked call will be canceled with 49074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * {@link IOException} if {@link #close} is called from another thread. 49174fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * 49239cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. 49339cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly * 49474fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @param blockIndex index of block to decrement, starting from 0 49574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @param value non-negative to decrement by 49674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @throws TagLostException if the tag leaves the field 49774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @throws IOException if there is an I/O failure, or the operation is canceled 498ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen */ 4991e233af3a783d44843a6f2b895d00a5d3b0c29f0Nick Pelly public void decrement(int blockIndex, int value) throws IOException { 500e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly validateBlock(blockIndex); 501b134223f91c8801d577cb72e92a37cb65fec717aNick Pelly validateValueOperand(value); 502ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen checkConnected(); 503ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen 5041e233af3a783d44843a6f2b895d00a5d3b0c29f0Nick Pelly ByteBuffer cmd = ByteBuffer.allocate(6); 505b134223f91c8801d577cb72e92a37cb65fec717aNick Pelly cmd.order(ByteOrder.LITTLE_ENDIAN); 5061e233af3a783d44843a6f2b895d00a5d3b0c29f0Nick Pelly cmd.put( (byte) 0xC0 ); 5071e233af3a783d44843a6f2b895d00a5d3b0c29f0Nick Pelly cmd.put( (byte) blockIndex ); 508b134223f91c8801d577cb72e92a37cb65fec717aNick Pelly cmd.putInt(value); 509ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen 5101e233af3a783d44843a6f2b895d00a5d3b0c29f0Nick Pelly transceive(cmd.array(), false); 511ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen } 5126be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton 5136be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton /** 51474fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * Copy from the temporary block to a value block. 51574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * 51674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <p>This is an I/O operation and will block until complete. It must 51774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * not be called from the main application thread. A blocked call will be canceled with 51874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * {@link IOException} if {@link #close} is called from another thread. 51974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * 52039cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. 52139cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly * 52274fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @param blockIndex index of block to copy to 52374fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @throws TagLostException if the tag leaves the field 52474fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @throws IOException if there is an I/O failure, or the operation is canceled 5256be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton */ 526e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly public void transfer(int blockIndex) throws IOException { 527e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly validateBlock(blockIndex); 528a42b352594edf959302f8fc98041e76adeb6a20dMartijn Coenen checkConnected(); 529a42b352594edf959302f8fc98041e76adeb6a20dMartijn Coenen 530e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly byte[] cmd = { (byte) 0xB0, (byte) blockIndex }; 531a42b352594edf959302f8fc98041e76adeb6a20dMartijn Coenen 532e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly transceive(cmd, false); 533a42b352594edf959302f8fc98041e76adeb6a20dMartijn Coenen } 534a42b352594edf959302f8fc98041e76adeb6a20dMartijn Coenen 535e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly /** 53674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * Copy from a value block to the temporary block. 53774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * 53874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <p>This is an I/O operation and will block until complete. It must 53974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * not be called from the main application thread. A blocked call will be canceled with 54074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * {@link IOException} if {@link #close} is called from another thread. 54174fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * 54239cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. 54339cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly * 54474fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @param blockIndex index of block to copy from 54574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @throws TagLostException if the tag leaves the field 54674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @throws IOException if there is an I/O failure, or the operation is canceled 547e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly */ 548e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly public void restore(int blockIndex) throws IOException { 549e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly validateBlock(blockIndex); 550a42b352594edf959302f8fc98041e76adeb6a20dMartijn Coenen checkConnected(); 551a42b352594edf959302f8fc98041e76adeb6a20dMartijn Coenen 552e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly byte[] cmd = { (byte) 0xC2, (byte) blockIndex }; 553a42b352594edf959302f8fc98041e76adeb6a20dMartijn Coenen 554e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly transceive(cmd, false); 555fc5a3b6cfb85679e82a39730c7154b55b0711a0cMartijn Coenen } 556ce3224cda51f946871daa1e11e3976e25c59e6faJeff Hamilton 557ce3224cda51f946871daa1e11e3976e25c59e6faJeff Hamilton /** 558ce3224cda51f946871daa1e11e3976e25c59e6faJeff Hamilton * Send raw NfcA data to a tag and receive the response. 559ce3224cda51f946871daa1e11e3976e25c59e6faJeff Hamilton * 56074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <p>This is equivalent to connecting to this tag via {@link NfcA} 56174fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * and calling {@link NfcA#transceive}. Note that all MIFARE Classic 56274fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * tags are based on {@link NfcA} technology. 56374fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * 564faca12adc62d148505fadfd286e6a2752c197fa0Martijn Coenen * <p>Use {@link #getMaxTransceiveLength} to retrieve the maximum number of bytes 565faca12adc62d148505fadfd286e6a2752c197fa0Martijn Coenen * that can be sent with {@link #transceive}. 566faca12adc62d148505fadfd286e6a2752c197fa0Martijn Coenen * 56774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * <p>This is an I/O operation and will block until complete. It must 56874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * not be called from the main application thread. A blocked call will be canceled with 56974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * {@link IOException} if {@link #close} is called from another thread. 57074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * 57139cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. 57239cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly * 57374fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly * @see NfcA#transceive 574ce3224cda51f946871daa1e11e3976e25c59e6faJeff Hamilton */ 575ce3224cda51f946871daa1e11e3976e25c59e6faJeff Hamilton public byte[] transceive(byte[] data) throws IOException { 576ce3224cda51f946871daa1e11e3976e25c59e6faJeff Hamilton return transceive(data, true); 577ce3224cda51f946871daa1e11e3976e25c59e6faJeff Hamilton } 578e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly 579112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen /** 580faca12adc62d148505fadfd286e6a2752c197fa0Martijn Coenen * Return the maximum number of bytes that can be sent with {@link #transceive}. 581faca12adc62d148505fadfd286e6a2752c197fa0Martijn Coenen * @return the maximum number of bytes that can be sent with {@link #transceive}. 582faca12adc62d148505fadfd286e6a2752c197fa0Martijn Coenen */ 583faca12adc62d148505fadfd286e6a2752c197fa0Martijn Coenen public int getMaxTransceiveLength() { 584faca12adc62d148505fadfd286e6a2752c197fa0Martijn Coenen return getMaxTransceiveLengthInternal(); 585faca12adc62d148505fadfd286e6a2752c197fa0Martijn Coenen } 586faca12adc62d148505fadfd286e6a2752c197fa0Martijn Coenen 587faca12adc62d148505fadfd286e6a2752c197fa0Martijn Coenen /** 58882328bfd40008d85917cc01a1b2eb8eed1f23ec4Nick Pelly * Set the {@link #transceive} timeout in milliseconds. 58982328bfd40008d85917cc01a1b2eb8eed1f23ec4Nick Pelly * 59082328bfd40008d85917cc01a1b2eb8eed1f23ec4Nick Pelly * <p>The timeout only applies to {@link #transceive} on this object, 591112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen * and is reset to a default value when {@link #close} is called. 59282328bfd40008d85917cc01a1b2eb8eed1f23ec4Nick Pelly * 593112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen * <p>Setting a longer timeout may be useful when performing 594112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen * transactions that require a long processing time on the tag 595112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen * such as key generation. 596112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen * 597112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. 598112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen * 599112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen * @param timeout timeout value in milliseconds 600112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen */ 601112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen public void setTimeout(int timeout) { 602112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen try { 603112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen int err = mTag.getTagService().setTimeout(TagTechnology.MIFARE_CLASSIC, timeout); 604112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen if (err != ErrorCodes.SUCCESS) { 605112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen throw new IllegalArgumentException("The supplied timeout is not valid"); 606112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen } 607112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen } catch (RemoteException e) { 608112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen Log.e(TAG, "NFC service dead", e); 609112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen } 610112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen } 611112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen 61220e62c9f1466ace5771e244f03a995dc0939b11bMartijn Coenen /** 61382328bfd40008d85917cc01a1b2eb8eed1f23ec4Nick Pelly * Get the current {@link #transceive} timeout in milliseconds. 61420e62c9f1466ace5771e244f03a995dc0939b11bMartijn Coenen * 61520e62c9f1466ace5771e244f03a995dc0939b11bMartijn Coenen * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. 61620e62c9f1466ace5771e244f03a995dc0939b11bMartijn Coenen * 61720e62c9f1466ace5771e244f03a995dc0939b11bMartijn Coenen * @return timeout value in milliseconds 61820e62c9f1466ace5771e244f03a995dc0939b11bMartijn Coenen */ 61920e62c9f1466ace5771e244f03a995dc0939b11bMartijn Coenen public int getTimeout() { 62020e62c9f1466ace5771e244f03a995dc0939b11bMartijn Coenen try { 62120e62c9f1466ace5771e244f03a995dc0939b11bMartijn Coenen return mTag.getTagService().getTimeout(TagTechnology.MIFARE_CLASSIC); 62220e62c9f1466ace5771e244f03a995dc0939b11bMartijn Coenen } catch (RemoteException e) { 62320e62c9f1466ace5771e244f03a995dc0939b11bMartijn Coenen Log.e(TAG, "NFC service dead", e); 62420e62c9f1466ace5771e244f03a995dc0939b11bMartijn Coenen return 0; 62520e62c9f1466ace5771e244f03a995dc0939b11bMartijn Coenen } 62620e62c9f1466ace5771e244f03a995dc0939b11bMartijn Coenen } 62720e62c9f1466ace5771e244f03a995dc0939b11bMartijn Coenen 6284a5e2532205252e0b8616ebc07ca089fd3721681Nick Pelly private static void validateSector(int sector) { 629e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly // Do not be too strict on upper bounds checking, since some cards 630e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly // have more addressable memory than they report. For example, 631734e9b0c73483fdaa582c21dedc24107b1fe8838Jeff Hamilton // MIFARE Plus 2k cards will appear as MIFARE Classic 1k cards when in 632734e9b0c73483fdaa582c21dedc24107b1fe8838Jeff Hamilton // MIFARE Classic compatibility mode. 633e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly // Note that issuing a command to an out-of-bounds block is safe - the 634e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly // tag should report error causing IOException. This validation is a 635e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly // helper to guard against obvious programming mistakes. 636e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly if (sector < 0 || sector >= MAX_SECTOR_COUNT) { 637e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly throw new IndexOutOfBoundsException("sector out of bounds: " + sector); 638e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly } 639e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly } 640e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly 6414a5e2532205252e0b8616ebc07ca089fd3721681Nick Pelly private static void validateBlock(int block) { 642e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly // Just looking for obvious out of bounds... 643e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly if (block < 0 || block >= MAX_BLOCK_COUNT) { 644e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly throw new IndexOutOfBoundsException("block out of bounds: " + block); 645e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly } 646e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly } 6474a5e2532205252e0b8616ebc07ca089fd3721681Nick Pelly 6484a5e2532205252e0b8616ebc07ca089fd3721681Nick Pelly private static void validateValueOperand(int value) { 6494a5e2532205252e0b8616ebc07ca089fd3721681Nick Pelly if (value < 0) { 6504a5e2532205252e0b8616ebc07ca089fd3721681Nick Pelly throw new IllegalArgumentException("value operand negative"); 6514a5e2532205252e0b8616ebc07ca089fd3721681Nick Pelly } 6524a5e2532205252e0b8616ebc07ca089fd3721681Nick Pelly } 6536be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton} 654