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