MifareClassic.java revision 20e62c9f1466ace5771e244f03a995dc0939b11b
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()) {
153e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        case 0x08:
154e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            mType = TYPE_CLASSIC;
155e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            mSize = SIZE_1K;
156e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            break;
157e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        case 0x09:
158e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            mType = TYPE_CLASSIC;
159e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            mSize = SIZE_MINI;
160e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            break;
161e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        case 0x10:
162e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            mType = TYPE_PLUS;
163e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            mSize = SIZE_2K;
164e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            // SecLevel = SL2
165e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            break;
166e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        case 0x11:
167e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            mType = TYPE_PLUS;
168e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            mSize = SIZE_4K;
169e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            // Seclevel = SL2
170e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            break;
171e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        case 0x18:
172e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            mType = TYPE_CLASSIC;
173e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            mSize = SIZE_4K;
174e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            break;
175e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        case 0x28:
176e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            mType = TYPE_CLASSIC;
177e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            mSize = SIZE_1K;
178e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            mIsEmulated = true;
179e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            break;
180e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        case 0x38:
181e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            mType = TYPE_CLASSIC;
182e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            mSize = SIZE_4K;
183e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            mIsEmulated = true;
184e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            break;
185e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        case 0x88:
186e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            mType = TYPE_CLASSIC;
187e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            mSize = SIZE_1K;
188e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            // NXP-tag: false
189e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            break;
190e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        case 0x98:
191e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        case 0xB8:
192e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            mType = TYPE_PRO;
193e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            mSize = SIZE_4K;
194e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            break;
195e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        default:
196e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            // Stack incorrectly reported a MifareClassic. We cannot handle this
197e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            // gracefully - we have no idea of the memory layout. Bail.
198e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            throw new RuntimeException(
199734e9b0c73483fdaa582c21dedc24107b1fe8838Jeff Hamilton                    "Tag incorrectly enumerated as MIFARE Classic, SAK = " + a.getSak());
2006be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton        }
2016be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton    }
2026be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton
20374fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly    /**
20474fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * Return the type of this MIFARE Classic compatible tag.
20574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * <p>One of {@link #TYPE_UNKNOWN}, {@link #TYPE_CLASSIC}, {@link #TYPE_PLUS} or
20674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * {@link #TYPE_PRO}.
20774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * <p>Does not cause any RF activity and does not block.
20874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     *
20974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @return type
21074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     */
2116be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton    public int getType() {
2126be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton        return mType;
2136be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton    }
2146be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton
21574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly    /**
21674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * Return the size of the tag in bytes
21774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * <p>One of {@link #SIZE_MINI}, {@link #SIZE_1K}, {@link #SIZE_2K}, {@link #SIZE_4K}.
21874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * These constants are equal to their respective size in bytes.
21974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * <p>Does not cause any RF activity and does not block.
22074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @return size in bytes
22174fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     */
222e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly    public int getSize() {
223e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        return mSize;
224e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly    }
225e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly
22674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly    /**
22774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * Return true if the tag is emulated, determined at discovery time.
228734e9b0c73483fdaa582c21dedc24107b1fe8838Jeff Hamilton     * These are actually smart-cards that emulate a MIFARE Classic interface.
229734e9b0c73483fdaa582c21dedc24107b1fe8838Jeff Hamilton     * They can be treated identically to a MIFARE Classic tag.
230e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly     * @hide
231e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly     */
2326be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton    public boolean isEmulated() {
2336be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton        return mIsEmulated;
2346be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton    }
2356be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton
23674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly    /**
23774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * Return the number of MIFARE Classic sectors.
23874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * <p>Does not cause any RF activity and does not block.
23974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @return number of sectors
24074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     */
2416be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton    public int getSectorCount() {
2426be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton        switch (mSize) {
243e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        case SIZE_1K:
244e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            return 16;
245e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        case SIZE_2K:
246e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            return 32;
247e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        case SIZE_4K:
248e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            return 40;
249e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        case SIZE_MINI:
250e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            return 5;
251e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        default:
252e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            return 0;
2536be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton        }
2546be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton    }
2556be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton
25674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly    /**
25774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * Return the total number of MIFARE Classic blocks.
25874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * <p>Does not cause any RF activity and does not block.
25974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @return total number of blocks
26046797ac098e90cbef5c266b75fb37fc06e9acc80Nick Pelly     */
261e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly    public int getBlockCount() {
262e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        return mSize / BLOCK_SIZE;
263a42b352594edf959302f8fc98041e76adeb6a20dMartijn Coenen    }
264a42b352594edf959302f8fc98041e76adeb6a20dMartijn Coenen
26574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly    /**
26674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * Return the number of blocks in the given sector.
26774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * <p>Does not cause any RF activity and does not block.
26874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     *
26974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @param sectorIndex index of sector, starting from 0
27074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @return number of blocks in the sector
27174fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     */
272e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly    public int getBlockCountInSector(int sectorIndex) {
273e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        validateSector(sectorIndex);
2746be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton
275e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        if (sectorIndex < 32) {
2766be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton            return 4;
2776be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton        } else {
2786be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton            return 16;
2796be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton        }
2806be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton    }
2816be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton
28274fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly    /**
28374fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * Return the sector that contains a given block.
28474fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * <p>Does not cause any RF activity and does not block.
28574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     *
28674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @param blockIndex index of block to lookup, starting from 0
28774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @return sector index that contains the block
28874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     */
289e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly    public int blockToSector(int blockIndex) {
290e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        validateBlock(blockIndex);
291e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly
292e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        if (blockIndex < 32 * 4) {
293e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            return blockIndex / 4;
294e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        } else {
295e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            return 32 + (blockIndex - 32 * 4) / 16;
296e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        }
297e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly    }
298e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly
29974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly    /**
30074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * Return the first block of a given sector.
30174fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * <p>Does not cause any RF activity and does not block.
30274fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     *
30374fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @param sectorIndex index of sector to lookup, starting from 0
30474fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @return block index of first block in sector
30574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     */
306e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly    public int sectorToBlock(int sectorIndex) {
307e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        if (sectorIndex < 32) {
308e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            return sectorIndex * 4;
3096be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton        } else {
310e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            return 32 * 4 + (sectorIndex - 32) * 16;
3116be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton        }
3126be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton    }
3136be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton
3146be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton    /**
31574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * Authenticate a sector with key A.
31674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     *
31774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * <p>Successful authentication of a sector with key A enables other
31874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * I/O operations on that sector. The set of operations granted by key A
31974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * key depends on the ACL bits set in that sector. For more information
32074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * see the MIFARE Classic specification on {@see http://www.nxp.com}.
32174fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     *
32274fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * <p>A failed authentication attempt causes an implicit reconnection to the
32374fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * tag, so authentication to other sectors will be lost.
32474fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     *
32574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * <p>This is an I/O operation and will block until complete. It must
32674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * not be called from the main application thread. A blocked call will be canceled with
32774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * {@link IOException} if {@link #close} is called from another thread.
32874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     *
32939cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
33039cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly     *
33174fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @param sectorIndex index of sector to authenticate, starting from 0
33274fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @param key 6-byte authentication key
33374fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @return true on success, false on authentication failure
33474fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @throws TagLostException if the tag leaves the field
33574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @throws IOException if there is an I/O failure, or the operation is canceled
336e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly     */
337e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly    public boolean authenticateSectorWithKeyA(int sectorIndex, byte[] key) throws IOException {
338e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        return authenticate(sectorIndex, key, true);
339e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly    }
340e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly
341e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly    /**
34274fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * Authenticate a sector with key B.
34374fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     *
34474fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * <p>Successful authentication of a sector with key B enables other
34574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * I/O operations on that sector. The set of operations granted by key B
34674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * depends on the ACL bits set in that sector. For more information
34774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * see the MIFARE Classic specification on {@see http://www.nxp.com}.
34874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     *
34974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * <p>A failed authentication attempt causes an implicit reconnection to the
35074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * tag, so authentication to other sectors will be lost.
35174fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     *
35274fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * <p>This is an I/O operation and will block until complete. It must
35374fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * not be called from the main application thread. A blocked call will be canceled with
35474fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * {@link IOException} if {@link #close} is called from another thread.
35574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     *
35639cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
35739cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly     *
35874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @param sectorIndex index of sector to authenticate, starting from 0
35974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @param key 6-byte authentication key
36074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @return true on success, false on authentication failure
36174fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @throws TagLostException if the tag leaves the field
36274fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @throws IOException if there is an I/O failure, or the operation is canceled
3636be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton     */
364e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly    public boolean authenticateSectorWithKeyB(int sectorIndex, byte[] key) throws IOException {
365e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        return authenticate(sectorIndex, key, false);
366e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly    }
367e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly
368e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly    private boolean authenticate(int sector, byte[] key, boolean keyA) throws IOException {
369e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        validateSector(sector);
3704049f9d00a86f848d42d2429068496b31a6795adMartijn Coenen        checkConnected();
3714049f9d00a86f848d42d2429068496b31a6795adMartijn Coenen
3726be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton        byte[] cmd = new byte[12];
3736be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton
3746be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton        // First byte is the command
3756be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton        if (keyA) {
3766be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton            cmd[0] = 0x60; // phHal_eMifareAuthentA
3776be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton        } else {
3786be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton            cmd[0] = 0x61; // phHal_eMifareAuthentB
3796be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton        }
3806be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton
3816be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton        // Second byte is block address
382e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        // Authenticate command takes a block address. Authenticating a block
383e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        // of a sector will authenticate the entire sector.
384e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        cmd[1] = (byte) sectorToBlock(sector);
3856be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton
3866be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton        // Next 4 bytes are last 4 bytes of UID
3876be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton        byte[] uid = getTag().getId();
3886be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton        System.arraycopy(uid, uid.length - 4, cmd, 2, 4);
3896be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton
3906be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton        // Next 6 bytes are key
3916be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton        System.arraycopy(key, 0, cmd, 6, 6);
3926be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton
3936be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton        try {
394e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            if (transceive(cmd, false) != null) {
3956be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton                return true;
3966be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton            }
397bf34061bb4af12aa9efaab653ae413f2bce4a240Martijn Coenen        } catch (TagLostException e) {
398bf34061bb4af12aa9efaab653ae413f2bce4a240Martijn Coenen            throw e;
3996be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton        } catch (IOException e) {
4006be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton            // No need to deal with, will return false anyway
4016be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton        }
4026be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton        return false;
4036be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton    }
4046be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton
4056be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton    /**
406e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly     * Read 16-byte block.
40774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     *
40874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * <p>This is an I/O operation and will block until complete. It must
40974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * not be called from the main application thread. A blocked call will be canceled with
41074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * {@link IOException} if {@link #close} is called from another thread.
41174fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     *
41239cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
41339cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly     *
41474fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @param blockIndex index of block to read, starting from 0
41574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @return 16 byte block
41674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @throws TagLostException if the tag leaves the field
41774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @throws IOException if there is an I/O failure, or the operation is canceled
418ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen     */
419e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly    public byte[] readBlock(int blockIndex) throws IOException {
420e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        validateBlock(blockIndex);
421ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen        checkConnected();
422ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen
423e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        byte[] cmd = { 0x30, (byte) blockIndex };
424e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        return transceive(cmd, false);
425ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen    }
426ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen
427ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen    /**
428e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly     * Write 16-byte block.
42974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     *
43074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * <p>This is an I/O operation and will block until complete. It must
43174fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * not be called from the main application thread. A blocked call will be canceled with
43274fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * {@link IOException} if {@link #close} is called from another thread.
43374fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     *
43439cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
43539cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly     *
43674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @param blockIndex index of block to write, starting from 0
43774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @param data 16 bytes of data to write
43874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @throws TagLostException if the tag leaves the field
43974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @throws IOException if there is an I/O failure, or the operation is canceled
4406be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton     */
441e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly    public void writeBlock(int blockIndex, byte[] data) throws IOException {
442e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        validateBlock(blockIndex);
4434049f9d00a86f848d42d2429068496b31a6795adMartijn Coenen        checkConnected();
444e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        if (data.length != 16) {
445e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            throw new IllegalArgumentException("must write 16-bytes");
446e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        }
4474049f9d00a86f848d42d2429068496b31a6795adMartijn Coenen
448e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        byte[] cmd = new byte[data.length + 2];
449e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        cmd[0] = (byte) 0xA0; // MF write command
450e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        cmd[1] = (byte) blockIndex;
451e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        System.arraycopy(data, 0, cmd, 2, data.length);
452e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly
453e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        transceive(cmd, false);
454ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen    }
455ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen
456ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen    /**
45774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * Increment a value block, storing the result in the temporary block on the tag.
45874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     *
45974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * <p>This is an I/O operation and will block until complete. It must
46074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * not be called from the main application thread. A blocked call will be canceled with
46174fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * {@link IOException} if {@link #close} is called from another thread.
46274fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     *
46339cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
46439cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly     *
46574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @param blockIndex index of block to increment, starting from 0
46674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @param value non-negative to increment by
46774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @throws TagLostException if the tag leaves the field
46874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @throws IOException if there is an I/O failure, or the operation is canceled
469ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen     */
4701e233af3a783d44843a6f2b895d00a5d3b0c29f0Nick Pelly    public void increment(int blockIndex, int value) throws IOException {
471e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        validateBlock(blockIndex);
472b134223f91c8801d577cb72e92a37cb65fec717aNick Pelly        validateValueOperand(value);
473ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen        checkConnected();
474ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen
4751e233af3a783d44843a6f2b895d00a5d3b0c29f0Nick Pelly        ByteBuffer cmd = ByteBuffer.allocate(6);
476b134223f91c8801d577cb72e92a37cb65fec717aNick Pelly        cmd.order(ByteOrder.LITTLE_ENDIAN);
4771e233af3a783d44843a6f2b895d00a5d3b0c29f0Nick Pelly        cmd.put( (byte) 0xC1 );
4781e233af3a783d44843a6f2b895d00a5d3b0c29f0Nick Pelly        cmd.put( (byte) blockIndex );
479b134223f91c8801d577cb72e92a37cb65fec717aNick Pelly        cmd.putInt(value);
4806be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton
4811e233af3a783d44843a6f2b895d00a5d3b0c29f0Nick Pelly        transceive(cmd.array(), false);
4826be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton    }
4836be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton
484ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen    /**
48574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * Decrement a value block, storing the result in the temporary block on the tag.
48674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     *
48774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * <p>This is an I/O operation and will block until complete. It must
48874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * not be called from the main application thread. A blocked call will be canceled with
48974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * {@link IOException} if {@link #close} is called from another thread.
49074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     *
49139cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
49239cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly     *
49374fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @param blockIndex index of block to decrement, starting from 0
49474fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @param value non-negative to decrement by
49574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @throws TagLostException if the tag leaves the field
49674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @throws IOException if there is an I/O failure, or the operation is canceled
497ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen     */
4981e233af3a783d44843a6f2b895d00a5d3b0c29f0Nick Pelly    public void decrement(int blockIndex, int value) throws IOException {
499e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        validateBlock(blockIndex);
500b134223f91c8801d577cb72e92a37cb65fec717aNick Pelly        validateValueOperand(value);
501ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen        checkConnected();
502ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen
5031e233af3a783d44843a6f2b895d00a5d3b0c29f0Nick Pelly        ByteBuffer cmd = ByteBuffer.allocate(6);
504b134223f91c8801d577cb72e92a37cb65fec717aNick Pelly        cmd.order(ByteOrder.LITTLE_ENDIAN);
5051e233af3a783d44843a6f2b895d00a5d3b0c29f0Nick Pelly        cmd.put( (byte) 0xC0 );
5061e233af3a783d44843a6f2b895d00a5d3b0c29f0Nick Pelly        cmd.put( (byte) blockIndex );
507b134223f91c8801d577cb72e92a37cb65fec717aNick Pelly        cmd.putInt(value);
508ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen
5091e233af3a783d44843a6f2b895d00a5d3b0c29f0Nick Pelly        transceive(cmd.array(), false);
510ab82a5b9a841cf052310e8500224932b9f5e3cadMartijn Coenen    }
5116be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton
5126be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton    /**
51374fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * Copy from the temporary block to a value block.
51474fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     *
51574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * <p>This is an I/O operation and will block until complete. It must
51674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * not be called from the main application thread. A blocked call will be canceled with
51774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * {@link IOException} if {@link #close} is called from another thread.
51874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     *
51939cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
52039cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly     *
52174fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @param blockIndex index of block to copy to
52274fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @throws TagLostException if the tag leaves the field
52374fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @throws IOException if there is an I/O failure, or the operation is canceled
5246be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton     */
525e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly    public void transfer(int blockIndex) throws IOException {
526e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        validateBlock(blockIndex);
527a42b352594edf959302f8fc98041e76adeb6a20dMartijn Coenen        checkConnected();
528a42b352594edf959302f8fc98041e76adeb6a20dMartijn Coenen
529e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        byte[] cmd = { (byte) 0xB0, (byte) blockIndex };
530a42b352594edf959302f8fc98041e76adeb6a20dMartijn Coenen
531e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        transceive(cmd, false);
532a42b352594edf959302f8fc98041e76adeb6a20dMartijn Coenen    }
533a42b352594edf959302f8fc98041e76adeb6a20dMartijn Coenen
534e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly    /**
53574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * Copy from a value block to the temporary block.
53674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     *
53774fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * <p>This is an I/O operation and will block until complete. It must
53874fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * not be called from the main application thread. A blocked call will be canceled with
53974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * {@link IOException} if {@link #close} is called from another thread.
54074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     *
54139cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
54239cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly     *
54374fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @param blockIndex index of block to copy from
54474fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @throws TagLostException if the tag leaves the field
54574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @throws IOException if there is an I/O failure, or the operation is canceled
546e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly     */
547e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly    public void restore(int blockIndex) throws IOException {
548e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        validateBlock(blockIndex);
549a42b352594edf959302f8fc98041e76adeb6a20dMartijn Coenen        checkConnected();
550a42b352594edf959302f8fc98041e76adeb6a20dMartijn Coenen
551e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        byte[] cmd = { (byte) 0xC2, (byte) blockIndex };
552a42b352594edf959302f8fc98041e76adeb6a20dMartijn Coenen
553e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        transceive(cmd, false);
554fc5a3b6cfb85679e82a39730c7154b55b0711a0cMartijn Coenen    }
555ce3224cda51f946871daa1e11e3976e25c59e6faJeff Hamilton
556ce3224cda51f946871daa1e11e3976e25c59e6faJeff Hamilton    /**
557ce3224cda51f946871daa1e11e3976e25c59e6faJeff Hamilton     * Send raw NfcA data to a tag and receive the response.
558ce3224cda51f946871daa1e11e3976e25c59e6faJeff Hamilton     *
55974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * <p>This is equivalent to connecting to this tag via {@link NfcA}
56074fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * and calling {@link NfcA#transceive}. Note that all MIFARE Classic
56174fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * tags are based on {@link NfcA} technology.
56274fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     *
56374fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * <p>This is an I/O operation and will block until complete. It must
56474fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * not be called from the main application thread. A blocked call will be canceled with
56574fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * {@link IOException} if {@link #close} is called from another thread.
56674fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     *
56739cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
56839cf3a445e507f219ecc8a476f6038f095d9d520Nick Pelly     *
56974fe6c6b245ebe7d3b3d96962c32980d88dca4f5Nick Pelly     * @see NfcA#transceive
570ce3224cda51f946871daa1e11e3976e25c59e6faJeff Hamilton     */
571ce3224cda51f946871daa1e11e3976e25c59e6faJeff Hamilton    public byte[] transceive(byte[] data) throws IOException {
572ce3224cda51f946871daa1e11e3976e25c59e6faJeff Hamilton        return transceive(data, true);
573ce3224cda51f946871daa1e11e3976e25c59e6faJeff Hamilton    }
574e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly
575112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen    /**
576112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen     * Set the timeout of {@link #transceive} in milliseconds.
577112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen     * <p>The timeout only applies to MifareUltralight {@link #transceive},
578112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen     * and is reset to a default value when {@link #close} is called.
579112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen     * <p>Setting a longer timeout may be useful when performing
580112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen     * transactions that require a long processing time on the tag
581112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen     * such as key generation.
582112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen     *
583112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
584112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen     *
585112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen     * @param timeout timeout value in milliseconds
586112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen     * @hide
587112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen     */
588112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen    // TODO Unhide for ICS
589112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen    public void setTimeout(int timeout) {
590112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen        try {
591112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen            int err = mTag.getTagService().setTimeout(TagTechnology.MIFARE_CLASSIC, timeout);
592112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen            if (err != ErrorCodes.SUCCESS) {
593112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen                throw new IllegalArgumentException("The supplied timeout is not valid");
594112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen            }
595112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen        } catch (RemoteException e) {
596112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen            Log.e(TAG, "NFC service dead", e);
597112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen        }
598112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen    }
599112fdf612db71a552fce063136bf2796df3b71ecMartijn Coenen
60020e62c9f1466ace5771e244f03a995dc0939b11bMartijn Coenen    /**
60120e62c9f1466ace5771e244f03a995dc0939b11bMartijn Coenen     * Gets the currently set timeout of {@link #transceive} in milliseconds.
60220e62c9f1466ace5771e244f03a995dc0939b11bMartijn Coenen     *
60320e62c9f1466ace5771e244f03a995dc0939b11bMartijn Coenen     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
60420e62c9f1466ace5771e244f03a995dc0939b11bMartijn Coenen     *
60520e62c9f1466ace5771e244f03a995dc0939b11bMartijn Coenen     * @return timeout value in milliseconds
60620e62c9f1466ace5771e244f03a995dc0939b11bMartijn Coenen     * @hide
60720e62c9f1466ace5771e244f03a995dc0939b11bMartijn Coenen     */
60820e62c9f1466ace5771e244f03a995dc0939b11bMartijn Coenen    // TODO Unhide for ICS
60920e62c9f1466ace5771e244f03a995dc0939b11bMartijn Coenen    public int getTimeout() {
61020e62c9f1466ace5771e244f03a995dc0939b11bMartijn Coenen        try {
61120e62c9f1466ace5771e244f03a995dc0939b11bMartijn Coenen            return mTag.getTagService().getTimeout(TagTechnology.MIFARE_CLASSIC);
61220e62c9f1466ace5771e244f03a995dc0939b11bMartijn Coenen        } catch (RemoteException e) {
61320e62c9f1466ace5771e244f03a995dc0939b11bMartijn Coenen            Log.e(TAG, "NFC service dead", e);
61420e62c9f1466ace5771e244f03a995dc0939b11bMartijn Coenen            return 0;
61520e62c9f1466ace5771e244f03a995dc0939b11bMartijn Coenen        }
61620e62c9f1466ace5771e244f03a995dc0939b11bMartijn Coenen    }
61720e62c9f1466ace5771e244f03a995dc0939b11bMartijn Coenen
6184a5e2532205252e0b8616ebc07ca089fd3721681Nick Pelly    private static void validateSector(int sector) {
619e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        // Do not be too strict on upper bounds checking, since some cards
620e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        // have more addressable memory than they report. For example,
621734e9b0c73483fdaa582c21dedc24107b1fe8838Jeff Hamilton        // MIFARE Plus 2k cards will appear as MIFARE Classic 1k cards when in
622734e9b0c73483fdaa582c21dedc24107b1fe8838Jeff Hamilton        // MIFARE Classic compatibility mode.
623e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        // Note that issuing a command to an out-of-bounds block is safe - the
624e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        // tag should report error causing IOException. This validation is a
625e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        // helper to guard against obvious programming mistakes.
626e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        if (sector < 0 || sector >= MAX_SECTOR_COUNT) {
627e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            throw new IndexOutOfBoundsException("sector out of bounds: " + sector);
628e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        }
629e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly    }
630e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly
6314a5e2532205252e0b8616ebc07ca089fd3721681Nick Pelly    private static void validateBlock(int block) {
632e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        // Just looking for obvious out of bounds...
633e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        if (block < 0 || block >= MAX_BLOCK_COUNT) {
634e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly            throw new IndexOutOfBoundsException("block out of bounds: " + block);
635e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly        }
636e45083b11bef915f713379fb4106dd2ebd897d03Nick Pelly    }
6374a5e2532205252e0b8616ebc07ca089fd3721681Nick Pelly
6384a5e2532205252e0b8616ebc07ca089fd3721681Nick Pelly    private static void validateValueOperand(int value) {
6394a5e2532205252e0b8616ebc07ca089fd3721681Nick Pelly        if (value < 0) {
6404a5e2532205252e0b8616ebc07ca089fd3721681Nick Pelly            throw new IllegalArgumentException("value operand negative");
6414a5e2532205252e0b8616ebc07ca089fd3721681Nick Pelly        }
6424a5e2532205252e0b8616ebc07ca089fd3721681Nick Pelly    }
6436be655c768a82716612c00fdd156254d8dc00f42Jeff Hamilton}
644