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