2121
2222import java .io .IOException ;
2323
24+ //TOOD: Ultralight C 3-DES authentication, one-way counter
25+
2426/**
2527 * Technology class representing MIFARE Ultralight and MIFARE Ultralight C tags.
2628 *
2729 * <p>Support for this technology type is optional. If the NFC stack doesn't support this technology
2830 * MIFARE Ultralight class tags will still be scanned, but will only show the NfcA technology.
2931 *
30- * <p>MIFARE Ultralight class tags have a series of 4 bytes pages that can be individually written
31- * and read in chunks of 4 for a total read of 16 bytes.
32+ * <p>MIFARE Ultralight compatible tags have 4 byte pages. The read command
33+ * returns 4 pages (16 bytes) at a time, for speed. The write command operates
34+ * on a single page (4 bytes) to minimize EEPROM write cycles.
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.
3247 */
3348public final class MifareUltralight extends BasicTagTechnology {
49+ /** A MIFARE Ultralight compatible tag of unknown type */
50+ public static final int TYPE_UNKNOWN = -1 ;
3451 /** A MIFARE Ultralight tag */
3552 public static final int TYPE_ULTRALIGHT = 1 ;
3653 /** A MIFARE Ultralight C tag */
3754 public static final int TYPE_ULTRALIGHT_C = 2 ;
38- /** The tag type is unknown */
39- public static final int TYPE_UNKNOWN = 10 ;
55+
56+ /** Size of a MIFARE Ultralight page in bytes */
57+ public static final int PAGE_SIZE = 4 ;
4058
4159 private static final int NXP_MANUFACTURER_ID = 0x04 ;
60+ private static final int MAX_PAGE_COUNT = 256 ;
4261
4362 private int mType ;
4463
@@ -68,48 +87,62 @@ public MifareUltralight(Tag tag) throws RemoteException {
6887
6988 if (a .getSak () == 0x00 && tag .getId ()[0 ] == NXP_MANUFACTURER_ID ) {
7089 // could be UL or UL-C
90+ //TODO: stack should use NXP AN1303 procedure to make a best guess
91+ // attempt at classifying Ultralight vs Ultralight C.
7192 mType = TYPE_ULTRALIGHT ;
7293 }
7394 }
7495
75- /** Returns the type of the tag */
96+ /** Returns the type of the tag.
97+ * <p>It is very hard to always accurately classify a MIFARE Ultralight
98+ * compatible tag as Ultralight original or Ultralight C. So consider
99+ * {@link #getType} a hint. */
76100 public int getType () {
77101 return mType ;
78102 }
79103
80104 // Methods that require connect()
81105 /**
82- * Reads a single 16 byte block from the given page offset.
83- *
84- * <p>This requires a that the tag be connected.
106+ * Read 4 pages (16 bytes).
107+ * <p>The MIFARE Ultralight protocol always reads 4 pages at a time.
108+ * <p>If the read spans past the last readable block, then the tag will
109+ * return pages that have been wrapped back to the first blocks. MIFARE
110+ * Ultralight tags have readable blocks 0x00 through 0x0F. So a read to
111+ * block offset 0x0E would return blocks 0x0E, 0x0F, 0x00, 0x01. MIFARE
112+ * Ultralight C tags have readable blocks 0x00 through 0x2B. So a read to
113+ * block 0x2A would return blocks 0x2A, 0x2B, 0x00, 0x01.
114+ * <p>This requires that the tag be connected.
85115 *
116+ * @return 4 pages (16 bytes)
86117 * @throws IOException
87118 */
88- public byte [] readBlock (int page ) throws IOException {
119+ public byte [] readPages (int pageOffset ) throws IOException {
120+ validatePageOffset (pageOffset );
89121 checkConnected ();
90122
91- byte [] blockread_cmd = { 0x30 , (byte ) page }; // phHal_eMifareRead
92- return transceive (blockread_cmd , false );
123+ byte [] cmd = { 0x30 , (byte ) pageOffset };
124+ return transceive (cmd , false );
93125 }
94126
95127 /**
96- * Writes a 4 byte page to the tag .
97- *
98- * <p>This requires a that the tag be connected.
128+ * Write 1 page (4 bytes) .
129+ * <p>The MIFARE Ultralight protocol always writes 1 page at a time.
130+ * <p>This requires that the tag be connected.
99131 *
100132 * @param page The offset of the page to write
101133 * @param data The data to write
102134 * @throws IOException
103135 */
104- public void writePage (int page , byte [] data ) throws IOException {
136+ public void writePage (int pageOffset , byte [] data ) throws IOException {
137+ validatePageOffset (pageOffset );
105138 checkConnected ();
106139
107- byte [] pagewrite_cmd = new byte [data .length + 2 ];
108- pagewrite_cmd [0 ] = (byte ) 0xA2 ;
109- pagewrite_cmd [1 ] = (byte ) page ;
110- System .arraycopy (data , 0 , pagewrite_cmd , 2 , data .length );
140+ byte [] cmd = new byte [data .length + 2 ];
141+ cmd [0 ] = (byte ) 0xA2 ;
142+ cmd [1 ] = (byte ) pageOffset ;
143+ System .arraycopy (data , 0 , cmd , 2 , data .length );
111144
112- transceive (pagewrite_cmd , false );
145+ transceive (cmd , false );
113146 }
114147
115148 /**
@@ -127,4 +160,15 @@ public void writePage(int page, byte[] data) throws IOException {
127160 public byte [] transceive (byte [] data ) throws IOException {
128161 return transceive (data , true );
129162 }
163+
164+ private static void validatePageOffset (int pageOffset ) {
165+ // Do not be too strict on upper bounds checking, since some cards
166+ // may have more addressable memory than they report.
167+ // Note that issuing a command to an out-of-bounds block is safe - the
168+ // tag will wrap the read to an addressable area. This validation is a
169+ // helper to guard against obvious programming mistakes.
170+ if (pageOffset < 0 || pageOffset >= MAX_PAGE_COUNT ) {
171+ throw new IndexOutOfBoundsException ("page out of bounds: " + pageOffset );
172+ }
173+ }
130174}
0 commit comments