blob: 3fd26dd755d7f7d66ee3bf57725653207c085060 [file] [log] [blame]
Nick Pellydc993792010-10-04 11:17:25 -07001/*
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
Nick Pellye0180d02011-06-07 17:27:44 -070019import android.net.Uri;
Nick Pellydc993792010-10-04 11:17:25 -070020import android.os.Parcel;
21import android.os.Parcelable;
22
23import java.lang.UnsupportedOperationException;
Nick Pellye0180d02011-06-07 17:27:44 -070024import java.nio.charset.Charsets;
25import java.util.Arrays;
Nick Pellydc993792010-10-04 11:17:25 -070026
27/**
Scott Mainc9f78902010-10-15 09:15:09 -070028 * Represents a logical (unchunked) NDEF (NFC Data Exchange Format) record.
29 * <p>An NDEF record always contains:
Nick Pellydc993792010-10-04 11:17:25 -070030 * <ul>
Scott Mainc9f78902010-10-15 09:15:09 -070031 * <li>3-bit TNF (Type Name Format) field: Indicates how to interpret the type field
32 * <li>Variable length type: Describes the record format
33 * <li>Variable length ID: A unique identifier for the record
34 * <li>Variable length payload: The actual data payload
Nick Pellydc993792010-10-04 11:17:25 -070035 * </ul>
Scott Mainc9f78902010-10-15 09:15:09 -070036 * <p>The underlying record
Nick Pellydc993792010-10-04 11:17:25 -070037 * representation may be chunked across several NDEF records when the payload is
38 * large.
Scott Mainc9f78902010-10-15 09:15:09 -070039 * <p>This is an immutable data class.
Nick Pellydc993792010-10-04 11:17:25 -070040 */
Nick Pelly11b075e2010-10-28 13:39:37 -070041public final class NdefRecord implements Parcelable {
Nick Pellydc993792010-10-04 11:17:25 -070042 /**
43 * Indicates no type, id, or payload is associated with this NDEF Record.
44 * <p>
45 * Type, id and payload fields must all be empty to be a valid TNF_EMPTY
46 * record.
47 */
48 public static final short TNF_EMPTY = 0x00;
49
50 /**
51 * Indicates the type field uses the RTD type name format.
52 * <p>
53 * Use this TNF with RTD types such as RTD_TEXT, RTD_URI.
54 */
55 public static final short TNF_WELL_KNOWN = 0x01;
56
57 /**
58 * Indicates the type field contains a value that follows the media-type BNF
59 * construct defined by RFC 2046.
60 */
61 public static final short TNF_MIME_MEDIA = 0x02;
62
63 /**
64 * Indicates the type field contains a value that follows the absolute-URI
65 * BNF construct defined by RFC 3986.
66 */
67 public static final short TNF_ABSOLUTE_URI = 0x03;
68
69 /**
70 * Indicates the type field contains a value that follows the RTD external
71 * name specification.
72 * <p>
73 * Note this TNF should not be used with RTD_TEXT or RTD_URI constants.
74 * Those are well known RTD constants, not external RTD constants.
75 */
76 public static final short TNF_EXTERNAL_TYPE = 0x04;
77
78 /**
79 * Indicates the payload type is unknown.
80 * <p>
81 * This is similar to the "application/octet-stream" MIME type. The payload
82 * type is not explicitly encoded within the NDEF Message.
83 * <p>
84 * The type field must be empty to be a valid TNF_UNKNOWN record.
85 */
86 public static final short TNF_UNKNOWN = 0x05;
87
88 /**
89 * Indicates the payload is an intermediate or final chunk of a chunked
90 * NDEF Record.
91 * <p>
92 * The payload type is specified in the first chunk, and subsequent chunks
93 * must use TNF_UNCHANGED with an empty type field. TNF_UNCHANGED must not
94 * be used in any other situation.
95 */
96 public static final short TNF_UNCHANGED = 0x06;
97
98 /**
99 * Reserved TNF type.
100 * <p>
101 * The NFC Forum NDEF Specification v1.0 suggests for NDEF parsers to treat this
102 * value like TNF_UNKNOWN.
103 * @hide
104 */
105 public static final short TNF_RESERVED = 0x07;
106
107 /**
108 * RTD Text type. For use with TNF_WELL_KNOWN.
109 */
110 public static final byte[] RTD_TEXT = {0x54}; // "T"
111
112 /**
113 * RTD URI type. For use with TNF_WELL_KNOWN.
114 */
115 public static final byte[] RTD_URI = {0x55}; // "U"
116
117 /**
118 * RTD Smart Poster type. For use with TNF_WELL_KNOWN.
119 */
120 public static final byte[] RTD_SMART_POSTER = {0x53, 0x70}; // "Sp"
121
122 /**
123 * RTD Alternative Carrier type. For use with TNF_WELL_KNOWN.
124 */
125 public static final byte[] RTD_ALTERNATIVE_CARRIER = {0x61, 0x63}; // "ac"
126
127 /**
128 * RTD Handover Carrier type. For use with TNF_WELL_KNOWN.
129 */
130 public static final byte[] RTD_HANDOVER_CARRIER = {0x48, 0x63}; // "Hc"
131
132 /**
133 * RTD Handover Request type. For use with TNF_WELL_KNOWN.
134 */
135 public static final byte[] RTD_HANDOVER_REQUEST = {0x48, 0x72}; // "Hr"
136
137 /**
138 * RTD Handover Select type. For use with TNF_WELL_KNOWN.
139 */
140 public static final byte[] RTD_HANDOVER_SELECT = {0x48, 0x73}; // "Hs"
141
Nick Pelly590b73b2010-10-12 13:00:50 -0700142 private static final byte FLAG_MB = (byte) 0x80;
143 private static final byte FLAG_ME = (byte) 0x40;
144 private static final byte FLAG_CF = (byte) 0x20;
145 private static final byte FLAG_SR = (byte) 0x10;
146 private static final byte FLAG_IL = (byte) 0x08;
147
Nick Pellye0180d02011-06-07 17:27:44 -0700148 /**
149 * NFC Forum "URI Record Type Definition"
150 *
151 * This is a mapping of "URI Identifier Codes" to URI string prefixes,
152 * per section 3.2.2 of the NFC Forum URI Record Type Definition document.
153 */
154 private static final String[] URI_PREFIX_MAP = new String[] {
155 "", // 0x00
156 "http://www.", // 0x01
157 "https://www.", // 0x02
158 "http://", // 0x03
159 "https://", // 0x04
160 "tel:", // 0x05
161 "mailto:", // 0x06
162 "ftp://anonymous:anonymous@", // 0x07
163 "ftp://ftp.", // 0x08
164 "ftps://", // 0x09
165 "sftp://", // 0x0A
166 "smb://", // 0x0B
167 "nfs://", // 0x0C
168 "ftp://", // 0x0D
169 "dav://", // 0x0E
170 "news:", // 0x0F
171 "telnet://", // 0x10
172 "imap:", // 0x11
173 "rtsp://", // 0x12
174 "urn:", // 0x13
175 "pop:", // 0x14
176 "sip:", // 0x15
177 "sips:", // 0x16
178 "tftp:", // 0x17
179 "btspp://", // 0x18
180 "btl2cap://", // 0x19
181 "btgoep://", // 0x1A
182 "tcpobex://", // 0x1B
183 "irdaobex://", // 0x1C
184 "file://", // 0x1D
185 "urn:epc:id:", // 0x1E
186 "urn:epc:tag:", // 0x1F
187 "urn:epc:pat:", // 0x20
188 "urn:epc:raw:", // 0x21
189 "urn:epc:", // 0x22
190 };
191
Nick Pelly590b73b2010-10-12 13:00:50 -0700192 private final byte mFlags;
193 private final short mTnf;
194 private final byte[] mType;
195 private final byte[] mId;
196 private final byte[] mPayload;
197
Nick Pellydc993792010-10-04 11:17:25 -0700198 /**
199 * Construct an NDEF Record.
200 * <p>
201 * Applications should not attempt to manually chunk NDEF Records - the
202 * implementation of android.nfc will automatically chunk an NDEF Record
203 * when necessary (and only present a single logical NDEF Record to the
204 * application). So applications should not use TNF_UNCHANGED.
205 *
206 * @param tnf a 3-bit TNF constant
207 * @param type byte array, containing zero to 255 bytes, must not be null
208 * @param id byte array, containing zero to 255 bytes, must not be null
209 * @param payload byte array, containing zero to (2 ** 32 - 1) bytes,
210 * must not be null
211 */
212 public NdefRecord(short tnf, byte[] type, byte[] id, byte[] payload) {
Martijn Coenen8bede172011-04-08 16:42:22 +0200213 /* New NDEF records created by applications will have FLAG_MB|FLAG_ME
214 * set by default; when multiple records are stored in a
215 * {@link NdefMessage}, these flags will be corrected when the {@link NdefMessage}
216 * is serialized to bytes.
217 */
218 this(tnf, type, id, payload, (byte)(FLAG_MB|FLAG_ME));
219 }
220
221 /**
222 * @hide
223 */
224 /*package*/ NdefRecord(short tnf, byte[] type, byte[] id, byte[] payload, byte flags) {
Nick Pelly590b73b2010-10-12 13:00:50 -0700225 /* check arguments */
226 if ((type == null) || (id == null) || (payload == null)) {
227 throw new IllegalArgumentException("Illegal null argument");
228 }
229
Nick Kralevich6df23602010-10-15 16:09:19 -0700230 if (tnf < 0 || tnf > 0x07) {
231 throw new IllegalArgumentException("TNF out of range " + tnf);
232 }
233
Nick Pelly590b73b2010-10-12 13:00:50 -0700234 /* Determine if it is a short record */
235 if(payload.length < 0xFF) {
236 flags |= FLAG_SR;
237 }
238
239 /* Determine if an id is present */
240 if(id.length != 0) {
241 flags |= FLAG_IL;
242 }
243
244 mFlags = flags;
245 mTnf = tnf;
246 mType = type.clone();
247 mId = id.clone();
248 mPayload = payload.clone();
Nick Pellydc993792010-10-04 11:17:25 -0700249 }
250
251 /**
252 * Construct an NDEF Record from raw bytes.
253 * <p>
254 * Validation is performed to make sure the header is valid, and that
255 * the id, type and payload sizes appear to be valid.
256 *
257 * @throws FormatException if the data is not a valid NDEF record
258 */
Sylvain Fonteneaudd7341f2010-10-17 15:32:33 -0700259 public NdefRecord(byte[] data) throws FormatException {
260 /* Prevent compiler to complain about unassigned final fields */
261 mFlags = 0;
262 mTnf = 0;
263 mType = null;
264 mId = null;
265 mPayload = null;
266 /* Perform actual parsing */
267 if (parseNdefRecord(data) == -1) {
268 throw new FormatException("Error while parsing NDEF record");
269 }
Nick Pellydc993792010-10-04 11:17:25 -0700270 }
271
272 /**
273 * Returns the 3-bit TNF.
274 * <p>
275 * TNF is the top-level type.
276 */
277 public short getTnf() {
Nick Pelly590b73b2010-10-12 13:00:50 -0700278 return mTnf;
Nick Pellydc993792010-10-04 11:17:25 -0700279 }
280
281 /**
282 * Returns the variable length Type field.
283 * <p>
284 * This should be used in conjunction with the TNF field to determine the
285 * payload format.
286 */
287 public byte[] getType() {
Nick Pelly590b73b2010-10-12 13:00:50 -0700288 return mType.clone();
Nick Pellydc993792010-10-04 11:17:25 -0700289 }
290
291 /**
292 * Returns the variable length ID.
293 */
294 public byte[] getId() {
Nick Pelly590b73b2010-10-12 13:00:50 -0700295 return mId.clone();
Nick Pellydc993792010-10-04 11:17:25 -0700296 }
297
298 /**
299 * Returns the variable length payload.
300 */
301 public byte[] getPayload() {
Nick Pelly590b73b2010-10-12 13:00:50 -0700302 return mPayload.clone();
Nick Pellydc993792010-10-04 11:17:25 -0700303 }
304
305 /**
Nick Pellye0180d02011-06-07 17:27:44 -0700306 * Helper to return the NdefRecord as a URI.
307 * TODO: Consider making a member method instead of static
308 * TODO: Consider more validation that this is a URI record
309 * TODO: Make a public API
310 * @hide
311 */
312 public static Uri parseWellKnownUriRecord(NdefRecord record) throws FormatException {
313 byte[] payload = record.getPayload();
314 if (payload.length < 2) {
315 throw new FormatException("Payload is not a valid URI (missing prefix)");
316 }
317
318 /*
319 * payload[0] contains the URI Identifier Code, per the
320 * NFC Forum "URI Record Type Definition" section 3.2.2.
321 *
322 * payload[1]...payload[payload.length - 1] contains the rest of
323 * the URI.
324 */
325 int prefixIndex = (payload[0] & 0xff);
326 if (prefixIndex < 0 || prefixIndex >= URI_PREFIX_MAP.length) {
327 throw new FormatException("Payload is not a valid URI (invalid prefix)");
328 }
329 String prefix = URI_PREFIX_MAP[prefixIndex];
330 byte[] fullUri = concat(prefix.getBytes(Charsets.UTF_8),
331 Arrays.copyOfRange(payload, 1, payload.length));
332 return Uri.parse(new String(fullUri, Charsets.UTF_8));
333 }
334
335 private static byte[] concat(byte[]... arrays) {
336 int length = 0;
337 for (byte[] array : arrays) {
338 length += array.length;
339 }
340 byte[] result = new byte[length];
341 int pos = 0;
342 for (byte[] array : arrays) {
343 System.arraycopy(array, 0, result, pos, array.length);
344 pos += array.length;
345 }
346 return result;
347 }
348
349 /**
Jeff Hamiltonda83f512010-10-21 22:39:30 -0500350 * Returns this entire NDEF Record as a byte array.
Nick Pellydc993792010-10-04 11:17:25 -0700351 */
352 public byte[] toByteArray() {
Nick Pelly590b73b2010-10-12 13:00:50 -0700353 return generate(mFlags, mTnf, mType, mId, mPayload);
Nick Pellydc993792010-10-04 11:17:25 -0700354 }
355
Nick Pellydc993792010-10-04 11:17:25 -0700356 public int describeContents() {
357 return 0;
358 }
359
Nick Pellydc993792010-10-04 11:17:25 -0700360 public void writeToParcel(Parcel dest, int flags) {
Martijn Coenen8bede172011-04-08 16:42:22 +0200361 dest.writeInt(mFlags);
Nick Pelly590b73b2010-10-12 13:00:50 -0700362 dest.writeInt(mTnf);
363 dest.writeInt(mType.length);
364 dest.writeByteArray(mType);
365 dest.writeInt(mId.length);
366 dest.writeByteArray(mId);
367 dest.writeInt(mPayload.length);
368 dest.writeByteArray(mPayload);
Nick Pellydc993792010-10-04 11:17:25 -0700369 }
370
371 public static final Parcelable.Creator<NdefRecord> CREATOR =
372 new Parcelable.Creator<NdefRecord>() {
373 public NdefRecord createFromParcel(Parcel in) {
Martijn Coenen8bede172011-04-08 16:42:22 +0200374 byte flags = (byte)in.readInt();
Nick Pelly590b73b2010-10-12 13:00:50 -0700375 short tnf = (short)in.readInt();
376 int typeLength = in.readInt();
377 byte[] type = new byte[typeLength];
378 in.readByteArray(type);
379 int idLength = in.readInt();
380 byte[] id = new byte[idLength];
381 in.readByteArray(id);
382 int payloadLength = in.readInt();
383 byte[] payload = new byte[payloadLength];
384 in.readByteArray(payload);
385
Martijn Coenen8bede172011-04-08 16:42:22 +0200386 return new NdefRecord(tnf, type, id, payload, flags);
Nick Pellydc993792010-10-04 11:17:25 -0700387 }
388 public NdefRecord[] newArray(int size) {
Nick Pelly590b73b2010-10-12 13:00:50 -0700389 return new NdefRecord[size];
Nick Pellydc993792010-10-04 11:17:25 -0700390 }
391 };
Nick Pelly590b73b2010-10-12 13:00:50 -0700392
Sylvain Fonteneaudd7341f2010-10-17 15:32:33 -0700393 private native int parseNdefRecord(byte[] data);
Nick Pelly590b73b2010-10-12 13:00:50 -0700394 private native byte[] generate(short flags, short tnf, byte[] type, byte[] id, byte[] data);
Martijn Coenen8bede172011-04-08 16:42:22 +0200395}