blob: 26571ff04bf1592f02599f57fb7c997ba7c22260 [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;
Martijn Coenena37fcbc2011-08-05 09:56:17 -070024import java.nio.charset.Charset;
Nick Pellye0180d02011-06-07 17:27:44 -070025import java.nio.charset.Charsets;
26import java.util.Arrays;
Nick Pellydc993792010-10-04 11:17:25 -070027
28/**
Scott Mainc9f78902010-10-15 09:15:09 -070029 * Represents a logical (unchunked) NDEF (NFC Data Exchange Format) record.
30 * <p>An NDEF record always contains:
Nick Pellydc993792010-10-04 11:17:25 -070031 * <ul>
Scott Mainc9f78902010-10-15 09:15:09 -070032 * <li>3-bit TNF (Type Name Format) field: Indicates how to interpret the type field
33 * <li>Variable length type: Describes the record format
34 * <li>Variable length ID: A unique identifier for the record
35 * <li>Variable length payload: The actual data payload
Nick Pellydc993792010-10-04 11:17:25 -070036 * </ul>
Scott Mainc9f78902010-10-15 09:15:09 -070037 * <p>The underlying record
Nick Pellydc993792010-10-04 11:17:25 -070038 * representation may be chunked across several NDEF records when the payload is
39 * large.
Scott Mainc9f78902010-10-15 09:15:09 -070040 * <p>This is an immutable data class.
Nick Pellydc993792010-10-04 11:17:25 -070041 */
Nick Pelly11b075e2010-10-28 13:39:37 -070042public final class NdefRecord implements Parcelable {
Nick Pellydc993792010-10-04 11:17:25 -070043 /**
44 * Indicates no type, id, or payload is associated with this NDEF Record.
45 * <p>
46 * Type, id and payload fields must all be empty to be a valid TNF_EMPTY
47 * record.
48 */
49 public static final short TNF_EMPTY = 0x00;
50
51 /**
52 * Indicates the type field uses the RTD type name format.
53 * <p>
54 * Use this TNF with RTD types such as RTD_TEXT, RTD_URI.
55 */
56 public static final short TNF_WELL_KNOWN = 0x01;
57
58 /**
59 * Indicates the type field contains a value that follows the media-type BNF
60 * construct defined by RFC 2046.
61 */
62 public static final short TNF_MIME_MEDIA = 0x02;
63
64 /**
65 * Indicates the type field contains a value that follows the absolute-URI
66 * BNF construct defined by RFC 3986.
67 */
68 public static final short TNF_ABSOLUTE_URI = 0x03;
69
70 /**
71 * Indicates the type field contains a value that follows the RTD external
72 * name specification.
73 * <p>
74 * Note this TNF should not be used with RTD_TEXT or RTD_URI constants.
75 * Those are well known RTD constants, not external RTD constants.
76 */
77 public static final short TNF_EXTERNAL_TYPE = 0x04;
78
79 /**
80 * Indicates the payload type is unknown.
81 * <p>
82 * This is similar to the "application/octet-stream" MIME type. The payload
83 * type is not explicitly encoded within the NDEF Message.
84 * <p>
85 * The type field must be empty to be a valid TNF_UNKNOWN record.
86 */
87 public static final short TNF_UNKNOWN = 0x05;
88
89 /**
90 * Indicates the payload is an intermediate or final chunk of a chunked
91 * NDEF Record.
92 * <p>
93 * The payload type is specified in the first chunk, and subsequent chunks
94 * must use TNF_UNCHANGED with an empty type field. TNF_UNCHANGED must not
95 * be used in any other situation.
96 */
97 public static final short TNF_UNCHANGED = 0x06;
98
99 /**
100 * Reserved TNF type.
101 * <p>
102 * The NFC Forum NDEF Specification v1.0 suggests for NDEF parsers to treat this
103 * value like TNF_UNKNOWN.
104 * @hide
105 */
106 public static final short TNF_RESERVED = 0x07;
107
108 /**
109 * RTD Text type. For use with TNF_WELL_KNOWN.
110 */
111 public static final byte[] RTD_TEXT = {0x54}; // "T"
112
113 /**
114 * RTD URI type. For use with TNF_WELL_KNOWN.
115 */
116 public static final byte[] RTD_URI = {0x55}; // "U"
117
118 /**
119 * RTD Smart Poster type. For use with TNF_WELL_KNOWN.
120 */
121 public static final byte[] RTD_SMART_POSTER = {0x53, 0x70}; // "Sp"
122
123 /**
124 * RTD Alternative Carrier type. For use with TNF_WELL_KNOWN.
125 */
126 public static final byte[] RTD_ALTERNATIVE_CARRIER = {0x61, 0x63}; // "ac"
127
128 /**
129 * RTD Handover Carrier type. For use with TNF_WELL_KNOWN.
130 */
131 public static final byte[] RTD_HANDOVER_CARRIER = {0x48, 0x63}; // "Hc"
132
133 /**
134 * RTD Handover Request type. For use with TNF_WELL_KNOWN.
135 */
136 public static final byte[] RTD_HANDOVER_REQUEST = {0x48, 0x72}; // "Hr"
137
138 /**
139 * RTD Handover Select type. For use with TNF_WELL_KNOWN.
140 */
141 public static final byte[] RTD_HANDOVER_SELECT = {0x48, 0x73}; // "Hs"
142
Martijn Coenena37fcbc2011-08-05 09:56:17 -0700143 /**
144 * RTD Android app type. For use with TNF_EXTERNAL.
145 * <p>
146 * The payload of a record with type RTD_ANDROID_APP
147 * should be the package name identifying an application.
148 * Multiple RTD_ANDROID_APP records may be included
149 * in a single {@link NdefMessage}.
150 * <p>
151 * Use {@link #createApplicationRecord(String)} to create
152 * RTD_ANDROID_APP records.
153 * @hide
154 */
Martijn Coenena37fcbc2011-08-05 09:56:17 -0700155 public static final byte[] RTD_ANDROID_APP = "android.com:pkg".getBytes();
156
Nick Pelly590b73b2010-10-12 13:00:50 -0700157 private static final byte FLAG_MB = (byte) 0x80;
158 private static final byte FLAG_ME = (byte) 0x40;
159 private static final byte FLAG_CF = (byte) 0x20;
160 private static final byte FLAG_SR = (byte) 0x10;
161 private static final byte FLAG_IL = (byte) 0x08;
162
Nick Pellye0180d02011-06-07 17:27:44 -0700163 /**
164 * NFC Forum "URI Record Type Definition"
165 *
166 * This is a mapping of "URI Identifier Codes" to URI string prefixes,
167 * per section 3.2.2 of the NFC Forum URI Record Type Definition document.
168 */
169 private static final String[] URI_PREFIX_MAP = new String[] {
170 "", // 0x00
171 "http://www.", // 0x01
172 "https://www.", // 0x02
173 "http://", // 0x03
174 "https://", // 0x04
175 "tel:", // 0x05
176 "mailto:", // 0x06
177 "ftp://anonymous:anonymous@", // 0x07
178 "ftp://ftp.", // 0x08
179 "ftps://", // 0x09
180 "sftp://", // 0x0A
181 "smb://", // 0x0B
182 "nfs://", // 0x0C
183 "ftp://", // 0x0D
184 "dav://", // 0x0E
185 "news:", // 0x0F
186 "telnet://", // 0x10
187 "imap:", // 0x11
188 "rtsp://", // 0x12
189 "urn:", // 0x13
190 "pop:", // 0x14
191 "sip:", // 0x15
192 "sips:", // 0x16
193 "tftp:", // 0x17
194 "btspp://", // 0x18
195 "btl2cap://", // 0x19
196 "btgoep://", // 0x1A
197 "tcpobex://", // 0x1B
198 "irdaobex://", // 0x1C
199 "file://", // 0x1D
200 "urn:epc:id:", // 0x1E
201 "urn:epc:tag:", // 0x1F
202 "urn:epc:pat:", // 0x20
203 "urn:epc:raw:", // 0x21
204 "urn:epc:", // 0x22
205 };
206
Nick Pelly590b73b2010-10-12 13:00:50 -0700207 private final byte mFlags;
208 private final short mTnf;
209 private final byte[] mType;
210 private final byte[] mId;
211 private final byte[] mPayload;
212
Nick Pellydc993792010-10-04 11:17:25 -0700213 /**
214 * Construct an NDEF Record.
215 * <p>
216 * Applications should not attempt to manually chunk NDEF Records - the
217 * implementation of android.nfc will automatically chunk an NDEF Record
218 * when necessary (and only present a single logical NDEF Record to the
219 * application). So applications should not use TNF_UNCHANGED.
220 *
221 * @param tnf a 3-bit TNF constant
222 * @param type byte array, containing zero to 255 bytes, must not be null
223 * @param id byte array, containing zero to 255 bytes, must not be null
224 * @param payload byte array, containing zero to (2 ** 32 - 1) bytes,
225 * must not be null
226 */
227 public NdefRecord(short tnf, byte[] type, byte[] id, byte[] payload) {
Martijn Coenen8bede172011-04-08 16:42:22 +0200228 /* New NDEF records created by applications will have FLAG_MB|FLAG_ME
229 * set by default; when multiple records are stored in a
230 * {@link NdefMessage}, these flags will be corrected when the {@link NdefMessage}
231 * is serialized to bytes.
232 */
233 this(tnf, type, id, payload, (byte)(FLAG_MB|FLAG_ME));
234 }
235
236 /**
237 * @hide
238 */
239 /*package*/ NdefRecord(short tnf, byte[] type, byte[] id, byte[] payload, byte flags) {
Nick Pelly590b73b2010-10-12 13:00:50 -0700240 /* check arguments */
241 if ((type == null) || (id == null) || (payload == null)) {
242 throw new IllegalArgumentException("Illegal null argument");
243 }
244
Nick Kralevich6df23602010-10-15 16:09:19 -0700245 if (tnf < 0 || tnf > 0x07) {
246 throw new IllegalArgumentException("TNF out of range " + tnf);
247 }
248
Nick Pelly590b73b2010-10-12 13:00:50 -0700249 /* Determine if it is a short record */
250 if(payload.length < 0xFF) {
251 flags |= FLAG_SR;
252 }
253
254 /* Determine if an id is present */
255 if(id.length != 0) {
256 flags |= FLAG_IL;
257 }
258
259 mFlags = flags;
260 mTnf = tnf;
261 mType = type.clone();
262 mId = id.clone();
263 mPayload = payload.clone();
Nick Pellydc993792010-10-04 11:17:25 -0700264 }
265
266 /**
267 * Construct an NDEF Record from raw bytes.
268 * <p>
269 * Validation is performed to make sure the header is valid, and that
270 * the id, type and payload sizes appear to be valid.
271 *
272 * @throws FormatException if the data is not a valid NDEF record
273 */
Sylvain Fonteneaudd7341f2010-10-17 15:32:33 -0700274 public NdefRecord(byte[] data) throws FormatException {
275 /* Prevent compiler to complain about unassigned final fields */
276 mFlags = 0;
277 mTnf = 0;
278 mType = null;
279 mId = null;
280 mPayload = null;
281 /* Perform actual parsing */
282 if (parseNdefRecord(data) == -1) {
283 throw new FormatException("Error while parsing NDEF record");
284 }
Nick Pellydc993792010-10-04 11:17:25 -0700285 }
286
287 /**
288 * Returns the 3-bit TNF.
289 * <p>
290 * TNF is the top-level type.
291 */
292 public short getTnf() {
Nick Pelly590b73b2010-10-12 13:00:50 -0700293 return mTnf;
Nick Pellydc993792010-10-04 11:17:25 -0700294 }
295
296 /**
297 * Returns the variable length Type field.
298 * <p>
299 * This should be used in conjunction with the TNF field to determine the
300 * payload format.
301 */
302 public byte[] getType() {
Nick Pelly590b73b2010-10-12 13:00:50 -0700303 return mType.clone();
Nick Pellydc993792010-10-04 11:17:25 -0700304 }
305
306 /**
307 * Returns the variable length ID.
308 */
309 public byte[] getId() {
Nick Pelly590b73b2010-10-12 13:00:50 -0700310 return mId.clone();
Nick Pellydc993792010-10-04 11:17:25 -0700311 }
312
313 /**
314 * Returns the variable length payload.
315 */
316 public byte[] getPayload() {
Nick Pelly590b73b2010-10-12 13:00:50 -0700317 return mPayload.clone();
Nick Pellydc993792010-10-04 11:17:25 -0700318 }
319
320 /**
Nick Pellye0180d02011-06-07 17:27:44 -0700321 * Helper to return the NdefRecord as a URI.
322 * TODO: Consider making a member method instead of static
323 * TODO: Consider more validation that this is a URI record
324 * TODO: Make a public API
325 * @hide
326 */
327 public static Uri parseWellKnownUriRecord(NdefRecord record) throws FormatException {
328 byte[] payload = record.getPayload();
329 if (payload.length < 2) {
330 throw new FormatException("Payload is not a valid URI (missing prefix)");
331 }
332
333 /*
334 * payload[0] contains the URI Identifier Code, per the
335 * NFC Forum "URI Record Type Definition" section 3.2.2.
336 *
337 * payload[1]...payload[payload.length - 1] contains the rest of
338 * the URI.
339 */
340 int prefixIndex = (payload[0] & 0xff);
341 if (prefixIndex < 0 || prefixIndex >= URI_PREFIX_MAP.length) {
342 throw new FormatException("Payload is not a valid URI (invalid prefix)");
343 }
344 String prefix = URI_PREFIX_MAP[prefixIndex];
345 byte[] fullUri = concat(prefix.getBytes(Charsets.UTF_8),
346 Arrays.copyOfRange(payload, 1, payload.length));
347 return Uri.parse(new String(fullUri, Charsets.UTF_8));
348 }
349
Ben Dodson3da3a452011-06-17 10:30:49 -0700350 /**
Martijn Coenena37fcbc2011-08-05 09:56:17 -0700351 * Creates an Android application NDEF record.
352 * <p>
Nick Pelly82328bf2011-08-30 09:37:25 -0700353 * This record indicates to other Android devices the package
354 * that should be used to handle the rest of the NDEF message.
355 * You can embed this record anywhere into your NDEF message
356 * to ensure that the intended package receives the message.
357 * <p>
Martijn Coenena37fcbc2011-08-05 09:56:17 -0700358 * When an Android device dispatches an {@link NdefMessage}
359 * containing one or more Android application records,
360 * the applications contained in those records will be the
361 * preferred target for the NDEF_DISCOVERED intent, in
362 * the order in which they appear in the {@link NdefMessage}.
Nick Pelly82328bf2011-08-30 09:37:25 -0700363 * This dispatch behavior was first added to Android in
364 * Ice Cream Sandwich.
Martijn Coenena37fcbc2011-08-05 09:56:17 -0700365 * <p>
366 * If none of the applications are installed on the device,
367 * a Market link will be opened to the first application.
368 * <p>
369 * Note that Android application records do not overrule
Nick Pelly82328bf2011-08-30 09:37:25 -0700370 * applications that have called
371 * {@link NfcAdapter#enableForegroundDispatch}.
372 *
373 * @param packageName Android package name
374 * @return Android application NDEF record
Martijn Coenena37fcbc2011-08-05 09:56:17 -0700375 */
Martijn Coenena37fcbc2011-08-05 09:56:17 -0700376 public static NdefRecord createApplicationRecord(String packageName) {
377 return new NdefRecord(TNF_EXTERNAL_TYPE, RTD_ANDROID_APP, new byte[] {},
378 packageName.getBytes(Charsets.US_ASCII));
379 }
380
381 /**
Ben Dodson3da3a452011-06-17 10:30:49 -0700382 * Creates an NDEF record of well known type URI.
Ben Dodson3da3a452011-06-17 10:30:49 -0700383 */
384 public static NdefRecord createUri(Uri uri) {
Jeff Hamilton1584af92011-06-30 14:51:07 -0500385 return createUri(uri.toString());
386 }
387
388 /**
389 * Creates an NDEF record of well known type URI.
Jeff Hamilton1584af92011-06-30 14:51:07 -0500390 */
391 public static NdefRecord createUri(String uriString) {
Ben Dodson3da3a452011-06-17 10:30:49 -0700392 byte prefix = 0x0;
393 for (int i = 1; i < URI_PREFIX_MAP.length; i++) {
394 if (uriString.startsWith(URI_PREFIX_MAP[i])) {
395 prefix = (byte) i;
396 uriString = uriString.substring(URI_PREFIX_MAP[i].length());
397 break;
398 }
399 }
400 byte[] uriBytes = uriString.getBytes(Charsets.UTF_8);
401 byte[] recordBytes = new byte[uriBytes.length + 1];
402 recordBytes[0] = prefix;
403 System.arraycopy(uriBytes, 0, recordBytes, 1, uriBytes.length);
404 return new NdefRecord(TNF_WELL_KNOWN, RTD_URI, new byte[0], recordBytes);
405 }
406
Nick Pellye0180d02011-06-07 17:27:44 -0700407 private static byte[] concat(byte[]... arrays) {
408 int length = 0;
409 for (byte[] array : arrays) {
410 length += array.length;
411 }
412 byte[] result = new byte[length];
413 int pos = 0;
414 for (byte[] array : arrays) {
415 System.arraycopy(array, 0, result, pos, array.length);
416 pos += array.length;
417 }
418 return result;
419 }
420
421 /**
Jeff Hamiltonda83f512010-10-21 22:39:30 -0500422 * Returns this entire NDEF Record as a byte array.
Nick Pellydc993792010-10-04 11:17:25 -0700423 */
424 public byte[] toByteArray() {
Nick Pelly590b73b2010-10-12 13:00:50 -0700425 return generate(mFlags, mTnf, mType, mId, mPayload);
Nick Pellydc993792010-10-04 11:17:25 -0700426 }
427
Nick Pellydc993792010-10-04 11:17:25 -0700428 public int describeContents() {
429 return 0;
430 }
431
Nick Pellydc993792010-10-04 11:17:25 -0700432 public void writeToParcel(Parcel dest, int flags) {
Martijn Coenen8bede172011-04-08 16:42:22 +0200433 dest.writeInt(mFlags);
Nick Pelly590b73b2010-10-12 13:00:50 -0700434 dest.writeInt(mTnf);
435 dest.writeInt(mType.length);
436 dest.writeByteArray(mType);
437 dest.writeInt(mId.length);
438 dest.writeByteArray(mId);
439 dest.writeInt(mPayload.length);
440 dest.writeByteArray(mPayload);
Nick Pellydc993792010-10-04 11:17:25 -0700441 }
442
443 public static final Parcelable.Creator<NdefRecord> CREATOR =
444 new Parcelable.Creator<NdefRecord>() {
445 public NdefRecord createFromParcel(Parcel in) {
Martijn Coenen8bede172011-04-08 16:42:22 +0200446 byte flags = (byte)in.readInt();
Nick Pelly590b73b2010-10-12 13:00:50 -0700447 short tnf = (short)in.readInt();
448 int typeLength = in.readInt();
449 byte[] type = new byte[typeLength];
450 in.readByteArray(type);
451 int idLength = in.readInt();
452 byte[] id = new byte[idLength];
453 in.readByteArray(id);
454 int payloadLength = in.readInt();
455 byte[] payload = new byte[payloadLength];
456 in.readByteArray(payload);
457
Martijn Coenen8bede172011-04-08 16:42:22 +0200458 return new NdefRecord(tnf, type, id, payload, flags);
Nick Pellydc993792010-10-04 11:17:25 -0700459 }
460 public NdefRecord[] newArray(int size) {
Nick Pelly590b73b2010-10-12 13:00:50 -0700461 return new NdefRecord[size];
Nick Pellydc993792010-10-04 11:17:25 -0700462 }
463 };
Nick Pelly590b73b2010-10-12 13:00:50 -0700464
Sylvain Fonteneaudd7341f2010-10-17 15:32:33 -0700465 private native int parseNdefRecord(byte[] data);
Nick Pelly590b73b2010-10-12 13:00:50 -0700466 private native byte[] generate(short flags, short tnf, byte[] type, byte[] id, byte[] data);
Martijn Coenen8bede172011-04-08 16:42:22 +0200467}