blob: 226e079ca226e3180ff2d951cb676c0cf1d4b53d [file] [log] [blame]
Jeff Hamilton6be655c2010-11-12 12:28:16 -06001/*
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
Jeff Hamilton4e21e1d2011-01-21 01:13:06 -060017package android.nfc.tech;
Jeff Hamilton6be655c2010-11-12 12:28:16 -060018
19import android.nfc.ErrorCodes;
20import android.nfc.FormatException;
Jeff Hamilton4e21e1d2011-01-21 01:13:06 -060021import android.nfc.INfcTag;
Jeff Hamilton6be655c2010-11-12 12:28:16 -060022import android.nfc.NdefMessage;
23import android.nfc.NfcAdapter;
24import android.nfc.Tag;
Nick Pelly74fe6c62011-02-02 22:37:40 -080025import android.nfc.TagLostException;
Jeff Hamilton6be655c2010-11-12 12:28:16 -060026import android.os.Bundle;
27import android.os.RemoteException;
Nick Pelly3dd6c452011-01-10 18:14:41 +110028import android.util.Log;
Jeff Hamilton6be655c2010-11-12 12:28:16 -060029
30import java.io.IOException;
31
32/**
Nick Pelly74fe6c62011-02-02 22:37:40 -080033 * Provides access to NDEF content and operations on a {@link Tag}.
Jeff Hamilton6be655c2010-11-12 12:28:16 -060034 *
Nick Pelly74fe6c62011-02-02 22:37:40 -080035 * <p>Acquire a {@link Ndef} object using {@link #get}.
36 *
37 * <p>NDEF is an NFC Forum data format. The data formats are implemented in
38 * {@link android.nfc.NdefMessage} and
39 * {@link android.nfc.NdefRecord}. This class provides methods to
40 * retrieve and modify the {@link android.nfc.NdefMessage}
41 * on a tag.
42 *
43 * <p>There are currently four NFC Forum standardized tag types that can be
44 * formatted to contain NDEF data.
45 * <ul>
46 * <li>NFC Forum Type 1 Tag ({@link #NFC_FORUM_TYPE_1}), such as the Innovision Topaz
Nick Pelly39cf3a42011-02-07 17:04:21 +090047 * <li>NFC Forum Type 2 Tag ({@link #NFC_FORUM_TYPE_2}), such as the NXP MIFARE Ultralight
Nick Pelly74fe6c62011-02-02 22:37:40 -080048 * <li>NFC Forum Type 3 Tag ({@link #NFC_FORUM_TYPE_3}), such as Sony Felica
49 * <li>NFC Forum Type 4 Tag ({@link #NFC_FORUM_TYPE_4}), such as NXP MIFARE Desfire
50 * </ul>
51 * It is mandatory for all Android devices with NFC to correctly enumerate
52 * {@link Ndef} on NFC Forum Tag Types 1-4, and implement all NDEF operations
53 * as defined in this class.
54 *
55 * <p>Some vendors have there own well defined specifications for storing NDEF data
56 * on tags that do not fall into the above categories. Android devices with NFC
57 * should enumerate and implement {@link Ndef} under these vendor specifications
58 * where possible, but it is not mandatory. {@link #getType} returns a String
59 * describing this specification, for example {@link #MIFARE_CLASSIC} is
60 * <code>com.nxp.ndef.mifareclassic</code>.
61 *
62 * <p>Android devices that support MIFARE Classic must also correctly
63 * implement {@link Ndef} on MIFARE Classic tags formatted to NDEF.
64 *
65 * <p>For guaranteed compatibility across all Android devices with NFC, it is
66 * recommended to use NFC Forum Types 1-4 in new deployments of NFC tags
67 * with NDEF payload. Vendor NDEF formats will not work on all Android devices.
Jeff Hamilton6be655c2010-11-12 12:28:16 -060068 *
Nick Pelly39cf3a42011-02-07 17:04:21 +090069 * <p class="note"><strong>Note:</strong> Methods that perform I/O operations
70 * require the {@link android.Manifest.permission#NFC} permission.
Jeff Hamilton6be655c2010-11-12 12:28:16 -060071 */
72public final class Ndef extends BasicTagTechnology {
Nick Pelly3dd6c452011-01-10 18:14:41 +110073 private static final String TAG = "NFC";
74
Martijn Coenen3300e4c2010-12-08 23:23:47 -080075 /** @hide */
76 public static final int NDEF_MODE_READ_ONLY = 1;
77 /** @hide */
78 public static final int NDEF_MODE_READ_WRITE = 2;
79 /** @hide */
80 public static final int NDEF_MODE_UNKNOWN = 3;
Jeff Hamilton6be655c2010-11-12 12:28:16 -060081
Martijn Coenen72df4ea2010-12-06 21:05:17 -080082 /** @hide */
83 public static final String EXTRA_NDEF_MSG = "ndefmsg";
84
Martijn Coenen6d9fc7e2010-12-07 14:04:28 -080085 /** @hide */
86 public static final String EXTRA_NDEF_MAXLENGTH = "ndefmaxlength";
87
Martijn Coenen3300e4c2010-12-08 23:23:47 -080088 /** @hide */
89 public static final String EXTRA_NDEF_CARDSTATE = "ndefcardstate";
90
Martijn Coenend27ebf12010-12-22 13:51:51 +010091 /** @hide */
92 public static final String EXTRA_NDEF_TYPE = "ndeftype";
93
Nick Pellyf003e262011-01-31 23:27:37 -080094 /** @hide */
95 public static final int TYPE_OTHER = -1;
96 /** @hide */
97 public static final int TYPE_1 = 1;
98 /** @hide */
99 public static final int TYPE_2 = 2;
100 /** @hide */
101 public static final int TYPE_3 = 3;
102 /** @hide */
103 public static final int TYPE_4 = 4;
104 /** @hide */
105 public static final int TYPE_MIFARE_CLASSIC = 101;
Jeff Hamilton5644d0e2011-02-24 21:59:52 -0600106 /** @hide */
107 public static final int TYPE_ICODE_SLI = 102;
Nick Pellyf003e262011-01-31 23:27:37 -0800108
109 /** @hide */
110 public static final String UNKNOWN = "android.ndef.unknown";
111
Nick Pelly74fe6c62011-02-02 22:37:40 -0800112 /** NFC Forum Tag Type 1 */
Nick Pellyf003e262011-01-31 23:27:37 -0800113 public static final String NFC_FORUM_TYPE_1 = "org.nfcforum.ndef.type1";
Nick Pelly74fe6c62011-02-02 22:37:40 -0800114 /** NFC Forum Tag Type 2 */
Nick Pellyf003e262011-01-31 23:27:37 -0800115 public static final String NFC_FORUM_TYPE_2 = "org.nfcforum.ndef.type2";
Nick Pelly74fe6c62011-02-02 22:37:40 -0800116 /** NFC Forum Tag Type 4 */
Nick Pellyf003e262011-01-31 23:27:37 -0800117 public static final String NFC_FORUM_TYPE_3 = "org.nfcforum.ndef.type3";
Nick Pelly74fe6c62011-02-02 22:37:40 -0800118 /** NFC Forum Tag Type 4 */
Nick Pellyf003e262011-01-31 23:27:37 -0800119 public static final String NFC_FORUM_TYPE_4 = "org.nfcforum.ndef.type4";
Nick Pelly74fe6c62011-02-02 22:37:40 -0800120 /** NDEF on MIFARE Classic */
Nick Pellyf003e262011-01-31 23:27:37 -0800121 public static final String MIFARE_CLASSIC = "com.nxp.ndef.mifareclassic";
Jeff Hamilton5644d0e2011-02-24 21:59:52 -0600122 /**
123 * NDEF on iCODE SLI
124 * @hide
125 */
126 public static final String ICODE_SLI = "com.nxp.ndef.icodesli";
Martijn Coenend27ebf12010-12-22 13:51:51 +0100127
Martijn Coenen3300e4c2010-12-08 23:23:47 -0800128 private final int mMaxNdefSize;
129 private final int mCardState;
Martijn Coenene3f63362010-12-13 16:18:41 +0100130 private final NdefMessage mNdefMsg;
Martijn Coenend27ebf12010-12-22 13:51:51 +0100131 private final int mNdefType;
Martijn Coenen6d9fc7e2010-12-07 14:04:28 -0800132
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600133 /**
Nick Pelly74fe6c62011-02-02 22:37:40 -0800134 * Get an instance of {@link Ndef} for the given tag.
Jeff Hamilton4e21e1d2011-01-21 01:13:06 -0600135 *
Nick Pelly74fe6c62011-02-02 22:37:40 -0800136 * <p>Returns null if {@link Ndef} was not enumerated in {@link Tag#getTechList}.
137 * This indicates the tag is not NDEF formatted, or that this tag
138 * is NDEF formatted but under a vendor specification that this Android
139 * device does not implement.
140 *
141 * <p>Does not cause any RF activity and does not block.
142 *
143 * @param tag an MIFARE Classic compatible tag
144 * @return MIFARE Classic object
Jeff Hamilton4e21e1d2011-01-21 01:13:06 -0600145 */
146 public static Ndef get(Tag tag) {
147 if (!tag.hasTech(TagTechnology.NDEF)) return null;
148 try {
149 return new Ndef(tag);
150 } catch (RemoteException e) {
151 return null;
152 }
153 }
154
155 /**
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600156 * Internal constructor, to be used by NfcAdapter
157 * @hide
158 */
Jeff Hamilton4e21e1d2011-01-21 01:13:06 -0600159 public Ndef(Tag tag) throws RemoteException {
160 super(tag, TagTechnology.NDEF);
161 Bundle extras = tag.getTechExtras(TagTechnology.NDEF);
Martijn Coenen6d9fc7e2010-12-07 14:04:28 -0800162 if (extras != null) {
Martijn Coenen3300e4c2010-12-08 23:23:47 -0800163 mMaxNdefSize = extras.getInt(EXTRA_NDEF_MAXLENGTH);
164 mCardState = extras.getInt(EXTRA_NDEF_CARDSTATE);
Martijn Coenene3f63362010-12-13 16:18:41 +0100165 mNdefMsg = extras.getParcelable(EXTRA_NDEF_MSG);
Martijn Coenend27ebf12010-12-22 13:51:51 +0100166 mNdefType = extras.getInt(EXTRA_NDEF_TYPE);
Martijn Coenen6d9fc7e2010-12-07 14:04:28 -0800167 } else {
Martijn Coenen3300e4c2010-12-08 23:23:47 -0800168 throw new NullPointerException("NDEF tech extras are null.");
Martijn Coenen6d9fc7e2010-12-07 14:04:28 -0800169 }
Martijn Coenen3300e4c2010-12-08 23:23:47 -0800170
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600171 }
172
173 /**
Nick Pelly74fe6c62011-02-02 22:37:40 -0800174 * Get the {@link NdefMessage} that was read from the tag at discovery time.
175 *
176 * <p>If the NDEF Message is modified by an I/O operation then it
177 * will not be updated here, this function only returns what was discovered
178 * when the tag entered the field.
179 * <p>Does not cause any RF activity and does not block.
180 * @return NDEF Message read from the tag at discovery time
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600181 */
Martijn Coenene3f63362010-12-13 16:18:41 +0100182 public NdefMessage getCachedNdefMessage() {
183 return mNdefMsg;
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600184 }
185
186 /**
Nick Pelly74fe6c62011-02-02 22:37:40 -0800187 * Get the NDEF tag type.
188 *
Nick Pellyddbb2c92011-01-04 17:29:14 +1100189 * <p>Returns one of {@link #NFC_FORUM_TYPE_1}, {@link #NFC_FORUM_TYPE_2},
190 * {@link #NFC_FORUM_TYPE_3}, {@link #NFC_FORUM_TYPE_4},
Nick Pelly74fe6c62011-02-02 22:37:40 -0800191 * {@link #MIFARE_CLASSIC} or another NDEF tag type that has not yet been
192 * formalized in this Android API.
193 *
194 * <p>Does not cause any RF activity and does not block.
195 *
196 * @return a string representing the NDEF tag type
Martijn Coenend27ebf12010-12-22 13:51:51 +0100197 */
Nick Pellyf003e262011-01-31 23:27:37 -0800198 public String getType() {
199 switch (mNdefType) {
200 case TYPE_1:
201 return NFC_FORUM_TYPE_1;
202 case TYPE_2:
203 return NFC_FORUM_TYPE_2;
204 case TYPE_3:
205 return NFC_FORUM_TYPE_3;
206 case TYPE_4:
207 return NFC_FORUM_TYPE_4;
208 case TYPE_MIFARE_CLASSIC:
209 return MIFARE_CLASSIC;
Jeff Hamilton5644d0e2011-02-24 21:59:52 -0600210 case TYPE_ICODE_SLI:
211 return ICODE_SLI;
Nick Pellyf003e262011-01-31 23:27:37 -0800212 default:
213 return UNKNOWN;
214 }
Martijn Coenend27ebf12010-12-22 13:51:51 +0100215 }
216
217 /**
Nick Pelly74fe6c62011-02-02 22:37:40 -0800218 * Get the maximum NDEF message size in bytes.
219 *
220 * <p>Does not cause any RF activity and does not block.
221 *
222 * @return size in bytes
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600223 */
Martijn Coenen3300e4c2010-12-08 23:23:47 -0800224 public int getMaxSize() {
225 return mMaxNdefSize;
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600226 }
227
228 /**
Nick Pelly74fe6c62011-02-02 22:37:40 -0800229 * Determine if the tag is writable.
230 *
231 * <p>NFC Forum tags can be in read-only or read-write states.
232 *
233 * <p>Does not cause any RF activity and does not block.
234 *
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600235 * <p>Requires {@link android.Manifest.permission#NFC} permission.
Nick Pelly74fe6c62011-02-02 22:37:40 -0800236 *
237 * @return true if the tag is writable
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600238 */
Martijn Coenen3300e4c2010-12-08 23:23:47 -0800239 public boolean isWritable() {
240 return (mCardState == NDEF_MODE_READ_WRITE);
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600241 }
242
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600243 /**
Nick Pelly74fe6c62011-02-02 22:37:40 -0800244 * Read the current {@link android.nfc.NdefMessage} on this tag.
245 *
246 * <p>This always reads the current NDEF Message stored on the tag.
247 *
248 * <p>This is an I/O operation and will block until complete. It must
249 * not be called from the main application thread. A blocked call will be canceled with
250 * {@link IOException} if {@link #close} is called from another thread.
251 *
252 * @return the NDEF Message, never null
253 * @throws TagLostException if the tag leaves the field
254 * @throws IOException if there is an I/O failure, or the operation is canceled
255 * @throws FormatException if the NDEF Message on the tag is malformed
Martijn Coenene3f63362010-12-13 16:18:41 +0100256 */
257 public NdefMessage getNdefMessage() throws IOException, FormatException {
Martijn Coenen4049f9d02010-12-14 16:58:27 +0100258 checkConnected();
259
Martijn Coenene3f63362010-12-13 16:18:41 +0100260 try {
Jeff Hamilton4e21e1d2011-01-21 01:13:06 -0600261 INfcTag tagService = mTag.getTagService();
Martijn Coenen23fc93a2011-11-28 10:43:14 -0800262 if (tagService == null) {
263 throw new IOException("Mock tags don't support this operation.");
264 }
Martijn Coenene3f63362010-12-13 16:18:41 +0100265 int serviceHandle = mTag.getServiceHandle();
Jeff Hamilton4e21e1d2011-01-21 01:13:06 -0600266 if (tagService.isNdef(serviceHandle)) {
267 NdefMessage msg = tagService.ndefRead(serviceHandle);
Martijn Coenene3f63362010-12-13 16:18:41 +0100268 if (msg == null) {
Jeff Hamilton4e21e1d2011-01-21 01:13:06 -0600269 int errorCode = tagService.getLastError(serviceHandle);
Martijn Coenene3f63362010-12-13 16:18:41 +0100270 switch (errorCode) {
271 case ErrorCodes.ERROR_IO:
272 throw new IOException();
273 case ErrorCodes.ERROR_INVALID_PARAM:
274 throw new FormatException();
275 default:
276 // Should not happen
277 throw new IOException();
278 }
279 }
280 return msg;
281 } else {
282 return null;
283 }
284 } catch (RemoteException e) {
Nick Pelly3dd6c452011-01-10 18:14:41 +1100285 Log.e(TAG, "NFC service dead", e);
Martijn Coenene3f63362010-12-13 16:18:41 +0100286 return null;
287 }
288 }
Jeff Hamilton3ce864812010-12-18 16:39:37 -0600289
Martijn Coenene3f63362010-12-13 16:18:41 +0100290 /**
Nick Pelly74fe6c62011-02-02 22:37:40 -0800291 * Overwrite the {@link NdefMessage} on this tag.
292 *
293 * <p>This is an I/O operation and will block until complete. It must
294 * not be called from the main application thread. A blocked call will be canceled with
295 * {@link IOException} if {@link #close} is called from another thread.
296 *
Nick Pelly39cf3a42011-02-07 17:04:21 +0900297 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
298 *
Nick Pelly74fe6c62011-02-02 22:37:40 -0800299 * @param msg the NDEF Message to write, must not be null
300 * @throws TagLostException if the tag leaves the field
301 * @throws IOException if there is an I/O failure, or the operation is canceled
302 * @throws FormatException if the NDEF Message to write is malformed
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600303 */
304 public void writeNdefMessage(NdefMessage msg) throws IOException, FormatException {
Martijn Coenen4049f9d02010-12-14 16:58:27 +0100305 checkConnected();
306
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600307 try {
Jeff Hamilton4e21e1d2011-01-21 01:13:06 -0600308 INfcTag tagService = mTag.getTagService();
Martijn Coenen23fc93a2011-11-28 10:43:14 -0800309 if (tagService == null) {
310 throw new IOException("Mock tags don't support this operation.");
311 }
Martijn Coenen01d159a2010-12-16 22:39:20 +0100312 int serviceHandle = mTag.getServiceHandle();
Jeff Hamilton4e21e1d2011-01-21 01:13:06 -0600313 if (tagService.isNdef(serviceHandle)) {
314 int errorCode = tagService.ndefWrite(serviceHandle, msg);
Martijn Coenen01d159a2010-12-16 22:39:20 +0100315 switch (errorCode) {
316 case ErrorCodes.SUCCESS:
317 break;
318 case ErrorCodes.ERROR_IO:
319 throw new IOException();
320 case ErrorCodes.ERROR_INVALID_PARAM:
321 throw new FormatException();
322 default:
323 // Should not happen
324 throw new IOException();
325 }
326 }
327 else {
328 throw new IOException("Tag is not ndef");
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600329 }
330 } catch (RemoteException e) {
Nick Pelly3dd6c452011-01-10 18:14:41 +1100331 Log.e(TAG, "NFC service dead", e);
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600332 }
333 }
334
335 /**
Nick Pelly74fe6c62011-02-02 22:37:40 -0800336 * Indicates whether a tag can be made read-only with {@link #makeReadOnly()}.
337 *
338 * <p>Does not cause any RF activity and does not block.
339 *
340 * @return true if it is possible to make this tag read-only
Martijn Coenen25be5362011-01-10 16:12:52 +0100341 */
Nick Pellyf003e262011-01-31 23:27:37 -0800342 public boolean canMakeReadOnly() {
Martijn Coenenfaca12a2011-08-19 14:07:52 +0200343 INfcTag tagService = mTag.getTagService();
Martijn Coenen23fc93a2011-11-28 10:43:14 -0800344 if (tagService == null) {
345 return false;
346 }
Martijn Coenenfaca12a2011-08-19 14:07:52 +0200347 try {
348 return tagService.canMakeReadOnly(mNdefType);
349 } catch (RemoteException e) {
350 Log.e(TAG, "NFC service dead", e);
Martijn Coenen25be5362011-01-10 16:12:52 +0100351 return false;
352 }
353 }
354
355 /**
Nick Pelly74fe6c62011-02-02 22:37:40 -0800356 * Make a tag read-only.
357 *
358 * <p>This sets the CC field to indicate the tag is read-only,
359 * and where possible permanently sets the lock bits to prevent
360 * any further modification of the memory.
361 * <p>This is a one-way process and cannot be reverted!
362 *
363 * <p>This is an I/O operation and will block until complete. It must
364 * not be called from the main application thread. A blocked call will be canceled with
365 * {@link IOException} if {@link #close} is called from another thread.
366 *
Nick Pelly39cf3a42011-02-07 17:04:21 +0900367 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
368 *
Nick Pelly74fe6c62011-02-02 22:37:40 -0800369 * @return true on success, false if it is not possible to make this tag read-only
370 * @throws TagLostException if the tag leaves the field
371 * @throws IOException if there is an I/O failure, or the operation is canceled
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600372 */
Nick Pellyf003e262011-01-31 23:27:37 -0800373 public boolean makeReadOnly() throws IOException {
Martijn Coenen4049f9d02010-12-14 16:58:27 +0100374 checkConnected();
375
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600376 try {
Jeff Hamilton4e21e1d2011-01-21 01:13:06 -0600377 INfcTag tagService = mTag.getTagService();
Martijn Coenen23fc93a2011-11-28 10:43:14 -0800378 if (tagService == null) {
379 return false;
380 }
Jeff Hamilton4e21e1d2011-01-21 01:13:06 -0600381 if (tagService.isNdef(mTag.getServiceHandle())) {
382 int errorCode = tagService.ndefMakeReadOnly(mTag.getServiceHandle());
Martijn Coenen07e6f612011-01-14 16:26:45 +0100383 switch (errorCode) {
384 case ErrorCodes.SUCCESS:
385 return true;
386 case ErrorCodes.ERROR_IO:
387 throw new IOException();
388 case ErrorCodes.ERROR_INVALID_PARAM:
389 return false;
390 default:
391 // Should not happen
392 throw new IOException();
393 }
394 }
395 else {
396 throw new IOException("Tag is not ndef");
397 }
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600398 } catch (RemoteException e) {
Nick Pelly3dd6c452011-01-10 18:14:41 +1100399 Log.e(TAG, "NFC service dead", e);
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600400 return false;
401 }
402 }
Jeff Hamilton6be655c2010-11-12 12:28:16 -0600403}