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