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.os.Parcel;
20import android.os.Parcelable;
21
22import java.lang.UnsupportedOperationException;
23
24/**
25 * Represents a logical (unchunked) NDEF (NFC Data Exchange Format) record.
26 * <p>An NDEF record always contains:
27 * <ul>
28 * <li>3-bit TNF (Type Name Format) field: Indicates how to interpret the type field
29 * <li>Variable length type: Describes the record format
30 * <li>Variable length ID: A unique identifier for the record
31 * <li>Variable length payload: The actual data payload
32 * </ul>
33 * <p>The underlying record
34 * representation may be chunked across several NDEF records when the payload is
35 * large.
36 * <p>This is an immutable data class.
37 */
38public final class NdefRecord implements Parcelable {
39    /**
40     * Indicates no type, id, or payload is associated with this NDEF Record.
41     * <p>
42     * Type, id and payload fields must all be empty to be a valid TNF_EMPTY
43     * record.
44     */
45    public static final short TNF_EMPTY = 0x00;
46
47    /**
48     * Indicates the type field uses the RTD type name format.
49     * <p>
50     * Use this TNF with RTD types such as RTD_TEXT, RTD_URI.
51     */
52    public static final short TNF_WELL_KNOWN = 0x01;
53
54    /**
55     * Indicates the type field contains a value that follows the media-type BNF
56     * construct defined by RFC 2046.
57     */
58    public static final short TNF_MIME_MEDIA = 0x02;
59
60    /**
61     * Indicates the type field contains a value that follows the absolute-URI
62     * BNF construct defined by RFC 3986.
63     */
64    public static final short TNF_ABSOLUTE_URI = 0x03;
65
66    /**
67     * Indicates the type field contains a value that follows the RTD external
68     * name specification.
69     * <p>
70     * Note this TNF should not be used with RTD_TEXT or RTD_URI constants.
71     * Those are well known RTD constants, not external RTD constants.
72     */
73    public static final short TNF_EXTERNAL_TYPE = 0x04;
74
75    /**
76     * Indicates the payload type is unknown.
77     * <p>
78     * This is similar to the "application/octet-stream" MIME type. The payload
79     * type is not explicitly encoded within the NDEF Message.
80     * <p>
81     * The type field must be empty to be a valid TNF_UNKNOWN record.
82     */
83    public static final short TNF_UNKNOWN = 0x05;
84
85    /**
86     * Indicates the payload is an intermediate or final chunk of a chunked
87     * NDEF Record.
88     * <p>
89     * The payload type is specified in the first chunk, and subsequent chunks
90     * must use TNF_UNCHANGED with an empty type field. TNF_UNCHANGED must not
91     * be used in any other situation.
92     */
93    public static final short TNF_UNCHANGED = 0x06;
94
95    /**
96     * Reserved TNF type.
97     * <p>
98     * The NFC Forum NDEF Specification v1.0 suggests for NDEF parsers to treat this
99     * value like TNF_UNKNOWN.
100     * @hide
101     */
102    public static final short TNF_RESERVED = 0x07;
103
104    /**
105     * RTD Text type. For use with TNF_WELL_KNOWN.
106     */
107    public static final byte[] RTD_TEXT = {0x54};  // "T"
108
109    /**
110     * RTD URI type. For use with TNF_WELL_KNOWN.
111     */
112    public static final byte[] RTD_URI = {0x55};   // "U"
113
114    /**
115     * RTD Smart Poster type. For use with TNF_WELL_KNOWN.
116     */
117    public static final byte[] RTD_SMART_POSTER = {0x53, 0x70};  // "Sp"
118
119    /**
120     * RTD Alternative Carrier type. For use with TNF_WELL_KNOWN.
121     */
122    public static final byte[] RTD_ALTERNATIVE_CARRIER = {0x61, 0x63};  // "ac"
123
124    /**
125     * RTD Handover Carrier type. For use with TNF_WELL_KNOWN.
126     */
127    public static final byte[] RTD_HANDOVER_CARRIER = {0x48, 0x63};  // "Hc"
128
129    /**
130     * RTD Handover Request type. For use with TNF_WELL_KNOWN.
131     */
132    public static final byte[] RTD_HANDOVER_REQUEST = {0x48, 0x72};  // "Hr"
133
134    /**
135     * RTD Handover Select type. For use with TNF_WELL_KNOWN.
136     */
137    public static final byte[] RTD_HANDOVER_SELECT = {0x48, 0x73}; // "Hs"
138
139    private static final byte FLAG_MB = (byte) 0x80;
140    private static final byte FLAG_ME = (byte) 0x40;
141    private static final byte FLAG_CF = (byte) 0x20;
142    private static final byte FLAG_SR = (byte) 0x10;
143    private static final byte FLAG_IL = (byte) 0x08;
144
145    private final byte mFlags;
146    private final short mTnf;
147    private final byte[] mType;
148    private final byte[] mId;
149    private final byte[] mPayload;
150
151    /**
152     * Construct an NDEF Record.
153     * <p>
154     * Applications should not attempt to manually chunk NDEF Records - the
155     * implementation of android.nfc will automatically chunk an NDEF Record
156     * when necessary (and only present a single logical NDEF Record to the
157     * application). So applications should not use TNF_UNCHANGED.
158     *
159     * @param tnf  a 3-bit TNF constant
160     * @param type byte array, containing zero to 255 bytes, must not be null
161     * @param id   byte array, containing zero to 255 bytes, must not be null
162     * @param payload byte array, containing zero to (2 ** 32 - 1) bytes,
163     *                must not be null
164     */
165    public NdefRecord(short tnf, byte[] type, byte[] id, byte[] payload) {
166        /* check arguments */
167        if ((type == null) || (id == null) || (payload == null)) {
168            throw new IllegalArgumentException("Illegal null argument");
169        }
170
171        if (tnf < 0 || tnf > 0x07) {
172            throw new IllegalArgumentException("TNF out of range " + tnf);
173        }
174
175        /* generate flag */
176        byte flags = FLAG_MB | FLAG_ME;
177
178        /* Determine if it is a short record */
179        if(payload.length < 0xFF) {
180            flags |= FLAG_SR;
181        }
182
183        /* Determine if an id is present */
184        if(id.length != 0) {
185            flags |= FLAG_IL;
186        }
187
188        mFlags = flags;
189        mTnf = tnf;
190        mType = type.clone();
191        mId = id.clone();
192        mPayload = payload.clone();
193    }
194
195    /**
196     * Construct an NDEF Record from raw bytes.
197     * <p>
198     * Validation is performed to make sure the header is valid, and that
199     * the id, type and payload sizes appear to be valid.
200     *
201     * @throws FormatException if the data is not a valid NDEF record
202     */
203    public NdefRecord(byte[] data) throws FormatException {
204        /* Prevent compiler to complain about unassigned final fields */
205        mFlags = 0;
206        mTnf = 0;
207        mType = null;
208        mId = null;
209        mPayload = null;
210        /* Perform actual parsing */
211        if (parseNdefRecord(data) == -1) {
212            throw new FormatException("Error while parsing NDEF record");
213        }
214    }
215
216    /**
217     * Returns the 3-bit TNF.
218     * <p>
219     * TNF is the top-level type.
220     */
221    public short getTnf() {
222        return mTnf;
223    }
224
225    /**
226     * Returns the variable length Type field.
227     * <p>
228     * This should be used in conjunction with the TNF field to determine the
229     * payload format.
230     */
231    public byte[] getType() {
232        return mType.clone();
233    }
234
235    /**
236     * Returns the variable length ID.
237     */
238    public byte[] getId() {
239        return mId.clone();
240    }
241
242    /**
243     * Returns the variable length payload.
244     */
245    public byte[] getPayload() {
246        return mPayload.clone();
247    }
248
249    /**
250     * Returns this entire NDEF Record as a byte array.
251     */
252    public byte[] toByteArray() {
253        return generate(mFlags, mTnf, mType, mId, mPayload);
254    }
255
256    public int describeContents() {
257        return 0;
258    }
259
260    public void writeToParcel(Parcel dest, int flags) {
261        dest.writeInt(mTnf);
262        dest.writeInt(mType.length);
263        dest.writeByteArray(mType);
264        dest.writeInt(mId.length);
265        dest.writeByteArray(mId);
266        dest.writeInt(mPayload.length);
267        dest.writeByteArray(mPayload);
268    }
269
270    public static final Parcelable.Creator<NdefRecord> CREATOR =
271            new Parcelable.Creator<NdefRecord>() {
272        public NdefRecord createFromParcel(Parcel in) {
273            short tnf = (short)in.readInt();
274            int typeLength = in.readInt();
275            byte[] type = new byte[typeLength];
276            in.readByteArray(type);
277            int idLength = in.readInt();
278            byte[] id = new byte[idLength];
279            in.readByteArray(id);
280            int payloadLength = in.readInt();
281            byte[] payload = new byte[payloadLength];
282            in.readByteArray(payload);
283
284            return new NdefRecord(tnf, type, id, payload);
285        }
286        public NdefRecord[] newArray(int size) {
287            return new NdefRecord[size];
288        }
289    };
290
291    private native int parseNdefRecord(byte[] data);
292    private native byte[] generate(short flags, short tnf, byte[] type, byte[] id, byte[] data);
293}