MifareUltralight.java revision 74fe6c6b245ebe7d3b3d96962c32980d88dca4f5
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 17package android.nfc.tech; 18 19import android.nfc.Tag; 20import android.nfc.TagLostException; 21import android.os.RemoteException; 22 23import java.io.IOException; 24 25//TOOD: Ultralight C 3-DES authentication, one-way counter 26 27/** 28 * Provides access to MIFARE Ultralight properties and I/O operations on a {@link Tag}. 29 * 30 * <p>Acquire a {@link MifareUltralight} object using {@link #get}. 31 * 32 * <p>MIFARE Ultralight compatible tags have 4 byte pages {@link #PAGE_SIZE}. 33 * The primary operations on an Ultralight tag are {@link #readPages} and 34 * {@link #writePage}. 35 * 36 * <p>The original MIFARE Ultralight consists of a 64 byte EEPROM. The first 37 * 4 pages are for the OTP area, manufacturer data, and locking bits. They are 38 * readable and some bits are writable. The final 12 pages are the user 39 * read/write area. For more information see the NXP data sheet MF0ICU1. 40 * 41 * <p>The MIFARE Ultralight C consists of a 192 byte EEPROM. The first 4 pages 42 * are for OTP, manufacturer data, and locking bits. The next 36 pages are the 43 * user read/write area. The next 4 pages are additional locking bits, counters 44 * and authentication configuration and are readable. The final 4 pages are for 45 * the authentication key and are not readable. For more information see the 46 * NXP data sheet MF0ICU2. 47 * 48 * <p>Implementation of this class on a Android NFC device is optional. 49 * If it is not implemented, then 50 * {@link MifareUltralight} will never be enumerated in {@link Tag#getTechList}. 51 * If it is enumerated, then all {@link MifareUltralight} I/O operations will be supported. 52 * In either case, {@link NfcA} will also be enumerated on the tag, 53 * because all MIFARE Ultralight tags are also {@link NfcA} tags. 54 */ 55public final class MifareUltralight extends BasicTagTechnology { 56 /** A MIFARE Ultralight compatible tag of unknown type */ 57 public static final int TYPE_UNKNOWN = -1; 58 /** A MIFARE Ultralight tag */ 59 public static final int TYPE_ULTRALIGHT = 1; 60 /** A MIFARE Ultralight C tag */ 61 public static final int TYPE_ULTRALIGHT_C = 2; 62 63 /** Size of a MIFARE Ultralight page in bytes */ 64 public static final int PAGE_SIZE = 4; 65 66 private static final int NXP_MANUFACTURER_ID = 0x04; 67 private static final int MAX_PAGE_COUNT = 256; 68 69 private int mType; 70 71 /** 72 * Get an instance of {@link MifareUltralight} for the given tag. 73 * <p>Returns null if {@link MifareUltralight} was not enumerated in 74 * {@link Tag#getTechList} - this indicates the tag is not MIFARE 75 * Ultralight compatible, or that this Android 76 * device does not implement MIFARE Ultralight. 77 * <p>Does not cause any RF activity and does not block. 78 * 79 * @param tag an MIFARE Ultralight compatible tag 80 * @return MIFARE Ultralight object 81 */ 82 public static MifareUltralight get(Tag tag) { 83 if (!tag.hasTech(TagTechnology.MIFARE_ULTRALIGHT)) return null; 84 try { 85 return new MifareUltralight(tag); 86 } catch (RemoteException e) { 87 return null; 88 } 89 } 90 91 /** @hide */ 92 public MifareUltralight(Tag tag) throws RemoteException { 93 super(tag, TagTechnology.MIFARE_ULTRALIGHT); 94 95 // Check if this could actually be a Mifare 96 NfcA a = NfcA.get(tag); 97 98 mType = TYPE_UNKNOWN; 99 100 if (a.getSak() == 0x00 && tag.getId()[0] == NXP_MANUFACTURER_ID) { 101 // could be UL or UL-C 102 //TODO: stack should use NXP AN1303 procedure to make a best guess 103 // attempt at classifying Ultralight vs Ultralight C. 104 mType = TYPE_ULTRALIGHT; 105 } 106 } 107 108 /** 109 * Return the MIFARE Ultralight type of the tag. 110 * <p>One of {@link #TYPE_ULTRALIGHT} or {@link #TYPE_ULTRALIGHT_C} or 111 * {@link #TYPE_UNKNOWN}. 112 * <p>Depending on how the tag has been formatted, it can be impossible 113 * to accurately classify between original MIFARE Ultralight and 114 * Ultralight C. So treat this method as a hint. 115 * <p>Does not cause any RF activity and does not block. 116 * 117 * @return the type 118 */ 119 public int getType() { 120 return mType; 121 } 122 123 /** 124 * Read 4 pages (16 bytes). 125 * 126 * <p>The MIFARE Ultralight protocol always reads 4 pages at a time, to 127 * reduce the number of commands required to read an entire tag. 128 * <p>If a read spans past the last readable block, then the tag will 129 * return pages that have been wrapped back to the first blocks. MIFARE 130 * Ultralight tags have readable blocks 0x00 through 0x0F. So a read to 131 * block offset 0x0E would return blocks 0x0E, 0x0F, 0x00, 0x01. MIFARE 132 * Ultralight C tags have readable blocks 0x00 through 0x2B. So a read to 133 * block 0x2A would return blocks 0x2A, 0x2B, 0x00, 0x01. 134 * 135 * <p>This is an I/O operation and will block until complete. It must 136 * not be called from the main application thread. A blocked call will be canceled with 137 * {@link IOException} if {@link #close} is called from another thread. 138 * 139 * @param pageIndex index of first page to read, starting from 0 140 * @return 4 pages (16 bytes) 141 * @throws TagLostException if the tag leaves the field 142 * @throws IOException if there is an I/O failure, or the operation is canceled 143 */ 144 public byte[] readPages(int pageIndex) throws IOException { 145 validatePageIndex(pageIndex); 146 checkConnected(); 147 148 byte[] cmd = { 0x30, (byte) pageIndex}; 149 return transceive(cmd, false); 150 } 151 152 /** 153 * Write 1 page (4 bytes). 154 * 155 * <p>The MIFARE Ultralight protocol always writes 1 page at a time, to 156 * minimize EEPROM write cycles. 157 * 158 * <p>This is an I/O operation and will block until complete. It must 159 * not be called from the main application thread. A blocked call will be canceled with 160 * {@link IOException} if {@link #close} is called from another thread. 161 * 162 * @param pageIndex index of page to write, starting from 0 163 * @param data 4 bytes to write 164 * @throws TagLostException if the tag leaves the field 165 * @throws IOException if there is an I/O failure, or the operation is canceled 166 */ 167 public void writePage(int pageIndex, byte[] data) throws IOException { 168 validatePageIndex(pageIndex); 169 checkConnected(); 170 171 byte[] cmd = new byte[data.length + 2]; 172 cmd[0] = (byte) 0xA2; 173 cmd[1] = (byte) pageIndex; 174 System.arraycopy(data, 0, cmd, 2, data.length); 175 176 transceive(cmd, false); 177 } 178 179 /** 180 * Send raw NfcA data to a tag and receive the response. 181 * 182 * <p>This is equivalent to connecting to this tag via {@link NfcA} 183 * and calling {@link NfcA#transceive}. Note that all MIFARE Classic 184 * tags are based on {@link NfcA} technology. 185 * 186 * <p>This is an I/O operation and will block until complete. It must 187 * not be called from the main application thread. A blocked call will be canceled with 188 * {@link IOException} if {@link #close} is called from another thread. 189 * 190 * @see NfcA#transceive 191 */ 192 public byte[] transceive(byte[] data) throws IOException { 193 return transceive(data, true); 194 } 195 196 private static void validatePageIndex(int pageIndex) { 197 // Do not be too strict on upper bounds checking, since some cards 198 // may have more addressable memory than they report. 199 // Note that issuing a command to an out-of-bounds block is safe - the 200 // tag will wrap the read to an addressable area. This validation is a 201 // helper to guard against obvious programming mistakes. 202 if (pageIndex < 0 || pageIndex >= MAX_PAGE_COUNT) { 203 throw new IndexOutOfBoundsException("page out of bounds: " + pageIndex); 204 } 205 } 206} 207