Home | History | Annotate | Download | only in tech
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.nfc.tech;
     18 
     19 import android.nfc.ErrorCodes;
     20 import android.nfc.Tag;
     21 import android.nfc.TagLostException;
     22 import android.os.RemoteException;
     23 import android.util.Log;
     24 
     25 import java.io.IOException;
     26 import java.nio.ByteBuffer;
     27 import java.nio.ByteOrder;
     28 
     29 /**
     30  * Provides access to MIFARE Classic properties and I/O operations on a {@link Tag}.
     31  *
     32  * <p>Acquire a {@link MifareClassic} object using {@link #get}.
     33  *
     34  * <p>MIFARE Classic is also known as MIFARE Standard.
     35  * <p>MIFARE Classic tags are divided into sectors, and each sector is sub-divided into
     36  * blocks. Block size is always 16 bytes ({@link #BLOCK_SIZE}. Sector size varies.
     37  * <ul>
     38  * <li>MIFARE Classic Mini are 320 bytes ({@link #SIZE_MINI}), with 5 sectors each of 4 blocks.
     39  * <li>MIFARE Classic 1k are 1024 bytes ({@link #SIZE_1K}), with 16 sectors each of 4 blocks.
     40  * <li>MIFARE Classic 2k are 2048 bytes ({@link #SIZE_2K}), with 32 sectors each of 4 blocks.
     41  * <li>MIFARE Classic 4k} are 4096 bytes ({@link #SIZE_4K}). The first 32 sectors contain 4 blocks
     42  * and the last 8 sectors contain 16 blocks.
     43  * </ul>
     44  *
     45  * <p>MIFARE Classic tags require authentication on a per-sector basis before any
     46  * other I/O operations on that sector can be performed. There are two keys per sector,
     47  * and ACL bits determine what I/O operations are allowed on that sector after
     48  * authenticating with a key. {@see #authenticateSectorWithKeyA} and
     49  * {@see #authenticateSectorWithKeyB}.
     50  *
     51  * <p>Three well-known authentication keys are defined in this class:
     52  * {@link #KEY_DEFAULT}, {@link #KEY_MIFARE_APPLICATION_DIRECTORY},
     53  * {@link #KEY_NFC_FORUM}.
     54  * <ul>
     55  * <li>{@link #KEY_DEFAULT} is the default factory key for MIFARE Classic.
     56  * <li>{@link #KEY_MIFARE_APPLICATION_DIRECTORY} is the well-known key for
     57  * MIFARE Classic cards that have been formatted according to the
     58  * MIFARE Application Directory (MAD) specification.
     59  * <li>{@link #KEY_NFC_FORUM} is the well-known key for MIFARE Classic cards that
     60  * have been formatted according to the NXP specification for NDEF on MIFARE Classic.
     61  *
     62  * <p>Implementation of this class on a Android NFC device is optional.
     63  * If it is not implemented, then
     64  * {@link MifareClassic} will never be enumerated in {@link Tag#getTechList}.
     65  * If it is enumerated, then all {@link MifareClassic} I/O operations will be supported,
     66  * and {@link Ndef#MIFARE_CLASSIC} NDEF tags will also be supported. In either case,
     67  * {@link NfcA} will also be enumerated on the tag, because all MIFARE Classic tags are also
     68  * {@link NfcA}.
     69  *
     70  * <p class="note"><strong>Note:</strong> Methods that perform I/O operations
     71  * require the {@link android.Manifest.permission#NFC} permission.
     72  */
     73 public final class MifareClassic extends BasicTagTechnology {
     74     private static final String TAG = "NFC";
     75 
     76     /**
     77      * The default factory key.
     78      */
     79     public static final byte[] KEY_DEFAULT =
     80             {(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF};
     81     /**
     82      * The well-known key for tags formatted according to the
     83      * MIFARE Application Directory (MAD) specification.
     84      */
     85     public static final byte[] KEY_MIFARE_APPLICATION_DIRECTORY =
     86             {(byte)0xA0,(byte)0xA1,(byte)0xA2,(byte)0xA3,(byte)0xA4,(byte)0xA5};
     87     /**
     88      * The well-known key for tags formatted according to the
     89      * NDEF on MIFARE Classic specification.
     90      */
     91     public static final byte[] KEY_NFC_FORUM =
     92             {(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7};
     93 
     94     /** A MIFARE Classic compatible card of unknown type */
     95     public static final int TYPE_UNKNOWN = -1;
     96     /** A MIFARE Classic tag */
     97     public static final int TYPE_CLASSIC = 0;
     98     /** A MIFARE Plus tag */
     99     public static final int TYPE_PLUS = 1;
    100     /** A MIFARE Pro tag */
    101     public static final int TYPE_PRO = 2;
    102 
    103     /** Tag contains 16 sectors, each with 4 blocks. */
    104     public static final int SIZE_1K = 1024;
    105     /** Tag contains 32 sectors, each with 4 blocks. */
    106     public static final int SIZE_2K = 2048;
    107     /**
    108      * Tag contains 40 sectors. The first 32 sectors contain 4 blocks and the last 8 sectors
    109      * contain 16 blocks.
    110      */
    111     public static final int SIZE_4K = 4096;
    112     /** Tag contains 5 sectors, each with 4 blocks. */
    113     public static final int SIZE_MINI = 320;
    114 
    115     /** Size of a MIFARE Classic block (in bytes) */
    116     public static final int BLOCK_SIZE = 16;
    117 
    118     private static final int MAX_BLOCK_COUNT = 256;
    119     private static final int MAX_SECTOR_COUNT = 40;
    120 
    121     private boolean mIsEmulated;
    122     private int mType;
    123     private int mSize;
    124 
    125     /**
    126      * Get an instance of {@link MifareClassic} for the given tag.
    127      * <p>Does not cause any RF activity and does not block.
    128      * <p>Returns null if {@link MifareClassic} was not enumerated in {@link Tag#getTechList}.
    129      * This indicates the tag is not MIFARE Classic compatible, or this Android
    130      * device does not support MIFARE Classic.
    131      *
    132      * @param tag an MIFARE Classic compatible tag
    133      * @return MIFARE Classic object
    134      */
    135     public static MifareClassic get(Tag tag) {
    136         if (!tag.hasTech(TagTechnology.MIFARE_CLASSIC)) return null;
    137         try {
    138             return new MifareClassic(tag);
    139         } catch (RemoteException e) {
    140             return null;
    141         }
    142     }
    143 
    144     /** @hide */
    145     public MifareClassic(Tag tag) throws RemoteException {
    146         super(tag, TagTechnology.MIFARE_CLASSIC);
    147 
    148         NfcA a = NfcA.get(tag);  // MIFARE Classic is always based on NFC a
    149 
    150         mIsEmulated = false;
    151 
    152         switch (a.getSak()) {
    153         case 0x01:
    154         case 0x08:
    155             mType = TYPE_CLASSIC;
    156             mSize = SIZE_1K;
    157             break;
    158         case 0x09:
    159             mType = TYPE_CLASSIC;
    160             mSize = SIZE_MINI;
    161             break;
    162         case 0x10:
    163             mType = TYPE_PLUS;
    164             mSize = SIZE_2K;
    165             // SecLevel = SL2
    166             break;
    167         case 0x11:
    168             mType = TYPE_PLUS;
    169             mSize = SIZE_4K;
    170             // Seclevel = SL2
    171             break;
    172         case 0x18:
    173             mType = TYPE_CLASSIC;
    174             mSize = SIZE_4K;
    175             break;
    176         case 0x28:
    177             mType = TYPE_CLASSIC;
    178             mSize = SIZE_1K;
    179             mIsEmulated = true;
    180             break;
    181         case 0x38:
    182             mType = TYPE_CLASSIC;
    183             mSize = SIZE_4K;
    184             mIsEmulated = true;
    185             break;
    186         case 0x88:
    187             mType = TYPE_CLASSIC;
    188             mSize = SIZE_1K;
    189             // NXP-tag: false
    190             break;
    191         case 0x98:
    192         case 0xB8:
    193             mType = TYPE_PRO;
    194             mSize = SIZE_4K;
    195             break;
    196         default:
    197             // Stack incorrectly reported a MifareClassic. We cannot handle this
    198             // gracefully - we have no idea of the memory layout. Bail.
    199             throw new RuntimeException(
    200                     "Tag incorrectly enumerated as MIFARE Classic, SAK = " + a.getSak());
    201         }
    202     }
    203 
    204     /**
    205      * Return the type of this MIFARE Classic compatible tag.
    206      * <p>One of {@link #TYPE_UNKNOWN}, {@link #TYPE_CLASSIC}, {@link #TYPE_PLUS} or
    207      * {@link #TYPE_PRO}.
    208      * <p>Does not cause any RF activity and does not block.
    209      *
    210      * @return type
    211      */
    212     public int getType() {
    213         return mType;
    214     }
    215 
    216     /**
    217      * Return the size of the tag in bytes
    218      * <p>One of {@link #SIZE_MINI}, {@link #SIZE_1K}, {@link #SIZE_2K}, {@link #SIZE_4K}.
    219      * These constants are equal to their respective size in bytes.
    220      * <p>Does not cause any RF activity and does not block.
    221      * @return size in bytes
    222      */
    223     public int getSize() {
    224         return mSize;
    225     }
    226 
    227     /**
    228      * Return true if the tag is emulated, determined at discovery time.
    229      * These are actually smart-cards that emulate a MIFARE Classic interface.
    230      * They can be treated identically to a MIFARE Classic tag.
    231      * @hide
    232      */
    233     public boolean isEmulated() {
    234         return mIsEmulated;
    235     }
    236 
    237     /**
    238      * Return the number of MIFARE Classic sectors.
    239      * <p>Does not cause any RF activity and does not block.
    240      * @return number of sectors
    241      */
    242     public int getSectorCount() {
    243         switch (mSize) {
    244         case SIZE_1K:
    245             return 16;
    246         case SIZE_2K:
    247             return 32;
    248         case SIZE_4K:
    249             return 40;
    250         case SIZE_MINI:
    251             return 5;
    252         default:
    253             return 0;
    254         }
    255     }
    256 
    257     /**
    258      * Return the total number of MIFARE Classic blocks.
    259      * <p>Does not cause any RF activity and does not block.
    260      * @return total number of blocks
    261      */
    262     public int getBlockCount() {
    263         return mSize / BLOCK_SIZE;
    264     }
    265 
    266     /**
    267      * Return the number of blocks in the given sector.
    268      * <p>Does not cause any RF activity and does not block.
    269      *
    270      * @param sectorIndex index of sector, starting from 0
    271      * @return number of blocks in the sector
    272      */
    273     public int getBlockCountInSector(int sectorIndex) {
    274         validateSector(sectorIndex);
    275 
    276         if (sectorIndex < 32) {
    277             return 4;
    278         } else {
    279             return 16;
    280         }
    281     }
    282 
    283     /**
    284      * Return the sector that contains a given block.
    285      * <p>Does not cause any RF activity and does not block.
    286      *
    287      * @param blockIndex index of block to lookup, starting from 0
    288      * @return sector index that contains the block
    289      */
    290     public int blockToSector(int blockIndex) {
    291         validateBlock(blockIndex);
    292 
    293         if (blockIndex < 32 * 4) {
    294             return blockIndex / 4;
    295         } else {
    296             return 32 + (blockIndex - 32 * 4) / 16;
    297         }
    298     }
    299 
    300     /**
    301      * Return the first block of a given sector.
    302      * <p>Does not cause any RF activity and does not block.
    303      *
    304      * @param sectorIndex index of sector to lookup, starting from 0
    305      * @return block index of first block in sector
    306      */
    307     public int sectorToBlock(int sectorIndex) {
    308         if (sectorIndex < 32) {
    309             return sectorIndex * 4;
    310         } else {
    311             return 32 * 4 + (sectorIndex - 32) * 16;
    312         }
    313     }
    314 
    315     /**
    316      * Authenticate a sector with key A.
    317      *
    318      * <p>Successful authentication of a sector with key A enables other
    319      * I/O operations on that sector. The set of operations granted by key A
    320      * key depends on the ACL bits set in that sector. For more information
    321      * see the MIFARE Classic specification on {@see http://www.nxp.com}.
    322      *
    323      * <p>A failed authentication attempt causes an implicit reconnection to the
    324      * tag, so authentication to other sectors will be lost.
    325      *
    326      * <p>This is an I/O operation and will block until complete. It must
    327      * not be called from the main application thread. A blocked call will be canceled with
    328      * {@link IOException} if {@link #close} is called from another thread.
    329      *
    330      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
    331      *
    332      * @param sectorIndex index of sector to authenticate, starting from 0
    333      * @param key 6-byte authentication key
    334      * @return true on success, false on authentication failure
    335      * @throws TagLostException if the tag leaves the field
    336      * @throws IOException if there is an I/O failure, or the operation is canceled
    337      */
    338     public boolean authenticateSectorWithKeyA(int sectorIndex, byte[] key) throws IOException {
    339         return authenticate(sectorIndex, key, true);
    340     }
    341 
    342     /**
    343      * Authenticate a sector with key B.
    344      *
    345      * <p>Successful authentication of a sector with key B enables other
    346      * I/O operations on that sector. The set of operations granted by key B
    347      * depends on the ACL bits set in that sector. For more information
    348      * see the MIFARE Classic specification on {@see http://www.nxp.com}.
    349      *
    350      * <p>A failed authentication attempt causes an implicit reconnection to the
    351      * tag, so authentication to other sectors will be lost.
    352      *
    353      * <p>This is an I/O operation and will block until complete. It must
    354      * not be called from the main application thread. A blocked call will be canceled with
    355      * {@link IOException} if {@link #close} is called from another thread.
    356      *
    357      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
    358      *
    359      * @param sectorIndex index of sector to authenticate, starting from 0
    360      * @param key 6-byte authentication key
    361      * @return true on success, false on authentication failure
    362      * @throws TagLostException if the tag leaves the field
    363      * @throws IOException if there is an I/O failure, or the operation is canceled
    364      */
    365     public boolean authenticateSectorWithKeyB(int sectorIndex, byte[] key) throws IOException {
    366         return authenticate(sectorIndex, key, false);
    367     }
    368 
    369     private boolean authenticate(int sector, byte[] key, boolean keyA) throws IOException {
    370         validateSector(sector);
    371         checkConnected();
    372 
    373         byte[] cmd = new byte[12];
    374 
    375         // First byte is the command
    376         if (keyA) {
    377             cmd[0] = 0x60; // phHal_eMifareAuthentA
    378         } else {
    379             cmd[0] = 0x61; // phHal_eMifareAuthentB
    380         }
    381 
    382         // Second byte is block address
    383         // Authenticate command takes a block address. Authenticating a block
    384         // of a sector will authenticate the entire sector.
    385         cmd[1] = (byte) sectorToBlock(sector);
    386 
    387         // Next 4 bytes are last 4 bytes of UID
    388         byte[] uid = getTag().getId();
    389         System.arraycopy(uid, uid.length - 4, cmd, 2, 4);
    390 
    391         // Next 6 bytes are key
    392         System.arraycopy(key, 0, cmd, 6, 6);
    393 
    394         try {
    395             if (transceive(cmd, false) != null) {
    396                 return true;
    397             }
    398         } catch (TagLostException e) {
    399             throw e;
    400         } catch (IOException e) {
    401             // No need to deal with, will return false anyway
    402         }
    403         return false;
    404     }
    405 
    406     /**
    407      * Read 16-byte block.
    408      *
    409      * <p>This is an I/O operation and will block until complete. It must
    410      * not be called from the main application thread. A blocked call will be canceled with
    411      * {@link IOException} if {@link #close} is called from another thread.
    412      *
    413      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
    414      *
    415      * @param blockIndex index of block to read, starting from 0
    416      * @return 16 byte block
    417      * @throws TagLostException if the tag leaves the field
    418      * @throws IOException if there is an I/O failure, or the operation is canceled
    419      */
    420     public byte[] readBlock(int blockIndex) throws IOException {
    421         validateBlock(blockIndex);
    422         checkConnected();
    423 
    424         byte[] cmd = { 0x30, (byte) blockIndex };
    425         return transceive(cmd, false);
    426     }
    427 
    428     /**
    429      * Write 16-byte block.
    430      *
    431      * <p>This is an I/O operation and will block until complete. It must
    432      * not be called from the main application thread. A blocked call will be canceled with
    433      * {@link IOException} if {@link #close} is called from another thread.
    434      *
    435      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
    436      *
    437      * @param blockIndex index of block to write, starting from 0
    438      * @param data 16 bytes of data to write
    439      * @throws TagLostException if the tag leaves the field
    440      * @throws IOException if there is an I/O failure, or the operation is canceled
    441      */
    442     public void writeBlock(int blockIndex, byte[] data) throws IOException {
    443         validateBlock(blockIndex);
    444         checkConnected();
    445         if (data.length != 16) {
    446             throw new IllegalArgumentException("must write 16-bytes");
    447         }
    448 
    449         byte[] cmd = new byte[data.length + 2];
    450         cmd[0] = (byte) 0xA0; // MF write command
    451         cmd[1] = (byte) blockIndex;
    452         System.arraycopy(data, 0, cmd, 2, data.length);
    453 
    454         transceive(cmd, false);
    455     }
    456 
    457     /**
    458      * Increment a value block, storing the result in the temporary block on the tag.
    459      *
    460      * <p>This is an I/O operation and will block until complete. It must
    461      * not be called from the main application thread. A blocked call will be canceled with
    462      * {@link IOException} if {@link #close} is called from another thread.
    463      *
    464      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
    465      *
    466      * @param blockIndex index of block to increment, starting from 0
    467      * @param value non-negative to increment by
    468      * @throws TagLostException if the tag leaves the field
    469      * @throws IOException if there is an I/O failure, or the operation is canceled
    470      */
    471     public void increment(int blockIndex, int value) throws IOException {
    472         validateBlock(blockIndex);
    473         validateValueOperand(value);
    474         checkConnected();
    475 
    476         ByteBuffer cmd = ByteBuffer.allocate(6);
    477         cmd.order(ByteOrder.LITTLE_ENDIAN);
    478         cmd.put( (byte) 0xC1 );
    479         cmd.put( (byte) blockIndex );
    480         cmd.putInt(value);
    481 
    482         transceive(cmd.array(), false);
    483     }
    484 
    485     /**
    486      * Decrement a value block, storing the result in the temporary block on the tag.
    487      *
    488      * <p>This is an I/O operation and will block until complete. It must
    489      * not be called from the main application thread. A blocked call will be canceled with
    490      * {@link IOException} if {@link #close} is called from another thread.
    491      *
    492      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
    493      *
    494      * @param blockIndex index of block to decrement, starting from 0
    495      * @param value non-negative to decrement by
    496      * @throws TagLostException if the tag leaves the field
    497      * @throws IOException if there is an I/O failure, or the operation is canceled
    498      */
    499     public void decrement(int blockIndex, int value) throws IOException {
    500         validateBlock(blockIndex);
    501         validateValueOperand(value);
    502         checkConnected();
    503 
    504         ByteBuffer cmd = ByteBuffer.allocate(6);
    505         cmd.order(ByteOrder.LITTLE_ENDIAN);
    506         cmd.put( (byte) 0xC0 );
    507         cmd.put( (byte) blockIndex );
    508         cmd.putInt(value);
    509 
    510         transceive(cmd.array(), false);
    511     }
    512 
    513     /**
    514      * Copy from the temporary block to a value block.
    515      *
    516      * <p>This is an I/O operation and will block until complete. It must
    517      * not be called from the main application thread. A blocked call will be canceled with
    518      * {@link IOException} if {@link #close} is called from another thread.
    519      *
    520      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
    521      *
    522      * @param blockIndex index of block to copy to
    523      * @throws TagLostException if the tag leaves the field
    524      * @throws IOException if there is an I/O failure, or the operation is canceled
    525      */
    526     public void transfer(int blockIndex) throws IOException {
    527         validateBlock(blockIndex);
    528         checkConnected();
    529 
    530         byte[] cmd = { (byte) 0xB0, (byte) blockIndex };
    531 
    532         transceive(cmd, false);
    533     }
    534 
    535     /**
    536      * Copy from a value block to the temporary block.
    537      *
    538      * <p>This is an I/O operation and will block until complete. It must
    539      * not be called from the main application thread. A blocked call will be canceled with
    540      * {@link IOException} if {@link #close} is called from another thread.
    541      *
    542      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
    543      *
    544      * @param blockIndex index of block to copy from
    545      * @throws TagLostException if the tag leaves the field
    546      * @throws IOException if there is an I/O failure, or the operation is canceled
    547      */
    548     public void restore(int blockIndex) throws IOException {
    549         validateBlock(blockIndex);
    550         checkConnected();
    551 
    552         byte[] cmd = { (byte) 0xC2, (byte) blockIndex };
    553 
    554         transceive(cmd, false);
    555     }
    556 
    557     /**
    558      * Send raw NfcA data to a tag and receive the response.
    559      *
    560      * <p>This is equivalent to connecting to this tag via {@link NfcA}
    561      * and calling {@link NfcA#transceive}. Note that all MIFARE Classic
    562      * tags are based on {@link NfcA} technology.
    563      *
    564      * <p>Use {@link #getMaxTransceiveLength} to retrieve the maximum number of bytes
    565      * that can be sent with {@link #transceive}.
    566      *
    567      * <p>This is an I/O operation and will block until complete. It must
    568      * not be called from the main application thread. A blocked call will be canceled with
    569      * {@link IOException} if {@link #close} is called from another thread.
    570      *
    571      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
    572      *
    573      * @see NfcA#transceive
    574      */
    575     public byte[] transceive(byte[] data) throws IOException {
    576         return transceive(data, true);
    577     }
    578 
    579     /**
    580      * Return the maximum number of bytes that can be sent with {@link #transceive}.
    581      * @return the maximum number of bytes that can be sent with {@link #transceive}.
    582      */
    583     public int getMaxTransceiveLength() {
    584         return getMaxTransceiveLengthInternal();
    585     }
    586 
    587     /**
    588      * Set the {@link #transceive} timeout in milliseconds.
    589      *
    590      * <p>The timeout only applies to {@link #transceive} on this object,
    591      * and is reset to a default value when {@link #close} is called.
    592      *
    593      * <p>Setting a longer timeout may be useful when performing
    594      * transactions that require a long processing time on the tag
    595      * such as key generation.
    596      *
    597      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
    598      *
    599      * @param timeout timeout value in milliseconds
    600      */
    601     public void setTimeout(int timeout) {
    602         try {
    603             int err = mTag.getTagService().setTimeout(TagTechnology.MIFARE_CLASSIC, timeout);
    604             if (err != ErrorCodes.SUCCESS) {
    605                 throw new IllegalArgumentException("The supplied timeout is not valid");
    606             }
    607         } catch (RemoteException e) {
    608             Log.e(TAG, "NFC service dead", e);
    609         }
    610     }
    611 
    612     /**
    613      * Get the current {@link #transceive} timeout in milliseconds.
    614      *
    615      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
    616      *
    617      * @return timeout value in milliseconds
    618      */
    619     public int getTimeout() {
    620         try {
    621             return mTag.getTagService().getTimeout(TagTechnology.MIFARE_CLASSIC);
    622         } catch (RemoteException e) {
    623             Log.e(TAG, "NFC service dead", e);
    624             return 0;
    625         }
    626     }
    627 
    628     private static void validateSector(int sector) {
    629         // Do not be too strict on upper bounds checking, since some cards
    630         // have more addressable memory than they report. For example,
    631         // MIFARE Plus 2k cards will appear as MIFARE Classic 1k cards when in
    632         // MIFARE Classic compatibility mode.
    633         // Note that issuing a command to an out-of-bounds block is safe - the
    634         // tag should report error causing IOException. This validation is a
    635         // helper to guard against obvious programming mistakes.
    636         if (sector < 0 || sector >= MAX_SECTOR_COUNT) {
    637             throw new IndexOutOfBoundsException("sector out of bounds: " + sector);
    638         }
    639     }
    640 
    641     private static void validateBlock(int block) {
    642         // Just looking for obvious out of bounds...
    643         if (block < 0 || block >= MAX_BLOCK_COUNT) {
    644             throw new IndexOutOfBoundsException("block out of bounds: " + block);
    645         }
    646     }
    647 
    648     private static void validateValueOperand(int value) {
    649         if (value < 0) {
    650             throw new IllegalArgumentException("value operand negative");
    651         }
    652     }
    653 }
    654