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