1dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly/*
2dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly * Copyright (C) 2010 The Android Open Source Project
3dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly *
4dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly * Licensed under the Apache License, Version 2.0 (the "License");
5dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly * you may not use this file except in compliance with the License.
6dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly * You may obtain a copy of the License at
7dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly *
8dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly *      http://www.apache.org/licenses/LICENSE-2.0
9dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly *
10dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly * Unless required by applicable law or agreed to in writing, software
11dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly * distributed under the License is distributed on an "AS IS" BASIS,
12dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly * See the License for the specific language governing permissions and
14dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly * limitations under the License.
15dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly */
16dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly
17dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pellypackage android.nfc;
18dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly
19c97a552023c3c71079b39092e80c9b44f25a789bNick Pellyimport android.content.Intent;
20e0180d053e956ac32c2a5ec60272927755f17251Nick Pellyimport android.net.Uri;
21dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pellyimport android.os.Parcel;
22dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pellyimport android.os.Parcelable;
237fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen
24a356bf1cd81614a94ef6c720998792480ade4c84Nick Pellyimport java.nio.BufferUnderflowException;
25a356bf1cd81614a94ef6c720998792480ade4c84Nick Pellyimport java.nio.ByteBuffer;
26d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughesimport java.nio.charset.StandardCharsets;
27a356bf1cd81614a94ef6c720998792480ade4c84Nick Pellyimport java.util.ArrayList;
28e0180d053e956ac32c2a5ec60272927755f17251Nick Pellyimport java.util.Arrays;
29a356bf1cd81614a94ef6c720998792480ade4c84Nick Pellyimport java.util.List;
30c97a552023c3c71079b39092e80c9b44f25a789bNick Pellyimport java.util.Locale;
31dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly
32dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly/**
33a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * Represents an immutable NDEF Record.
34a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * <p>
35a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * NDEF (NFC Data Exchange Format) is a light-weight binary format,
36a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * used to encapsulate typed data. It is specified by the NFC Forum,
37a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * for transmission and storage with NFC, however it is transport agnostic.
38a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * <p>
39a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * NDEF defines messages and records. An NDEF Record contains
40a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * typed data, such as MIME-type media, a URI, or a custom
41a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * application payload. An NDEF Message is a container for
42a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * one or more NDEF Records.
43a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * <p>
44a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * This class represents logical (complete) NDEF Records, and can not be
45a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * used to represent chunked (partial) NDEF Records. However
46a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * {@link NdefMessage#NdefMessage(byte[])} can be used to parse a message
47a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * containing chunked records, and will return a message with unchunked
48a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * (complete) records.
49a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * <p>
50a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * A logical NDEF Record always contains a 3-bit TNF (Type Name Field)
51a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * that provides high level typing for the rest of the record. The
52a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * remaining fields are variable length and not always present:
53dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly * <ul>
54a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * <li><em>type</em>: detailed typing for the payload</li>
55a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * <li><em>id</em>: identifier meta-data, not commonly used</li>
56a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * <li><em>payload</em>: the actual payload</li>
57dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly * </ul>
58a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * <p>
59a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * Helpers such as {@link NdefRecord#createUri}, {@link NdefRecord#createMime}
60a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * and {@link NdefRecord#createExternal} are included to create well-formatted
61a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * NDEF Records with correctly set tnf, type, id and payload fields, please
62a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * use these helpers whenever possible.
63a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * <p>
64a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * Use the constructor {@link #NdefRecord(short, byte[], byte[], byte[])}
65a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * if you know what you are doing and what to set the fields individually.
66a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * Only basic validation is performed with this constructor, so it is possible
67a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * to create records that do not confirm to the strict NFC Forum
68a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * specifications.
69a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * <p>
70a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * The binary representation of an NDEF Record includes additional flags to
71a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * indicate location with an NDEF message, provide support for chunking of
72a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * NDEF records, and to pack optional fields. This class does not expose
73a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * those details. To write an NDEF Record as binary you must first put it
74a45746efadd11bb7dfab026fb3c81a25fae74ca4Jeff Smith * into an {@link NdefMessage}, then call {@link NdefMessage#toByteArray()}.
75a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * <p class="note">
76a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * {@link NdefMessage} and {@link NdefRecord} implementations are
77a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * always available, even on Android devices that do not have NFC hardware.
78a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * <p class="note">
79a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * {@link NdefRecord}s are intended to be immutable (and thread-safe),
80a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * however they may contain mutable fields. So take care not to modify
81a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * mutable fields passed into constructors, or modify mutable fields
82a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * obtained by getter methods, unless such modification is explicitly
83a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * marked as safe.
84a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly *
85a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * @see NfcAdapter#ACTION_NDEF_DISCOVERED
86a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly * @see NdefMessage
87dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly */
8811b075e218b9921a953eeebe73fcd1a8a81f764bNick Pellypublic final class NdefRecord implements Parcelable {
89dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    /**
90a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Indicates the record is empty.<p>
91a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Type, id and payload fields are empty in a {@literal TNF_EMPTY} record.
92dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     */
93dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    public static final short TNF_EMPTY = 0x00;
94dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly
95dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    /**
96a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Indicates the type field contains a well-known RTD type name.<p>
97a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Use this tnf with RTD types such as {@link #RTD_TEXT}, {@link #RTD_URI}.
98dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     * <p>
99a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * The RTD type name format is specified in NFCForum-TS-RTD_1.0.
100a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     *
101a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @see #RTD_URI
102a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @see #RTD_TEXT
103a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @see #RTD_SMART_POSTER
104a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @see #createUri
105dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     */
106dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    public static final short TNF_WELL_KNOWN = 0x01;
107dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly
108dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    /**
109a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Indicates the type field contains a media-type BNF
110a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * construct, defined by RFC 2046.<p>
111a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Use this with MIME type names such as {@literal "image/jpeg"}, or
112a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * using the helper {@link #createMime}.
113a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     *
114a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @see #createMime
115dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     */
116dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    public static final short TNF_MIME_MEDIA = 0x02;
117dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly
118dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    /**
119a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Indicates the type field contains an absolute-URI
120a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * BNF construct defined by RFC 3986.<p>
121a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * When creating new records prefer {@link #createUri},
122a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * since it offers more compact URI encoding
123a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * ({@literal #RTD_URI} allows compression of common URI prefixes).
124a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     *
125a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @see #createUri
126dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     */
127dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    public static final short TNF_ABSOLUTE_URI = 0x03;
128dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly
129dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    /**
130a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Indicates the type field contains an external type name.<p>
131a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Used to encode custom payloads. When creating new records
132a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * use the helper {@link #createExternal}.<p>
133a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * The external-type RTD format is specified in NFCForum-TS-RTD_1.0.<p>
134dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     * <p>
135dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     * Note this TNF should not be used with RTD_TEXT or RTD_URI constants.
136dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     * Those are well known RTD constants, not external RTD constants.
137a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     *
138a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @see #createExternal
139dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     */
140dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    public static final short TNF_EXTERNAL_TYPE = 0x04;
141dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly
142dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    /**
143a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Indicates the payload type is unknown.<p>
144a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * NFC Forum explains this should be treated similarly to the
145a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * "application/octet-stream" MIME type. The payload
146a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * type is not explicitly encoded within the record.
147dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     * <p>
148a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * The type field is empty in an {@literal TNF_UNKNOWN} record.
149dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     */
150dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    public static final short TNF_UNKNOWN = 0x05;
151dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly
152dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    /**
153dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     * Indicates the payload is an intermediate or final chunk of a chunked
154a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * NDEF Record.<p>
155a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * {@literal TNF_UNCHANGED} can not be used with this class
156a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * since all {@link NdefRecord}s are already unchunked, however they
157a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * may appear in the binary format.
158dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     */
159dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    public static final short TNF_UNCHANGED = 0x06;
160dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly
161dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    /**
162dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     * Reserved TNF type.
163dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     * <p>
164dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     * The NFC Forum NDEF Specification v1.0 suggests for NDEF parsers to treat this
165dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     * value like TNF_UNKNOWN.
166dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     * @hide
167dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     */
168dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    public static final short TNF_RESERVED = 0x07;
169dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly
170dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    /**
171a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * RTD Text type. For use with {@literal TNF_WELL_KNOWN}.
172a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @see #TNF_WELL_KNOWN
173dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     */
174dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    public static final byte[] RTD_TEXT = {0x54};  // "T"
175dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly
176dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    /**
177a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * RTD URI type. For use with {@literal TNF_WELL_KNOWN}.
178a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @see #TNF_WELL_KNOWN
179dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     */
180dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    public static final byte[] RTD_URI = {0x55};   // "U"
181dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly
182dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    /**
183a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * RTD Smart Poster type. For use with {@literal TNF_WELL_KNOWN}.
184a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @see #TNF_WELL_KNOWN
185dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     */
186dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    public static final byte[] RTD_SMART_POSTER = {0x53, 0x70};  // "Sp"
187dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly
188dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    /**
189a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * RTD Alternative Carrier type. For use with {@literal TNF_WELL_KNOWN}.
190a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @see #TNF_WELL_KNOWN
191dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     */
192dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    public static final byte[] RTD_ALTERNATIVE_CARRIER = {0x61, 0x63};  // "ac"
193dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly
194dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    /**
195a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * RTD Handover Carrier type. For use with {@literal TNF_WELL_KNOWN}.
196a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @see #TNF_WELL_KNOWN
197dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     */
198dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    public static final byte[] RTD_HANDOVER_CARRIER = {0x48, 0x63};  // "Hc"
199dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly
200dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    /**
201a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * RTD Handover Request type. For use with {@literal TNF_WELL_KNOWN}.
202a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @see #TNF_WELL_KNOWN
203dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     */
204dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    public static final byte[] RTD_HANDOVER_REQUEST = {0x48, 0x72};  // "Hr"
205dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly
206dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    /**
207a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * RTD Handover Select type. For use with {@literal TNF_WELL_KNOWN}.
208a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @see #TNF_WELL_KNOWN
209dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     */
210dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    public static final byte[] RTD_HANDOVER_SELECT = {0x48, 0x73}; // "Hs"
211dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly
212a37fcbce59c8a746f641936b4de99867dbfabac9Martijn Coenen    /**
213a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * RTD Android app type. For use with {@literal TNF_EXTERNAL}.
214a37fcbce59c8a746f641936b4de99867dbfabac9Martijn Coenen     * <p>
215a37fcbce59c8a746f641936b4de99867dbfabac9Martijn Coenen     * The payload of a record with type RTD_ANDROID_APP
216a37fcbce59c8a746f641936b4de99867dbfabac9Martijn Coenen     * should be the package name identifying an application.
217a37fcbce59c8a746f641936b4de99867dbfabac9Martijn Coenen     * Multiple RTD_ANDROID_APP records may be included
218a37fcbce59c8a746f641936b4de99867dbfabac9Martijn Coenen     * in a single {@link NdefMessage}.
219a37fcbce59c8a746f641936b4de99867dbfabac9Martijn Coenen     * <p>
220a37fcbce59c8a746f641936b4de99867dbfabac9Martijn Coenen     * Use {@link #createApplicationRecord(String)} to create
221a37fcbce59c8a746f641936b4de99867dbfabac9Martijn Coenen     * RTD_ANDROID_APP records.
222a37fcbce59c8a746f641936b4de99867dbfabac9Martijn Coenen     * @hide
223a37fcbce59c8a746f641936b4de99867dbfabac9Martijn Coenen     */
224a37fcbce59c8a746f641936b4de99867dbfabac9Martijn Coenen    public static final byte[] RTD_ANDROID_APP = "android.com:pkg".getBytes();
225a37fcbce59c8a746f641936b4de99867dbfabac9Martijn Coenen
226590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly    private static final byte FLAG_MB = (byte) 0x80;
227590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly    private static final byte FLAG_ME = (byte) 0x40;
228590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly    private static final byte FLAG_CF = (byte) 0x20;
229590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly    private static final byte FLAG_SR = (byte) 0x10;
230590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly    private static final byte FLAG_IL = (byte) 0x08;
231590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly
232e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly    /**
233a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * NFC Forum "URI Record Type Definition"<p>
234e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly     * This is a mapping of "URI Identifier Codes" to URI string prefixes,
235e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly     * per section 3.2.2 of the NFC Forum URI Record Type Definition document.
236e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly     */
237e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly    private static final String[] URI_PREFIX_MAP = new String[] {
238e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "", // 0x00
239e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "http://www.", // 0x01
240e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "https://www.", // 0x02
241e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "http://", // 0x03
242e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "https://", // 0x04
243e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "tel:", // 0x05
244e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "mailto:", // 0x06
245e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "ftp://anonymous:anonymous@", // 0x07
246e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "ftp://ftp.", // 0x08
247e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "ftps://", // 0x09
248e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "sftp://", // 0x0A
249e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "smb://", // 0x0B
250e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "nfs://", // 0x0C
251e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "ftp://", // 0x0D
252e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "dav://", // 0x0E
253e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "news:", // 0x0F
254e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "telnet://", // 0x10
255e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "imap:", // 0x11
256e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "rtsp://", // 0x12
257e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "urn:", // 0x13
258e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "pop:", // 0x14
259e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "sip:", // 0x15
260e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "sips:", // 0x16
261e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "tftp:", // 0x17
262e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "btspp://", // 0x18
263e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "btl2cap://", // 0x19
264e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "btgoep://", // 0x1A
265e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "tcpobex://", // 0x1B
266e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "irdaobex://", // 0x1C
267e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "file://", // 0x1D
268e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "urn:epc:id:", // 0x1E
269e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "urn:epc:tag:", // 0x1F
270e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "urn:epc:pat:", // 0x20
271e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "urn:epc:raw:", // 0x21
272e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly            "urn:epc:", // 0x22
273f180f30b0de7330c415eee0bac83b6e41242ad9bMartijn Coenen            "urn:nfc:", // 0x23
274e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly    };
275e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly
276a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    private static final int MAX_PAYLOAD_SIZE = 10 * (1 << 20);  // 10 MB payload limit
277a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly
278a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
279a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly
280590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly    private final short mTnf;
281590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly    private final byte[] mType;
282590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly    private final byte[] mId;
283590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly    private final byte[] mPayload;
284590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly
285dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    /**
286a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Create a new Android Application Record (AAR).
287a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * <p>
288a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * This record indicates to other Android devices the package
289a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * that should be used to handle the entire NDEF message.
290a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * You can embed this record anywhere into your message
291a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * to ensure that the intended package receives the message.
292a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * <p>
293a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * When an Android device dispatches an {@link NdefMessage}
294a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * containing one or more Android application records,
295a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * the applications contained in those records will be the
296a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * preferred target for the {@link NfcAdapter#ACTION_NDEF_DISCOVERED}
297a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * intent, in the order in which they appear in the message.
298a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * This dispatch behavior was first added to Android in
299a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Ice Cream Sandwich.
300a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * <p>
301a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * If none of the applications have a are installed on the device,
302a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * a Market link will be opened to the first application.
303dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     * <p>
304a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Note that Android application records do not overrule
305a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * applications that have called
306a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * {@link NfcAdapter#enableForegroundDispatch}.
307dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     *
308a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @param packageName Android package name
309a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @return Android application NDEF record
310dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     */
311a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    public static NdefRecord createApplicationRecord(String packageName) {
312c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        if (packageName == null) throw new NullPointerException("packageName is null");
313c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        if (packageName.length() == 0) throw new IllegalArgumentException("packageName is empty");
314c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly
315a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        return new NdefRecord(TNF_EXTERNAL_TYPE, RTD_ANDROID_APP, null,
316d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes                packageName.getBytes(StandardCharsets.UTF_8));
3178bede1704717f594a0f924a57ff46f6300347e30Martijn Coenen    }
3188bede1704717f594a0f924a57ff46f6300347e30Martijn Coenen
3198bede1704717f594a0f924a57ff46f6300347e30Martijn Coenen    /**
320a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Create a new NDEF Record containing a URI.<p>
321a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Use this method to encode a URI (or URL) into an NDEF Record.<p>
322a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Uses the well known URI type representation: {@link #TNF_WELL_KNOWN}
323a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * and {@link #RTD_URI}. This is the most efficient encoding
324a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * of a URI into NDEF.<p>
325c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * The uri parameter will be normalized with
326abc43ddd8ae098de7a56afc55909f904cd933016Jesse Wilson     * {@link Uri#normalizeScheme} to set the scheme to lower case to
327c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * follow Android best practices for intent filtering.
328c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * However the unchecked exception
329c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * {@link IllegalArgumentException} may be thrown if the uri
330c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * parameter has serious problems, for example if it is empty, so always
331c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * catch this exception if you are passing user-generated data into this
332c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * method.<p>
333c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     *
334a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Reference specification: NFCForum-TS-RTD_URI_1.0
335a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     *
336a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @param uri URI to encode.
337a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @return an NDEF Record containing the URI
338c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * @throws IllegalArugmentException if the uri is empty or invalid
3398bede1704717f594a0f924a57ff46f6300347e30Martijn Coenen     */
340a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    public static NdefRecord createUri(Uri uri) {
341c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        if (uri == null) throw new NullPointerException("uri is null");
342a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly
343abc43ddd8ae098de7a56afc55909f904cd933016Jesse Wilson        uri = uri.normalizeScheme();
344c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        String uriString = uri.toString();
345c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        if (uriString.length() == 0) throw new IllegalArgumentException("uri is empty");
346590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly
347a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        byte prefix = 0;
348a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        for (int i = 1; i < URI_PREFIX_MAP.length; i++) {
349a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly            if (uriString.startsWith(URI_PREFIX_MAP[i])) {
350a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                prefix = (byte) i;
351a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                uriString = uriString.substring(URI_PREFIX_MAP[i].length());
352a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                break;
353a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly            }
354a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        }
355d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes        byte[] uriBytes = uriString.getBytes(StandardCharsets.UTF_8);
356a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        byte[] recordBytes = new byte[uriBytes.length + 1];
357a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        recordBytes[0] = prefix;
358a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        System.arraycopy(uriBytes, 0, recordBytes, 1, uriBytes.length);
359a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        return new NdefRecord(TNF_WELL_KNOWN, RTD_URI, null, recordBytes);
360a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    }
361a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly
362a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    /**
363c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * Create a new NDEF Record containing a URI.<p>
364c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * Use this method to encode a URI (or URL) into an NDEF Record.<p>
365c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * Uses the well known URI type representation: {@link #TNF_WELL_KNOWN}
366c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * and {@link #RTD_URI}. This is the most efficient encoding
367c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * of a URI into NDEF.<p>
368c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly      * The uriString parameter will be normalized with
369abc43ddd8ae098de7a56afc55909f904cd933016Jesse Wilson     * {@link Uri#normalizeScheme} to set the scheme to lower case to
370c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * follow Android best practices for intent filtering.
371c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * However the unchecked exception
372c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * {@link IllegalArgumentException} may be thrown if the uriString
373c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * parameter has serious problems, for example if it is empty, so always
374c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * catch this exception if you are passing user-generated data into this
375c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * method.<p>
376c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     *
377c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * Reference specification: NFCForum-TS-RTD_URI_1.0
378c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     *
379c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * @param uriString string URI to encode.
380c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * @return an NDEF Record containing the URI
381c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * @throws IllegalArugmentException if the uriString is empty or invalid
382c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     */
383c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly    public static NdefRecord createUri(String uriString) {
384c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        return createUri(Uri.parse(uriString));
385c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly    }
386c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly
387c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly    /**
388a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Create a new NDEF Record containing MIME data.<p>
389a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Use this method to encode MIME-typed data into an NDEF Record,
390a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * such as "text/plain", or "image/jpeg".<p>
391c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * The mimeType parameter will be normalized with
392c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * {@link Intent#normalizeMimeType} to follow Android best
393c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * practices for intent filtering, for example to force lower-case.
394c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * However the unchecked exception
395c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * {@link IllegalArgumentException} may be thrown
396c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * if the mimeType parameter has serious problems,
397c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * for example if it is empty, so always catch this
398c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * exception if you are passing user-generated data into this method.
399c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * <p>
400a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * For efficiency, This method might not make an internal copy of the
401a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * mimeData byte array, so take care not
402c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * to modify the mimeData byte array while still using the returned
403a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * NdefRecord.
404a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     *
405c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * @param mimeType a valid MIME type
406a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @param mimeData MIME data as bytes
407a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @return an NDEF Record containing the MIME-typed data
408c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * @throws IllegalArugmentException if the mimeType is empty or invalid
409c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     *
410a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     */
411a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    public static NdefRecord createMime(String mimeType, byte[] mimeData) {
412c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        if (mimeType == null) throw new NullPointerException("mimeType is null");
413c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly
414c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        // We only do basic MIME type validation: trying to follow the
415c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        // RFCs strictly only ends in tears, since there are lots of MIME
416c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        // types in common use that are not strictly valid as per RFC rules
417c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        mimeType = Intent.normalizeMimeType(mimeType);
418c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        if (mimeType.length() == 0) throw new IllegalArgumentException("mimeType is empty");
419c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        int slashIndex = mimeType.indexOf('/');
420c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        if (slashIndex == 0) throw new IllegalArgumentException("mimeType must have major type");
421c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        if (slashIndex == mimeType.length() - 1) {
422c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly            throw new IllegalArgumentException("mimeType must have minor type");
4236df23609d4c1f257b11d67b965e5b63cbdf26fcdNick Kralevich        }
424c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        // missing '/' is allowed
4256df23609d4c1f257b11d67b965e5b63cbdf26fcdNick Kralevich
426c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        // MIME RFCs suggest ASCII encoding for content-type
427d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes        byte[] typeBytes = mimeType.getBytes(StandardCharsets.US_ASCII);
428c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        return new NdefRecord(TNF_MIME_MEDIA, typeBytes, null, mimeData);
429a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    }
430a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly
431a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    /**
432a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Create a new NDEF Record containing external (application-specific) data.<p>
433a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Use this method to encode application specific data into an NDEF Record.
434a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * The data is typed by a domain name (usually your Android package name) and
435a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * a domain-specific type. This data is packaged into a "NFC Forum External
436a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Type" NDEF Record.<p>
437c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * NFC Forum requires that the domain and type used in an external record
438c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * are treated as case insensitive, however Android intent filtering is
439c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * always case sensitive. So this method will force the domain and type to
440c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * lower-case before creating the NDEF Record.<p>
441c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * The unchecked exception {@link IllegalArgumentException} will be thrown
442c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * if the domain and type have serious problems, for example if either field
443c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * is empty, so always catch this
444c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * exception if you are passing user-generated data into this method.<p>
445c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * There are no such restrictions on the payload data.<p>
446a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * For efficiency, This method might not make an internal copy of the
447a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * data byte array, so take care not
448c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * to modify the data byte array while still using the returned
449a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * NdefRecord.
450a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     *
451a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Reference specification: NFCForum-TS-RTD_1.0
452a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @param domain domain-name of issuing organization
453a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @param type domain-specific type of data
454a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @param data payload as bytes
455c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * @throws IllegalArugmentException if either domain or type are empty or invalid
456a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     */
457a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    public static NdefRecord createExternal(String domain, String type, byte[] data) {
458c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        if (domain == null) throw new NullPointerException("domain is null");
459c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        if (type == null) throw new NullPointerException("type is null");
460c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly
461cb64d430627b71221c588ef5f23599dd34a89ee9Elliott Hughes        domain = domain.trim().toLowerCase(Locale.ROOT);
462cb64d430627b71221c588ef5f23599dd34a89ee9Elliott Hughes        type = type.trim().toLowerCase(Locale.ROOT);
463c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly
464c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        if (domain.length() == 0) throw new IllegalArgumentException("domain is empty");
465c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        if (type.length() == 0) throw new IllegalArgumentException("type is empty");
466a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly
467d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes        byte[] byteDomain = domain.getBytes(StandardCharsets.UTF_8);
468d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes        byte[] byteType = type.getBytes(StandardCharsets.UTF_8);
469a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        byte[] b = new byte[byteDomain.length + 1 + byteType.length];
470a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        System.arraycopy(byteDomain, 0, b, 0, byteDomain.length);
471a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        b[byteDomain.length] = ':';
472a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        System.arraycopy(byteType, 0, b, byteDomain.length + 1, byteType.length);
473a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly
474a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        return new NdefRecord(TNF_EXTERNAL_TYPE, b, null, data);
475a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    }
476590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly
477a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    /**
4787fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen     * Create a new NDEF record containing UTF-8 text data.<p>
4797fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen     *
4807fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen     * The caller can either specify the language code for the provided text,
4817fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen     * or otherwise the language code corresponding to the current default
4827fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen     * locale will be used.
4837fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen     *
4847fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen     * Reference specification: NFCForum-TS-RTD_Text_1.0
4857fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen     * @param languageCode The languageCode for the record. If locale is empty or null,
4867fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen     *                     the language code of the current default locale will be used.
4877fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen     * @param text   The text to be encoded in the record. Will be represented in UTF-8 format.
4887fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen     * @throws IllegalArgumentException if text is null
4897fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen     */
4907fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen    public static NdefRecord createTextRecord(String languageCode, String text) {
4917fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen        if (text == null) throw new NullPointerException("text is null");
4927fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen
4937fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen        byte[] textBytes = text.getBytes(StandardCharsets.UTF_8);
4947fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen
4957fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen        byte[] languageCodeBytes = null;
4967fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen        if (languageCode != null && !languageCode.isEmpty()) {
4977fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen            languageCodeBytes = languageCode.getBytes(StandardCharsets.US_ASCII);
4987fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen        } else {
4997fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen            languageCodeBytes = Locale.getDefault().getLanguage().
5007fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen                    getBytes(StandardCharsets.US_ASCII);
5017fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen        }
5027fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen        // We only have 6 bits to indicate ISO/IANA language code.
5037fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen        if (languageCodeBytes.length >= 64) {
5047fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen            throw new IllegalArgumentException("language code is too long, must be <64 bytes.");
5057fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen        }
5067fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen        ByteBuffer buffer = ByteBuffer.allocate(1 + languageCodeBytes.length + textBytes.length);
5077fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen
5087fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen        byte status = (byte) (languageCodeBytes.length & 0xFF);
5097fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen        buffer.put(status);
5107fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen        buffer.put(languageCodeBytes);
5117fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen        buffer.put(textBytes);
5127fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen
5137fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen        return new NdefRecord(TNF_WELL_KNOWN, RTD_TEXT, null, buffer.array());
5147fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen    }
5157fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen
5167fe9fa163ccd2ec34cbf533be4422aa66356431fMartijn Coenen    /**
517a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Construct an NDEF Record from its component fields.<p>
518a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Recommend to use helpers such as {#createUri} or
519a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * {{@link #createExternal} where possible, since they perform
520a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * stricter validation that the record is correctly formatted
521a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * as per NDEF specifications. However if you know what you are
522a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * doing then this constructor offers the most flexibility.<p>
523a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * An {@link NdefRecord} represents a logical (complete)
524a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * record, and cannot represent NDEF Record chunks.<p>
525a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Basic validation of the tnf, type, id and payload is performed
526a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * as per the following rules:
527a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * <ul>
528a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * <li>The tnf paramter must be a 3-bit value.</li>
529a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * <li>Records with a tnf of {@link #TNF_EMPTY} cannot have a type,
530a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * id or payload.</li>
531a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * <li>Records with a tnf of {@link #TNF_UNKNOWN} or {@literal 0x07}
532a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * cannot have a type.</li>
533a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * <li>Records with a tnf of {@link #TNF_UNCHANGED} are not allowed
534a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * since this class only represents complete (unchunked) records.</li>
535a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * </ul>
536a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * This minimal validation is specified by
537a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * NFCForum-TS-NDEF_1.0 section 3.2.6 (Type Name Format).<p>
538a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * If any of the above validation
539a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * steps fail then {@link IllegalArgumentException} is thrown.<p>
540a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Deep inspection of the type, id and payload fields is not
541a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * performed, so it is possible to create NDEF Records
542a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * that conform to section 3.2.6
543a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * but fail other more strict NDEF specification requirements. For
544a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * example, the payload may be invalid given the tnf and type.
545a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * <p>
546a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * To omit a type, id or payload field, set the parameter to an
547a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * empty byte array or null.
548a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     *
549a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @param tnf  a 3-bit TNF constant
550a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @param type byte array, containing zero to 255 bytes, or null
551a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @param id   byte array, containing zero to 255 bytes, or null
552a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @param payload byte array, containing zero to (2 ** 32 - 1) bytes,
553a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     *                or null
554a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @throws IllegalArugmentException if a valid record cannot be created
555a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     */
556a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    public NdefRecord(short tnf, byte[] type, byte[] id, byte[] payload) {
557a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        /* convert nulls */
558a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        if (type == null) type = EMPTY_BYTE_ARRAY;
559a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        if (id == null) id = EMPTY_BYTE_ARRAY;
560a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        if (payload == null) payload = EMPTY_BYTE_ARRAY;
561a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly
562a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        String message = validateTnf(tnf, type, id, payload);
563a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        if (message != null) {
564a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly            throw new IllegalArgumentException(message);
565590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly        }
566590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly
567590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly        mTnf = tnf;
568a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        mType = type;
569a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        mId = id;
570a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        mPayload = payload;
571dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    }
572dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly
573dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    /**
574a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Construct an NDEF Record from raw bytes.<p>
575a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * This method is deprecated, use {@link NdefMessage#NdefMessage(byte[])}
576a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * instead. This is because it does not make sense to parse a record:
577a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * the NDEF binary format is only defined for a message, and the
578a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * record flags MB and ME do not make sense outside of the context of
579a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * an entire message.<p>
580a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * This implementation will attempt to parse a single record by ignoring
581a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * the MB and ME flags, and otherwise following the rules of
582a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * {@link NdefMessage#NdefMessage(byte[])}.<p>
583dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     *
584a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @param data raw bytes to parse
585a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @throws FormatException if the data cannot be parsed into a valid record
586a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @deprecated use {@link NdefMessage#NdefMessage(byte[])} instead.
587dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     */
588a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    @Deprecated
589dd7341f775ca4d86091f4106d6dfef9214073a7dSylvain Fonteneau    public NdefRecord(byte[] data) throws FormatException {
590a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        ByteBuffer buffer = ByteBuffer.wrap(data);
591a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        NdefRecord[] rs = parse(buffer, true);
592a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly
593a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        if (buffer.remaining() > 0) {
594a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly            throw new FormatException("data too long");
595dd7341f775ca4d86091f4106d6dfef9214073a7dSylvain Fonteneau        }
596a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly
597a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        mTnf = rs[0].mTnf;
598a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        mType = rs[0].mType;
599a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        mId = rs[0].mId;
600a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        mPayload = rs[0].mPayload;
601dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    }
602dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly
603dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    /**
604dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     * Returns the 3-bit TNF.
605dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     * <p>
606dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     * TNF is the top-level type.
607dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     */
608dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    public short getTnf() {
609590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly        return mTnf;
610dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    }
611dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly
612dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    /**
613dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     * Returns the variable length Type field.
614dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     * <p>
615dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     * This should be used in conjunction with the TNF field to determine the
616dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     * payload format.
617a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * <p>
618a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Returns an empty byte array if this record
619a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * does not have a type field.
620dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     */
621dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    public byte[] getType() {
622590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly        return mType.clone();
623dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    }
624dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly
625dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    /**
626dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     * Returns the variable length ID.
627a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * <p>
628a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Returns an empty byte array if this record
629a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * does not have an id field.
630dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     */
631dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    public byte[] getId() {
632590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly        return mId.clone();
633dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    }
634dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly
635dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    /**
636dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     * Returns the variable length payload.
637a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * <p>
638a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Returns an empty byte array if this record
639a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * does not have a payload field.
640dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     */
641dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    public byte[] getPayload() {
642590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly        return mPayload.clone();
643dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    }
644dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly
645dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    /**
646a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Return this NDEF Record as a byte array.<p>
647a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * This method is deprecated, use {@link NdefMessage#toByteArray}
648a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * instead. This is because the NDEF binary format is not defined for
649a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * a record outside of the context of a message: the MB and ME flags
650a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * cannot be set without knowing the location inside a message.<p>
651a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * This implementation will attempt to serialize a single record by
652a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * always setting the MB and ME flags (in other words, assume this
653a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * is a single-record NDEF Message).<p>
654a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     *
655a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @deprecated use {@link NdefMessage#toByteArray()} instead
656a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     */
657a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    @Deprecated
658a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    public byte[] toByteArray() {
659a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        ByteBuffer buffer = ByteBuffer.allocate(getByteLength());
660a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        writeToByteBuffer(buffer, true, true);
661a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        return buffer.array();
662a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    }
663a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly
664a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    /**
665c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * Map this record to a MIME type, or return null if it cannot be mapped.<p>
666c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * Currently this method considers all {@link #TNF_MIME_MEDIA} records to
667c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * be MIME records, as well as some {@link #TNF_WELL_KNOWN} records such as
668c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * {@link #RTD_TEXT}. If this is a MIME record then the MIME type as string
669c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * is returned, otherwise null is returned.<p>
670c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * This method does not perform validation that the MIME type is
671c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * actually valid. It always attempts to
672c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * return a string containing the type if this is a MIME record.<p>
673c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * The returned MIME type will by normalized to lower-case using
674c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * {@link Intent#normalizeMimeType}.<p>
675c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * The MIME payload can be obtained using {@link #getPayload}.
676c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     *
677c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * @return MIME type as a string, or null if this is not a MIME record
678e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly     */
679c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly    public String toMimeType() {
680c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        switch (mTnf) {
681c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly            case NdefRecord.TNF_WELL_KNOWN:
682c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly                if (Arrays.equals(mType, NdefRecord.RTD_TEXT)) {
683c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly                    return "text/plain";
684c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly                }
685c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly                break;
686c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly            case NdefRecord.TNF_MIME_MEDIA:
687d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes                String mimeType = new String(mType, StandardCharsets.US_ASCII);
688c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly                return Intent.normalizeMimeType(mimeType);
689e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly        }
690c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        return null;
691c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly    }
692e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly
693c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly    /**
694c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * Map this record to a URI, or return null if it cannot be mapped.<p>
695c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * Currently this method considers the following to be URI records:
696c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * <ul>
697c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * <li>{@link #TNF_ABSOLUTE_URI} records.</li>
698c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * <li>{@link #TNF_WELL_KNOWN} with a type of {@link #RTD_URI}.</li>
699c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * <li>{@link #TNF_WELL_KNOWN} with a type of {@link #RTD_SMART_POSTER}
700c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * and containing a URI record in the NDEF message nested in the payload.
701c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * </li>
702c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * <li>{@link #TNF_EXTERNAL_TYPE} records.</li>
703c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * </ul>
704c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * If this is not a URI record by the above rules, then null is returned.<p>
705c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * This method does not perform validation that the URI is
706c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * actually valid: it always attempts to create and return a URI if
707c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * this record appears to be a URI record by the above rules.<p>
708c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * The returned URI will be normalized to have a lower case scheme
709abc43ddd8ae098de7a56afc55909f904cd933016Jesse Wilson     * using {@link Uri#normalizeScheme}.<p>
710c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     *
711c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * @return URI, or null if this is not a URI record
712c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     */
713c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly    public Uri toUri() {
714c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        return toUri(false);
715c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly    }
716c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly
717c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly    private Uri toUri(boolean inSmartPoster) {
718c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        switch (mTnf) {
719c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly            case TNF_WELL_KNOWN:
720c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly                if (Arrays.equals(mType, RTD_SMART_POSTER) && !inSmartPoster) {
721c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly                    try {
722c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly                        // check payload for a nested NDEF Message containing a URI
723c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly                        NdefMessage nestedMessage = new NdefMessage(mPayload);
724c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly                        for (NdefRecord nestedRecord : nestedMessage.getRecords()) {
725c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly                            Uri uri = nestedRecord.toUri(true);
726c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly                            if (uri != null) {
727c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly                                return uri;
728c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly                            }
729c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly                        }
730c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly                    } catch (FormatException e) {  }
731c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly                } else if (Arrays.equals(mType, RTD_URI)) {
73232ac1e142c4a0f3529c49cde433ae5e22f84b1d6Martijn Coenen                    Uri wktUri = parseWktUri();
73332ac1e142c4a0f3529c49cde433ae5e22f84b1d6Martijn Coenen                    return (wktUri != null ? wktUri.normalizeScheme() : null);
734c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly                }
735c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly                break;
736c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly
737c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly            case TNF_ABSOLUTE_URI:
738d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes                Uri uri = Uri.parse(new String(mType, StandardCharsets.UTF_8));
739abc43ddd8ae098de7a56afc55909f904cd933016Jesse Wilson                return uri.normalizeScheme();
740c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly
741c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly            case TNF_EXTERNAL_TYPE:
742c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly                if (inSmartPoster) {
743c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly                    break;
744c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly                }
745d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes                return Uri.parse("vnd.android.nfc://ext/" + new String(mType, StandardCharsets.US_ASCII));
746e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly        }
747c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        return null;
748e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly    }
749e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly
750c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly    /**
751c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * Return complete URI of {@link #TNF_WELL_KNOWN}, {@link #RTD_URI} records.
752c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * @return complete URI, or null if invalid
753c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     */
754c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly    private Uri parseWktUri() {
755c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        if (mPayload.length < 2) {
756c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly            return null;
757a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        }
758c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly
759c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        // payload[0] contains the URI Identifier Code, as per
760c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        // NFC Forum "URI Record Type Definition" section 3.2.2.
761c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        int prefixIndex = (mPayload[0] & (byte)0xFF);
762c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        if (prefixIndex < 0 || prefixIndex >= URI_PREFIX_MAP.length) {
763c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly            return null;
764a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        }
765c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        String prefix = URI_PREFIX_MAP[prefixIndex];
766c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        String suffix = new String(Arrays.copyOfRange(mPayload, 1, mPayload.length),
767d396a448b2e36e29598c954b64bfddef73f3fae0Elliott Hughes                StandardCharsets.UTF_8);
768c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly        return Uri.parse(prefix + suffix);
769a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    }
770a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly
7713da3a4582c0793f59a1fd897a992e5e4fd57b6caBen Dodson    /**
772c97a552023c3c71079b39092e80c9b44f25a789bNick Pelly     * Main record parsing method.<p>
773a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Expects NdefMessage to begin immediately, allows trailing data.<p>
774a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Currently has strict validation of all fields as per NDEF 1.0
775a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * specification section 2.5. We will attempt to keep this as strict as
776a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * possible to encourage well-formatted NDEF.<p>
777a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Always returns 1 or more NdefRecord's, or throws FormatException.
77882328bfd40008d85917cc01a1b2eb8eed1f23ec4Nick Pelly     *
779a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @param buffer ByteBuffer to read from
780a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @param ignoreMbMe ignore MB and ME flags, and read only 1 complete record
781a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @return one or more records
782a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @throws FormatException on any parsing error
783a37fcbce59c8a746f641936b4de99867dbfabac9Martijn Coenen     */
784a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    static NdefRecord[] parse(ByteBuffer buffer, boolean ignoreMbMe) throws FormatException {
785a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        List<NdefRecord> records = new ArrayList<NdefRecord>();
786a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly
787a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        try {
788a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly            byte[] type = null;
789a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly            byte[] id = null;
790a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly            byte[] payload = null;
791a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly            ArrayList<byte[]> chunks = new ArrayList<byte[]>();
792a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly            boolean inChunk = false;
793a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly            short chunkTnf = -1;
794a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly            boolean me = false;
795a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly
796a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly            while (!me) {
797a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                byte flag = buffer.get();
798a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly
799a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                boolean mb = (flag & NdefRecord.FLAG_MB) != 0;
800a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                me = (flag & NdefRecord.FLAG_ME) != 0;
801a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                boolean cf = (flag & NdefRecord.FLAG_CF) != 0;
802a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                boolean sr = (flag & NdefRecord.FLAG_SR) != 0;
803a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                boolean il = (flag & NdefRecord.FLAG_IL) != 0;
804a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                short tnf = (short)(flag & 0x07);
805a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly
806a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                if (!mb && records.size() == 0 && !inChunk && !ignoreMbMe) {
807a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    throw new FormatException("expected MB flag");
808a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                } else if (mb && records.size() != 0 && !ignoreMbMe) {
809a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    throw new FormatException("unexpected MB flag");
810a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                } else if (inChunk && il) {
811a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    throw new FormatException("unexpected IL flag in non-leading chunk");
812a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                } else if (cf && me) {
813a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    throw new FormatException("unexpected ME flag in non-trailing chunk");
814a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                } else if (inChunk && tnf != NdefRecord.TNF_UNCHANGED) {
815a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    throw new FormatException("expected TNF_UNCHANGED in non-leading chunk");
816a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                } else if (!inChunk && tnf == NdefRecord.TNF_UNCHANGED) {
817a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    throw new FormatException("" +
8188a985d24ce9a38f40ed88fecbdcd0e75e3a68f44John Spurlock                            "unexpected TNF_UNCHANGED in first chunk or unchunked record");
819a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                }
820a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly
821a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                int typeLength = buffer.get() & 0xFF;
822a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                long payloadLength = sr ? (buffer.get() & 0xFF) : (buffer.getInt() & 0xFFFFFFFFL);
823a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                int idLength = il ? (buffer.get() & 0xFF) : 0;
824a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly
825a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                if (inChunk && typeLength != 0) {
826a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    throw new FormatException("expected zero-length type in non-leading chunk");
827a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                }
828a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly
829a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                if (!inChunk) {
830a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    type = (typeLength > 0 ? new byte[typeLength] : EMPTY_BYTE_ARRAY);
831a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    id = (idLength > 0 ? new byte[idLength] : EMPTY_BYTE_ARRAY);
832a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    buffer.get(type);
833a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    buffer.get(id);
834a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                }
835a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly
836a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                ensureSanePayloadSize(payloadLength);
837a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                payload = (payloadLength > 0 ? new byte[(int)payloadLength] : EMPTY_BYTE_ARRAY);
838a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                buffer.get(payload);
839a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly
840a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                if (cf && !inChunk) {
841a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    // first chunk
842a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    chunks.clear();
843a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    chunkTnf = tnf;
844a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                }
845a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                if (cf || inChunk) {
846a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    // any chunk
847a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    chunks.add(payload);
848a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                }
849a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                if (!cf && inChunk) {
850a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    // last chunk, flatten the payload
851a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    payloadLength = 0;
852a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    for (byte[] p : chunks) {
853a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                        payloadLength += p.length;
854a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    }
855a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    ensureSanePayloadSize(payloadLength);
856a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    payload = new byte[(int)payloadLength];
857a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    int i = 0;
858a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    for (byte[] p : chunks) {
859a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                        System.arraycopy(p, 0, payload, i, p.length);
860a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                        i += p.length;
861a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    }
862a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    tnf = chunkTnf;
863a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                }
864a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                if (cf) {
865a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    // more chunks to come
866a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    inChunk = true;
867a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    continue;
868a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                } else {
869a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    inChunk = false;
870a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                }
871a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly
872a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                String error = validateTnf(tnf, type, id, payload);
873a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                if (error != null) {
874a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    throw new FormatException(error);
875a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                }
876a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                records.add(new NdefRecord(tnf, type, id, payload));
877a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                if (ignoreMbMe) {  // for parsing a single NdefRecord
878a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    break;
879a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                }
880a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly            }
881a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        } catch (BufferUnderflowException e) {
882a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly            throw new FormatException("expected more data", e);
883a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        }
884a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        return records.toArray(new NdefRecord[records.size()]);
885a37fcbce59c8a746f641936b4de99867dbfabac9Martijn Coenen    }
886a37fcbce59c8a746f641936b4de99867dbfabac9Martijn Coenen
887a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    private static void ensureSanePayloadSize(long size) throws FormatException {
888a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        if (size > MAX_PAYLOAD_SIZE) {
889a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly            throw new FormatException(
890a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    "payload above max limit: " + size + " > " + MAX_PAYLOAD_SIZE);
891a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        }
8921584af9d4e96282f0b4f4e8e647fd6a6d18594fbJeff Hamilton    }
8931584af9d4e96282f0b4f4e8e647fd6a6d18594fbJeff Hamilton
8941584af9d4e96282f0b4f4e8e647fd6a6d18594fbJeff Hamilton    /**
895a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Perform simple validation that the tnf is valid.<p>
896a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Validates the requirements of NFCForum-TS-NDEF_1.0 section
897a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * 3.2.6 (Type Name Format). This just validates that the tnf
898a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * is valid, and that the relevant type, id and payload
899a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * fields are present (or empty) for this tnf. It does not
900a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * perform any deep inspection of the type, id and payload fields.<p>
901a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Also does not allow TNF_UNCHANGED since this class is only used
902a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * to present logical (unchunked) records.
903a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     *
904a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * @return null if valid, or a string error if invalid.
9051584af9d4e96282f0b4f4e8e647fd6a6d18594fbJeff Hamilton     */
906a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    static String validateTnf(short tnf, byte[] type, byte[] id, byte[] payload) {
907a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        switch (tnf) {
908a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly            case TNF_EMPTY:
909a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                if (type.length != 0 || id.length != 0 || payload.length != 0) {
910a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    return "unexpected data in TNF_EMPTY record";
911a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                }
912a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                return null;
913a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly            case TNF_WELL_KNOWN:
914a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly            case TNF_MIME_MEDIA:
915a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly            case TNF_ABSOLUTE_URI:
916a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly            case TNF_EXTERNAL_TYPE:
917a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                return null;
918a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly            case TNF_UNKNOWN:
919a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly            case TNF_RESERVED:
920a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                if (type.length != 0) {
921a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                    return "unexpected type field in TNF_UNKNOWN or TNF_RESERVEd record";
922a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                }
923a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                return null;
924a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly            case TNF_UNCHANGED:
925a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                return "unexpected TNF_UNCHANGED in first chunk or logical record";
926a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly            default:
927a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                return String.format("unexpected tnf value: 0x%02x", tnf);
9283da3a4582c0793f59a1fd897a992e5e4fd57b6caBen Dodson        }
9293da3a4582c0793f59a1fd897a992e5e4fd57b6caBen Dodson    }
9303da3a4582c0793f59a1fd897a992e5e4fd57b6caBen Dodson
931a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    /**
932a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Serialize record for network transmission.<p>
933a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Uses specified MB and ME flags.<p>
934a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Does not chunk records.
935a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     */
936a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    void writeToByteBuffer(ByteBuffer buffer, boolean mb, boolean me) {
937a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        boolean sr = mPayload.length < 256;
938a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        boolean il = mId.length > 0;
939a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly
940a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        byte flags = (byte)((mb ? FLAG_MB : 0) | (me ? FLAG_ME : 0) |
941a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly                (sr ? FLAG_SR : 0) | (il ? FLAG_IL : 0) | mTnf);
942a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        buffer.put(flags);
943a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly
944a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        buffer.put((byte)mType.length);
945a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        if (sr) {
946a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly            buffer.put((byte)mPayload.length);
947a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        } else {
948a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly            buffer.putInt(mPayload.length);
949e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly        }
950a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        if (il) {
951a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly            buffer.put((byte)mId.length);
952e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly        }
953a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly
954a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        buffer.put(mType);
955a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        buffer.put(mId);
956a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        buffer.put(mPayload);
957e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly    }
958e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly
959e0180d053e956ac32c2a5ec60272927755f17251Nick Pelly    /**
960a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Get byte length of serialized record.
961dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly     */
962a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    int getByteLength() {
963a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        int length = 3 + mType.length + mId.length + mPayload.length;
964a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly
965a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        boolean sr = mPayload.length < 256;
966a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        boolean il = mId.length > 0;
967a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly
968a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        if (!sr) length += 3;
969a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        if (il) length += 1;
970a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly
971a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        return length;
972dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    }
973dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly
974a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    @Override
975dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    public int describeContents() {
976dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly        return 0;
977dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    }
978dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly
979a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    @Override
980dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    public void writeToParcel(Parcel dest, int flags) {
981590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly        dest.writeInt(mTnf);
982590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly        dest.writeInt(mType.length);
983590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly        dest.writeByteArray(mType);
984590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly        dest.writeInt(mId.length);
985590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly        dest.writeByteArray(mId);
986590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly        dest.writeInt(mPayload.length);
987590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly        dest.writeByteArray(mPayload);
988dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    }
989dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly
990dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    public static final Parcelable.Creator<NdefRecord> CREATOR =
991dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly            new Parcelable.Creator<NdefRecord>() {
992a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        @Override
993dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly        public NdefRecord createFromParcel(Parcel in) {
994590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly            short tnf = (short)in.readInt();
995590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly            int typeLength = in.readInt();
996590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly            byte[] type = new byte[typeLength];
997590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly            in.readByteArray(type);
998590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly            int idLength = in.readInt();
999590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly            byte[] id = new byte[idLength];
1000590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly            in.readByteArray(id);
1001590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly            int payloadLength = in.readInt();
1002590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly            byte[] payload = new byte[payloadLength];
1003590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly            in.readByteArray(payload);
1004590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly
1005a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly            return new NdefRecord(tnf, type, id, payload);
1006dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly        }
1007a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        @Override
1008dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly        public NdefRecord[] newArray(int size) {
1009590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly            return new NdefRecord[size];
1010dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly        }
1011dc993791fc3cf7a270921f7419b0c6b875bbd92bNick Pelly    };
1012590b73bc5b8e5f7b59bff1d9264a52388a5162e6Nick Pelly
1013a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    @Override
1014a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    public int hashCode() {
1015a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        final int prime = 31;
1016a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        int result = 1;
1017a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        result = prime * result + Arrays.hashCode(mId);
1018a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        result = prime * result + Arrays.hashCode(mPayload);
1019a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        result = prime * result + mTnf;
1020a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        result = prime * result + Arrays.hashCode(mType);
1021a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        return result;
1022a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    }
1023a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly
1024a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    /**
1025a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * Returns true if the specified NDEF Record contains
1026a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     * identical tnf, type, id and payload fields.
1027a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly     */
1028a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    @Override
1029a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    public boolean equals(Object obj) {
1030a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        if (this == obj) return true;
1031a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        if (obj == null) return false;
1032a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        if (getClass() != obj.getClass()) return false;
1033a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        NdefRecord other = (NdefRecord) obj;
1034a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        if (!Arrays.equals(mId, other.mId)) return false;
1035a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        if (!Arrays.equals(mPayload, other.mPayload)) return false;
1036a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        if (mTnf != other.mTnf) return false;
1037a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        return Arrays.equals(mType, other.mType);
1038a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    }
1039a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly
1040a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    @Override
1041a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    public String toString() {
1042a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        StringBuilder b = new StringBuilder(String.format("NdefRecord tnf=%X", mTnf));
1043a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        if (mType.length > 0) b.append(" type=").append(bytesToString(mType));
1044a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        if (mId.length > 0) b.append(" id=").append(bytesToString(mId));
1045a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        if (mPayload.length > 0) b.append(" payload=").append(bytesToString(mPayload));
1046a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        return b.toString();
1047a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    }
1048a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly
1049a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    private static StringBuilder bytesToString(byte[] bs) {
1050a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        StringBuilder s = new StringBuilder();
1051a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        for (byte b : bs) {
1052a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly            s.append(String.format("%02X", b));
1053a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        }
1054a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly        return s;
1055a356bf1cd81614a94ef6c720998792480ade4c84Nick Pelly    }
10568bede1704717f594a0f924a57ff46f6300347e30Martijn Coenen}
1057