• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.Bundle;
23 import android.os.RemoteException;
24 import android.util.Log;
25 
26 import java.io.IOException;
27 
28 //TOOD: Ultralight C 3-DES authentication, one-way counter
29 
30 /**
31  * Provides access to MIFARE Ultralight properties and I/O operations on a {@link Tag}.
32  *
33  * <p>Acquire a {@link MifareUltralight} object using {@link #get}.
34  *
35  * <p>MIFARE Ultralight compatible tags have 4 byte pages {@link #PAGE_SIZE}.
36  * The primary operations on an Ultralight tag are {@link #readPages} and
37  * {@link #writePage}.
38  *
39  * <p>The original MIFARE Ultralight consists of a 64 byte EEPROM. The first
40  * 4 pages are for the OTP area, manufacturer data, and locking bits. They are
41  * readable and some bits are writable. The final 12 pages are the user
42  * read/write area. For more information see the NXP data sheet MF0ICU1.
43  *
44  * <p>The MIFARE Ultralight C consists of a 192 byte EEPROM. The first 4 pages
45  * are for OTP, manufacturer data, and locking bits. The next 36 pages are the
46  * user read/write area. The next 4 pages are additional locking bits, counters
47  * and authentication configuration and are readable. The final 4 pages are for
48  * the authentication key and are not readable. For more information see the
49  * NXP data sheet MF0ICU2.
50  *
51  * <p>Implementation of this class on a Android NFC device is optional.
52  * If it is not implemented, then
53  * {@link MifareUltralight} will never be enumerated in {@link Tag#getTechList}.
54  * If it is enumerated, then all {@link MifareUltralight} I/O operations will be supported.
55  * In either case, {@link NfcA} will also be enumerated on the tag,
56  * because all MIFARE Ultralight tags are also {@link NfcA} tags.
57  *
58  * <p class="note"><strong>Note:</strong> Methods that perform I/O operations
59  * require the {@link android.Manifest.permission#NFC} permission.
60  */
61 public final class MifareUltralight extends BasicTagTechnology {
62     private static final String TAG = "NFC";
63 
64     /** A MIFARE Ultralight compatible tag of unknown type */
65     public static final int TYPE_UNKNOWN = -1;
66     /** A MIFARE Ultralight tag */
67     public static final int TYPE_ULTRALIGHT = 1;
68     /** A MIFARE Ultralight C tag */
69     public static final int TYPE_ULTRALIGHT_C = 2;
70 
71     /** Size of a MIFARE Ultralight page in bytes */
72     public static final int PAGE_SIZE = 4;
73 
74     private static final int NXP_MANUFACTURER_ID = 0x04;
75     private static final int MAX_PAGE_COUNT = 256;
76 
77     /** @hide */
78     public static final String EXTRA_IS_UL_C = "isulc";
79 
80     private int mType;
81 
82     /**
83      * Get an instance of {@link MifareUltralight} for the given tag.
84      * <p>Returns null if {@link MifareUltralight} was not enumerated in
85      * {@link Tag#getTechList} - this indicates the tag is not MIFARE
86      * Ultralight compatible, or that this Android
87      * device does not implement MIFARE Ultralight.
88      * <p>Does not cause any RF activity and does not block.
89      *
90      * @param tag an MIFARE Ultralight compatible tag
91      * @return MIFARE Ultralight object
92      */
get(Tag tag)93     public static MifareUltralight get(Tag tag) {
94         if (!tag.hasTech(TagTechnology.MIFARE_ULTRALIGHT)) return null;
95         try {
96             return new MifareUltralight(tag);
97         } catch (RemoteException e) {
98             return null;
99         }
100     }
101 
102     /** @hide */
MifareUltralight(Tag tag)103     public MifareUltralight(Tag tag) throws RemoteException {
104         super(tag, TagTechnology.MIFARE_ULTRALIGHT);
105 
106         // Check if this could actually be a MIFARE
107         NfcA a = NfcA.get(tag);
108 
109         mType = TYPE_UNKNOWN;
110 
111         if (a.getSak() == 0x00 && tag.getId()[0] == NXP_MANUFACTURER_ID) {
112             Bundle extras = tag.getTechExtras(TagTechnology.MIFARE_ULTRALIGHT);
113             if (extras.getBoolean(EXTRA_IS_UL_C)) {
114                 mType = TYPE_ULTRALIGHT_C;
115             } else {
116                 mType = TYPE_ULTRALIGHT;
117             }
118         }
119     }
120 
121     /**
122      * Return the MIFARE Ultralight type of the tag.
123      * <p>One of {@link #TYPE_ULTRALIGHT} or {@link #TYPE_ULTRALIGHT_C} or
124      * {@link #TYPE_UNKNOWN}.
125      * <p>Depending on how the tag has been formatted, it can be impossible
126      * to accurately classify between original MIFARE Ultralight and
127      * Ultralight C. So treat this method as a hint.
128      * <p>Does not cause any RF activity and does not block.
129      *
130      * @return the type
131      */
getType()132     public int getType() {
133         return mType;
134     }
135 
136     /**
137      * Read 4 pages (16 bytes).
138      *
139      * <p>The MIFARE Ultralight protocol always reads 4 pages at a time, to
140      * reduce the number of commands required to read an entire tag.
141      * <p>If a read spans past the last readable block, then the tag will
142      * return pages that have been wrapped back to the first blocks. MIFARE
143      * Ultralight tags have readable blocks 0x00 through 0x0F. So a read to
144      * block offset 0x0E would return blocks 0x0E, 0x0F, 0x00, 0x01. MIFARE
145      * Ultralight C tags have readable blocks 0x00 through 0x2B. So a read to
146      * block 0x2A would return blocks 0x2A, 0x2B, 0x00, 0x01.
147      *
148      * <p>This is an I/O operation and will block until complete. It must
149      * not be called from the main application thread. A blocked call will be canceled with
150      * {@link IOException} if {@link #close} is called from another thread.
151      *
152      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
153      *
154      * @param pageOffset index of first page to read, starting from 0
155      * @return 4 pages (16 bytes)
156      * @throws TagLostException if the tag leaves the field
157      * @throws IOException if there is an I/O failure, or the operation is canceled
158      */
readPages(int pageOffset)159     public byte[] readPages(int pageOffset) throws IOException {
160         validatePageIndex(pageOffset);
161         checkConnected();
162 
163         byte[] cmd = { 0x30, (byte) pageOffset};
164         return transceive(cmd, false);
165     }
166 
167     /**
168      * Write 1 page (4 bytes).
169      *
170      * <p>The MIFARE Ultralight protocol always writes 1 page at a time, to
171      * minimize EEPROM write cycles.
172      *
173      * <p>This is an I/O operation and will block until complete. It must
174      * not be called from the main application thread. A blocked call will be canceled with
175      * {@link IOException} if {@link #close} is called from another thread.
176      *
177      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
178      *
179      * @param pageOffset index of page to write, starting from 0
180      * @param data 4 bytes to write
181      * @throws TagLostException if the tag leaves the field
182      * @throws IOException if there is an I/O failure, or the operation is canceled
183      */
writePage(int pageOffset, byte[] data)184     public void writePage(int pageOffset, byte[] data) throws IOException {
185         validatePageIndex(pageOffset);
186         checkConnected();
187 
188         byte[] cmd = new byte[data.length + 2];
189         cmd[0] = (byte) 0xA2;
190         cmd[1] = (byte) pageOffset;
191         System.arraycopy(data, 0, cmd, 2, data.length);
192 
193         transceive(cmd, false);
194     }
195 
196     /**
197      * Send raw NfcA data to a tag and receive the response.
198      *
199      * <p>This is equivalent to connecting to this tag via {@link NfcA}
200      * and calling {@link NfcA#transceive}. Note that all MIFARE Classic
201      * tags are based on {@link NfcA} technology.
202      *
203      * <p>Use {@link #getMaxTransceiveLength} to retrieve the maximum number of bytes
204      * that can be sent with {@link #transceive}.
205      *
206      * <p>This is an I/O operation and will block until complete. It must
207      * not be called from the main application thread. A blocked call will be canceled with
208      * {@link IOException} if {@link #close} is called from another thread.
209      *
210      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
211      *
212      * @see NfcA#transceive
213      */
transceive(byte[] data)214     public byte[] transceive(byte[] data) throws IOException {
215         return transceive(data, true);
216     }
217 
218     /**
219      * Return the maximum number of bytes that can be sent with {@link #transceive}.
220      * @return the maximum number of bytes that can be sent with {@link #transceive}.
221      */
getMaxTransceiveLength()222     public int getMaxTransceiveLength() {
223         return getMaxTransceiveLengthInternal();
224     }
225 
226     /**
227      * Set the {@link #transceive} timeout in milliseconds.
228      *
229      * <p>The timeout only applies to {@link #transceive} on this object,
230      * and is reset to a default value when {@link #close} is called.
231      *
232      * <p>Setting a longer timeout may be useful when performing
233      * transactions that require a long processing time on the tag
234      * such as key generation.
235      *
236      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
237      *
238      * @param timeout timeout value in milliseconds
239      * @throws SecurityException if the tag object is reused after the tag has left the field
240      */
setTimeout(int timeout)241     public void setTimeout(int timeout) {
242         try {
243             int err = mTag.getTagService().setTimeout(
244                     TagTechnology.MIFARE_ULTRALIGHT, timeout);
245             if (err != ErrorCodes.SUCCESS) {
246                 throw new IllegalArgumentException("The supplied timeout is not valid");
247             }
248         } catch (RemoteException e) {
249             Log.e(TAG, "NFC service dead", e);
250         }
251     }
252 
253     /**
254      * Get the current {@link #transceive} timeout in milliseconds.
255      *
256      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
257      *
258      * @return timeout value in milliseconds
259      * @throws SecurityException if the tag object is reused after the tag has left the field
260      */
getTimeout()261     public int getTimeout() {
262         try {
263             return mTag.getTagService().getTimeout(TagTechnology.MIFARE_ULTRALIGHT);
264         } catch (RemoteException e) {
265             Log.e(TAG, "NFC service dead", e);
266             return 0;
267         }
268     }
269 
validatePageIndex(int pageIndex)270     private static void validatePageIndex(int pageIndex) {
271         // Do not be too strict on upper bounds checking, since some cards
272         // may have more addressable memory than they report.
273         // Note that issuing a command to an out-of-bounds block is safe - the
274         // tag will wrap the read to an addressable area. This validation is a
275         // helper to guard against obvious programming mistakes.
276         if (pageIndex < 0 || pageIndex >= MAX_PAGE_COUNT) {
277             throw new IndexOutOfBoundsException("page out of bounds: " + pageIndex);
278         }
279     }
280 }
281