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; 18 19import android.content.Context; 20import android.nfc.tech.IsoDep; 21import android.nfc.tech.MifareClassic; 22import android.nfc.tech.MifareUltralight; 23import android.nfc.tech.Ndef; 24import android.nfc.tech.NdefFormatable; 25import android.nfc.tech.NfcA; 26import android.nfc.tech.NfcB; 27import android.nfc.tech.NfcF; 28import android.nfc.tech.NfcV; 29import android.nfc.tech.TagTechnology; 30import android.os.Bundle; 31import android.os.Parcel; 32import android.os.Parcelable; 33 34import java.util.Arrays; 35 36/** 37 * Represents an NFC tag that has been discovered. 38 * <p> 39 * {@link Tag} is an immutable object that represents the state of a NFC tag at 40 * the time of discovery. It can be used as a handle to {@link TagTechnology} classes 41 * to perform advanced operations, or directly queried for its ID via {@link #getId} and the 42 * set of technologies it contains via {@link #getTechList}. Arrays passed to and 43 * returned by this class are <em>not</em> cloned, so be careful not to modify them. 44 * <p> 45 * A new tag object is created every time a tag is discovered (comes into range), even 46 * if it is the same physical tag. If a tag is removed and then returned into range, then 47 * only the most recent tag object can be successfully used to create a {@link TagTechnology}. 48 * 49 * <h3>Tag Dispatch</h3> 50 * When a tag is discovered, a {@link Tag} object is created and passed to a 51 * single activity via the {@link NfcAdapter#EXTRA_TAG} extra in an 52 * {@link android.content.Intent} via {@link Context#startActivity}. A four stage dispatch is used 53 * to select the 54 * most appropriate activity to handle the tag. The Android OS executes each stage in order, 55 * and completes dispatch as soon as a single matching activity is found. If there are multiple 56 * matching activities found at any one stage then the Android activity chooser dialog is shown 57 * to allow the user to select the activity to receive the tag. 58 * 59 * <p>The Tag dispatch mechanism was designed to give a high probability of dispatching 60 * a tag to the correct activity without showing the user an activity chooser dialog. 61 * This is important for NFC interactions because they are very transient -- if a user has to 62 * move the Android device to choose an application then the connection will likely be broken. 63 * 64 * <h4>1. Foreground activity dispatch</h4> 65 * A foreground activity that has called 66 * {@link NfcAdapter#enableForegroundDispatch NfcAdapter.enableForegroundDispatch()} is 67 * given priority. See the documentation on 68 * {@link NfcAdapter#enableForegroundDispatch NfcAdapter.enableForegroundDispatch()} for 69 * its usage. 70 * <h4>2. NDEF data dispatch</h4> 71 * If the tag contains NDEF data the system inspects the first {@link NdefRecord} in the first 72 * {@link NdefMessage}. If the record is a URI, SmartPoster, or MIME data 73 * {@link Context#startActivity} is called with {@link NfcAdapter#ACTION_NDEF_DISCOVERED}. For URI 74 * and SmartPoster records the URI is put into the intent's data field. For MIME records the MIME 75 * type is put in the intent's type field. This allows activities to register to be launched only 76 * when data they know how to handle is present on a tag. This is the preferred method of handling 77 * data on a tag since NDEF data can be stored on many types of tags and doesn't depend on a 78 * specific tag technology. 79 * See {@link NfcAdapter#ACTION_NDEF_DISCOVERED} for more detail. If the tag does not contain 80 * NDEF data, or if no activity is registered 81 * for {@link NfcAdapter#ACTION_NDEF_DISCOVERED} with a matching data URI or MIME type then dispatch 82 * moves to stage 3. 83 * <h4>3. Tag Technology dispatch</h4> 84 * {@link Context#startActivity} is called with {@link NfcAdapter#ACTION_TECH_DISCOVERED} to 85 * dispatch the tag to an activity that can handle the technologies present on the tag. 86 * Technologies are defined as sub-classes of {@link TagTechnology}, see the package 87 * {@link android.nfc.tech}. The Android OS looks for an activity that can handle one or 88 * more technologies in the tag. See {@link NfcAdapter#ACTION_TECH_DISCOVERED} for more detail. 89 * <h4>4. Fall-back dispatch</h4> 90 * If no activity has been matched then {@link Context#startActivity} is called with 91 * {@link NfcAdapter#ACTION_TAG_DISCOVERED}. This is intended as a fall-back mechanism. 92 * See {@link NfcAdapter#ACTION_TAG_DISCOVERED}. 93 * 94 * <h3>NFC Tag Background</h3> 95 * An NFC tag is a passive NFC device, powered by the NFC field of this Android device while 96 * it is in range. Tag's can come in many forms, such as stickers, cards, key fobs, or 97 * even embedded in a more sophisticated device. 98 * <p> 99 * Tags can have a wide range of capabilities. Simple tags just offer read/write semantics, 100 * and contain some one time 101 * programmable areas to make read-only. More complex tags offer math operations 102 * and per-sector access control and authentication. The most sophisticated tags 103 * contain operating environments allowing complex interactions with the 104 * code executing on the tag. Use {@link TagTechnology} classes to access a broad 105 * range of capabilities available in NFC tags. 106 * <p> 107 */ 108public final class Tag implements Parcelable { 109 /*package*/ final byte[] mId; 110 /*package*/ final int[] mTechList; 111 /*package*/ final String[] mTechStringList; 112 /*package*/ final Bundle[] mTechExtras; 113 /*package*/ final int mServiceHandle; // for use by NFC service, 0 indicates a mock 114 /*package*/ final INfcTag mTagService; 115 116 /*package*/ int mConnectedTechnology; 117 118 /** 119 * Hidden constructor to be used by NFC service and internal classes. 120 * @hide 121 */ 122 public Tag(byte[] id, int[] techList, Bundle[] techListExtras, int serviceHandle, 123 INfcTag tagService) { 124 if (techList == null) { 125 throw new IllegalArgumentException("rawTargets cannot be null"); 126 } 127 mId = id; 128 mTechList = Arrays.copyOf(techList, techList.length); 129 mTechStringList = generateTechStringList(techList); 130 // Ensure mTechExtras is as long as mTechList 131 mTechExtras = Arrays.copyOf(techListExtras, techList.length); 132 mServiceHandle = serviceHandle; 133 mTagService = tagService; 134 135 mConnectedTechnology = -1; 136 } 137 138 /** 139 * Construct a mock Tag. 140 * <p>This is an application constructed tag, so NfcAdapter methods on this Tag may fail 141 * with {@link IllegalArgumentException} since it does not represent a physical Tag. 142 * <p>This constructor might be useful for mock testing. 143 * @param id The tag identifier, can be null 144 * @param techList must not be null 145 * @return freshly constructed tag 146 * @hide 147 */ 148 public static Tag createMockTag(byte[] id, int[] techList, Bundle[] techListExtras) { 149 // set serviceHandle to 0 to indicate mock tag 150 return new Tag(id, techList, techListExtras, 0, null); 151 } 152 153 private String[] generateTechStringList(int[] techList) { 154 final int size = techList.length; 155 String[] strings = new String[size]; 156 for (int i = 0; i < size; i++) { 157 switch (techList[i]) { 158 case TagTechnology.ISO_DEP: 159 strings[i] = IsoDep.class.getName(); 160 break; 161 case TagTechnology.MIFARE_CLASSIC: 162 strings[i] = MifareClassic.class.getName(); 163 break; 164 case TagTechnology.MIFARE_ULTRALIGHT: 165 strings[i] = MifareUltralight.class.getName(); 166 break; 167 case TagTechnology.NDEF: 168 strings[i] = Ndef.class.getName(); 169 break; 170 case TagTechnology.NDEF_FORMATABLE: 171 strings[i] = NdefFormatable.class.getName(); 172 break; 173 case TagTechnology.NFC_A: 174 strings[i] = NfcA.class.getName(); 175 break; 176 case TagTechnology.NFC_B: 177 strings[i] = NfcB.class.getName(); 178 break; 179 case TagTechnology.NFC_F: 180 strings[i] = NfcF.class.getName(); 181 break; 182 case TagTechnology.NFC_V: 183 strings[i] = NfcV.class.getName(); 184 break; 185 default: 186 throw new IllegalArgumentException("Unknown tech type " + techList[i]); 187 } 188 } 189 return strings; 190 } 191 192 /** 193 * For use by NfcService only. 194 * @hide 195 */ 196 public int getServiceHandle() { 197 return mServiceHandle; 198 } 199 200 /** 201 * Get the Tag Identifier (if it has one). 202 * <p>The tag identifier is a low level serial number, used for anti-collision 203 * and identification. 204 * <p> Most tags have a stable unique identifier 205 * (UID), but some tags will generate a random ID every time they are discovered 206 * (RID), and there are some tags with no ID at all (the byte array will be zero-sized). 207 * <p> The size and format of an ID is specific to the RF technology used by the tag. 208 * <p> This function retrieves the ID as determined at discovery time, and does not 209 * perform any further RF communication or block. 210 * @return ID as byte array, never null 211 */ 212 public byte[] getId() { 213 return mId; 214 } 215 216 /** 217 * Get the technologies available in this tag, as fully qualified class names. 218 * <p> 219 * A technology is an implementation of the {@link TagTechnology} interface, 220 * and can be instantiated by calling the static <code>get(Tag)</code> 221 * method on the implementation with this Tag. The {@link TagTechnology} 222 * object can then be used to perform advanced, technology-specific operations on a tag. 223 * <p> 224 * Android defines a mandatory set of technologies that must be correctly 225 * enumerated by all Android NFC devices, and an optional 226 * set of proprietary technologies. 227 * See {@link TagTechnology} for more details. 228 * <p> 229 * The ordering of the returned array is undefined and should not be relied upon. 230 * @return an array of fully-qualified {@link TagTechnology} class-names. 231 */ 232 public String[] getTechList() { 233 return mTechStringList; 234 } 235 236 /** @hide */ 237 public boolean hasTech(int techType) { 238 for (int tech : mTechList) { 239 if (tech == techType) return true; 240 } 241 return false; 242 } 243 244 /** @hide */ 245 public Bundle getTechExtras(int tech) { 246 int pos = -1; 247 for (int idx = 0; idx < mTechList.length; idx++) { 248 if (mTechList[idx] == tech) { 249 pos = idx; 250 break; 251 } 252 } 253 if (pos < 0) { 254 return null; 255 } 256 257 return mTechExtras[pos]; 258 } 259 260 /** @hide */ 261 public INfcTag getTagService() { 262 return mTagService; 263 } 264 265 /** 266 * Human-readable description of the tag, for debugging. 267 */ 268 @Override 269 public String toString() { 270 StringBuilder sb = new StringBuilder("TAG ") 271 .append("uid = ") 272 .append(mId) 273 .append(" Tech ["); 274 for (int i : mTechList) { 275 sb.append(i) 276 .append(", "); 277 } 278 return sb.toString(); 279 } 280 281 /*package*/ static byte[] readBytesWithNull(Parcel in) { 282 int len = in.readInt(); 283 byte[] result = null; 284 if (len >= 0) { 285 result = new byte[len]; 286 in.readByteArray(result); 287 } 288 return result; 289 } 290 291 /*package*/ static void writeBytesWithNull(Parcel out, byte[] b) { 292 if (b == null) { 293 out.writeInt(-1); 294 return; 295 } 296 out.writeInt(b.length); 297 out.writeByteArray(b); 298 } 299 300 @Override 301 public int describeContents() { 302 return 0; 303 } 304 305 @Override 306 public void writeToParcel(Parcel dest, int flags) { 307 // Null mTagService means this is a mock tag 308 int isMock = (mTagService == null)?1:0; 309 310 writeBytesWithNull(dest, mId); 311 dest.writeInt(mTechList.length); 312 dest.writeIntArray(mTechList); 313 dest.writeTypedArray(mTechExtras, 0); 314 dest.writeInt(mServiceHandle); 315 dest.writeInt(isMock); 316 if (isMock == 0) { 317 dest.writeStrongBinder(mTagService.asBinder()); 318 } 319 } 320 321 public static final Parcelable.Creator<Tag> CREATOR = 322 new Parcelable.Creator<Tag>() { 323 @Override 324 public Tag createFromParcel(Parcel in) { 325 INfcTag tagService; 326 327 // Tag fields 328 byte[] id = Tag.readBytesWithNull(in); 329 int[] techList = new int[in.readInt()]; 330 in.readIntArray(techList); 331 Bundle[] techExtras = in.createTypedArray(Bundle.CREATOR); 332 int serviceHandle = in.readInt(); 333 int isMock = in.readInt(); 334 if (isMock == 0) { 335 tagService = INfcTag.Stub.asInterface(in.readStrongBinder()); 336 } 337 else { 338 tagService = null; 339 } 340 341 return new Tag(id, techList, techExtras, serviceHandle, tagService); 342 } 343 344 @Override 345 public Tag[] newArray(int size) { 346 return new Tag[size]; 347 } 348 }; 349 350 /** 351 * For internal use only. 352 * 353 * @hide 354 */ 355 public synchronized void setConnectedTechnology(int technology) { 356 if (mConnectedTechnology == -1) { 357 mConnectedTechnology = technology; 358 } else { 359 throw new IllegalStateException("Close other technology first!"); 360 } 361 } 362 363 /** 364 * For internal use only. 365 * 366 * @hide 367 */ 368 public int getConnectedTechnology() { 369 return mConnectedTechnology; 370 } 371 372 /** 373 * For internal use only. 374 * 375 * @hide 376 */ 377 public void setTechnologyDisconnected() { 378 mConnectedTechnology = -1; 379 } 380} 381