Ndef.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.ErrorCodes; 20import android.nfc.FormatException; 21import android.nfc.INfcTag; 22import android.nfc.NdefMessage; 23import android.nfc.NfcAdapter; 24import android.nfc.Tag; 25import android.nfc.TagLostException; 26import android.os.Bundle; 27import android.os.RemoteException; 28import android.util.Log; 29 30import java.io.IOException; 31 32/** 33 * Provides access to NDEF content and operations on a {@link Tag}. 34 * 35 * <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 47 * <li>NFC Forum Type 2 Tag ({@link #NFC_FORUM_TYPE_2}), such as the NXP Mifare Ultralight 48 * <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. 68 * 69 * <p class="note"><strong>Note:</strong> 70 * Use of this class requires the {@link android.Manifest.permission#NFC} 71 * permission. 72 */ 73public final class Ndef extends BasicTagTechnology { 74 private static final String TAG = "NFC"; 75 76 /** @hide */ 77 public static final int NDEF_MODE_READ_ONLY = 1; 78 /** @hide */ 79 public static final int NDEF_MODE_READ_WRITE = 2; 80 /** @hide */ 81 public static final int NDEF_MODE_UNKNOWN = 3; 82 83 /** @hide */ 84 public static final String EXTRA_NDEF_MSG = "ndefmsg"; 85 86 /** @hide */ 87 public static final String EXTRA_NDEF_MAXLENGTH = "ndefmaxlength"; 88 89 /** @hide */ 90 public static final String EXTRA_NDEF_CARDSTATE = "ndefcardstate"; 91 92 /** @hide */ 93 public static final String EXTRA_NDEF_TYPE = "ndeftype"; 94 95 /** @hide */ 96 public static final int TYPE_OTHER = -1; 97 /** @hide */ 98 public static final int TYPE_1 = 1; 99 /** @hide */ 100 public static final int TYPE_2 = 2; 101 /** @hide */ 102 public static final int TYPE_3 = 3; 103 /** @hide */ 104 public static final int TYPE_4 = 4; 105 /** @hide */ 106 public static final int TYPE_MIFARE_CLASSIC = 101; 107 108 /** @hide */ 109 public static final String UNKNOWN = "android.ndef.unknown"; 110 111 /** NFC Forum Tag Type 1 */ 112 public static final String NFC_FORUM_TYPE_1 = "org.nfcforum.ndef.type1"; 113 /** NFC Forum Tag Type 2 */ 114 public static final String NFC_FORUM_TYPE_2 = "org.nfcforum.ndef.type2"; 115 /** NFC Forum Tag Type 4 */ 116 public static final String NFC_FORUM_TYPE_3 = "org.nfcforum.ndef.type3"; 117 /** NFC Forum Tag Type 4 */ 118 public static final String NFC_FORUM_TYPE_4 = "org.nfcforum.ndef.type4"; 119 /** NDEF on MIFARE Classic */ 120 public static final String MIFARE_CLASSIC = "com.nxp.ndef.mifareclassic"; 121 122 private final int mMaxNdefSize; 123 private final int mCardState; 124 private final NdefMessage mNdefMsg; 125 private final int mNdefType; 126 127 /** 128 * Get an instance of {@link Ndef} for the given tag. 129 * 130 * <p>Returns null if {@link Ndef} was not enumerated in {@link Tag#getTechList}. 131 * This indicates the tag is not NDEF formatted, or that this tag 132 * is NDEF formatted but under a vendor specification that this Android 133 * device does not implement. 134 * 135 * <p>Does not cause any RF activity and does not block. 136 * 137 * @param tag an MIFARE Classic compatible tag 138 * @return MIFARE Classic object 139 */ 140 141 public static Ndef get(Tag tag) { 142 if (!tag.hasTech(TagTechnology.NDEF)) return null; 143 try { 144 return new Ndef(tag); 145 } catch (RemoteException e) { 146 return null; 147 } 148 } 149 150 /** 151 * Internal constructor, to be used by NfcAdapter 152 * @hide 153 */ 154 public Ndef(Tag tag) throws RemoteException { 155 super(tag, TagTechnology.NDEF); 156 Bundle extras = tag.getTechExtras(TagTechnology.NDEF); 157 if (extras != null) { 158 mMaxNdefSize = extras.getInt(EXTRA_NDEF_MAXLENGTH); 159 mCardState = extras.getInt(EXTRA_NDEF_CARDSTATE); 160 mNdefMsg = extras.getParcelable(EXTRA_NDEF_MSG); 161 mNdefType = extras.getInt(EXTRA_NDEF_TYPE); 162 } else { 163 throw new NullPointerException("NDEF tech extras are null."); 164 } 165 166 } 167 168 /** 169 * Get the {@link NdefMessage} that was read from the tag at discovery time. 170 * 171 * <p>If the NDEF Message is modified by an I/O operation then it 172 * will not be updated here, this function only returns what was discovered 173 * when the tag entered the field. 174 * <p>Does not cause any RF activity and does not block. 175 * @return NDEF Message read from the tag at discovery time 176 */ 177 public NdefMessage getCachedNdefMessage() { 178 return mNdefMsg; 179 } 180 181 /** 182 * Get the NDEF tag type. 183 * 184 * <p>Returns one of {@link #NFC_FORUM_TYPE_1}, {@link #NFC_FORUM_TYPE_2}, 185 * {@link #NFC_FORUM_TYPE_3}, {@link #NFC_FORUM_TYPE_4}, 186 * {@link #MIFARE_CLASSIC} or another NDEF tag type that has not yet been 187 * formalized in this Android API. 188 * 189 * <p>Does not cause any RF activity and does not block. 190 * 191 * @return a string representing the NDEF tag type 192 */ 193 public String getType() { 194 switch (mNdefType) { 195 case TYPE_1: 196 return NFC_FORUM_TYPE_1; 197 case TYPE_2: 198 return NFC_FORUM_TYPE_2; 199 case TYPE_3: 200 return NFC_FORUM_TYPE_3; 201 case TYPE_4: 202 return NFC_FORUM_TYPE_4; 203 case TYPE_MIFARE_CLASSIC: 204 return MIFARE_CLASSIC; 205 default: 206 return UNKNOWN; 207 } 208 } 209 210 /** 211 * Get the maximum NDEF message size in bytes. 212 * 213 * <p>Does not cause any RF activity and does not block. 214 * 215 * @return size in bytes 216 */ 217 public int getMaxSize() { 218 return mMaxNdefSize; 219 } 220 221 /** 222 * Determine if the tag is writable. 223 * 224 * <p>NFC Forum tags can be in read-only or read-write states. 225 * 226 * <p>Does not cause any RF activity and does not block. 227 * 228 * <p>Requires {@link android.Manifest.permission#NFC} permission. 229 * 230 * @return true if the tag is writable 231 */ 232 public boolean isWritable() { 233 return (mCardState == NDEF_MODE_READ_WRITE); 234 } 235 236 /** 237 * Read the current {@link android.nfc.NdefMessage} on this tag. 238 * 239 * <p>This always reads the current NDEF Message stored on the tag. 240 * 241 * <p>This is an I/O operation and will block until complete. It must 242 * not be called from the main application thread. A blocked call will be canceled with 243 * {@link IOException} if {@link #close} is called from another thread. 244 * 245 * @return the NDEF Message, never null 246 * @throws TagLostException if the tag leaves the field 247 * @throws IOException if there is an I/O failure, or the operation is canceled 248 * @throws FormatException if the NDEF Message on the tag is malformed 249 */ 250 public NdefMessage getNdefMessage() throws IOException, FormatException { 251 checkConnected(); 252 253 try { 254 INfcTag tagService = mTag.getTagService(); 255 int serviceHandle = mTag.getServiceHandle(); 256 if (tagService.isNdef(serviceHandle)) { 257 NdefMessage msg = tagService.ndefRead(serviceHandle); 258 if (msg == null) { 259 int errorCode = tagService.getLastError(serviceHandle); 260 switch (errorCode) { 261 case ErrorCodes.ERROR_IO: 262 throw new IOException(); 263 case ErrorCodes.ERROR_INVALID_PARAM: 264 throw new FormatException(); 265 default: 266 // Should not happen 267 throw new IOException(); 268 } 269 } 270 return msg; 271 } else { 272 return null; 273 } 274 } catch (RemoteException e) { 275 Log.e(TAG, "NFC service dead", e); 276 return null; 277 } 278 } 279 280 /** 281 * Overwrite the {@link NdefMessage} on this tag. 282 * 283 * <p>This is an I/O operation and will block until complete. It must 284 * not be called from the main application thread. A blocked call will be canceled with 285 * {@link IOException} if {@link #close} is called from another thread. 286 * 287 * @param msg the NDEF Message to write, must not be null 288 * @throws TagLostException if the tag leaves the field 289 * @throws IOException if there is an I/O failure, or the operation is canceled 290 * @throws FormatException if the NDEF Message to write is malformed 291 */ 292 public void writeNdefMessage(NdefMessage msg) throws IOException, FormatException { 293 checkConnected(); 294 295 try { 296 INfcTag tagService = mTag.getTagService(); 297 int serviceHandle = mTag.getServiceHandle(); 298 if (tagService.isNdef(serviceHandle)) { 299 int errorCode = tagService.ndefWrite(serviceHandle, msg); 300 switch (errorCode) { 301 case ErrorCodes.SUCCESS: 302 break; 303 case ErrorCodes.ERROR_IO: 304 throw new IOException(); 305 case ErrorCodes.ERROR_INVALID_PARAM: 306 throw new FormatException(); 307 default: 308 // Should not happen 309 throw new IOException(); 310 } 311 } 312 else { 313 throw new IOException("Tag is not ndef"); 314 } 315 } catch (RemoteException e) { 316 Log.e(TAG, "NFC service dead", e); 317 } 318 } 319 320 /** 321 * Indicates whether a tag can be made read-only with {@link #makeReadOnly()}. 322 * 323 * <p>Does not cause any RF activity and does not block. 324 * 325 * @return true if it is possible to make this tag read-only 326 */ 327 public boolean canMakeReadOnly() { 328 if (mNdefType == TYPE_1 || mNdefType == TYPE_2) { 329 return true; 330 } else { 331 return false; 332 } 333 } 334 335 /** 336 * Make a tag read-only. 337 * 338 * <p>This sets the CC field to indicate the tag is read-only, 339 * and where possible permanently sets the lock bits to prevent 340 * any further modification of the memory. 341 * <p>This is a one-way process and cannot be reverted! 342 * 343 * <p>This is an I/O operation and will block until complete. It must 344 * not be called from the main application thread. A blocked call will be canceled with 345 * {@link IOException} if {@link #close} is called from another thread. 346 * 347 * @return true on success, false if it is not possible to make this tag read-only 348 * @throws TagLostException if the tag leaves the field 349 * @throws IOException if there is an I/O failure, or the operation is canceled 350 */ 351 public boolean makeReadOnly() throws IOException { 352 checkConnected(); 353 354 try { 355 INfcTag tagService = mTag.getTagService(); 356 if (tagService.isNdef(mTag.getServiceHandle())) { 357 int errorCode = tagService.ndefMakeReadOnly(mTag.getServiceHandle()); 358 switch (errorCode) { 359 case ErrorCodes.SUCCESS: 360 return true; 361 case ErrorCodes.ERROR_IO: 362 throw new IOException(); 363 case ErrorCodes.ERROR_INVALID_PARAM: 364 return false; 365 default: 366 // Should not happen 367 throw new IOException(); 368 } 369 } 370 else { 371 throw new IOException("Tag is not ndef"); 372 } 373 } catch (RemoteException e) { 374 Log.e(TAG, "NFC service dead", e); 375 return false; 376 } 377 } 378} 379