blob: 5df9272c9feade53f347f541e60deea58a155ce2 [file] [log] [blame]
Nick Pellydc993792010-10-04 11:17:25 -07001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.nfc;
18
Nick Pellya356bf12011-12-13 15:36:31 -080019import java.nio.ByteBuffer;
20import java.util.Arrays;
21
Nick Pellydc993792010-10-04 11:17:25 -070022import android.os.Parcel;
23import android.os.Parcelable;
24
Nick Pellya356bf12011-12-13 15:36:31 -080025
Nick Pellydc993792010-10-04 11:17:25 -070026/**
Nick Pellya356bf12011-12-13 15:36:31 -080027 * Represents an immutable NDEF Message.
28 * <p>
29 * NDEF (NFC Data Exchange Format) is a light-weight binary format,
30 * used to encapsulate typed data. It is specified by the NFC Forum,
31 * for transmission and storage with NFC, however it is transport agnostic.
32 * <p>
33 * NDEF defines messages and records. An NDEF Record contains
34 * typed data, such as MIME-type media, a URI, or a custom
35 * application payload. An NDEF Message is a container for
36 * one or more NDEF Records.
37 * <p>
38 * When an Android device receives an NDEF Message
39 * (for example by reading an NFC tag) it processes it through
40 * a dispatch mechanism to determine an activity to launch.
41 * The type of the <em>first</em> record in the message has
42 * special importance for message dispatch, so design this record
43 * carefully.
44 * <p>
45 * Use {@link #NdefMessage(byte[])} to construct an NDEF Message from
46 * binary data, or {@link #NdefMessage(NdefRecord[])} to
47 * construct from one or more {@link NdefRecord}s.
48 * <p class="note">
49 * {@link NdefMessage} and {@link NdefRecord} implementations are
50 * always available, even on Android devices that do not have NFC hardware.
51 * <p class="note">
52 * {@link NdefRecord}s are intended to be immutable (and thread-safe),
53 * however they may contain mutable fields. So take care not to modify
54 * mutable fields passed into constructors, or modify mutable fields
55 * obtained by getter methods, unless such modification is explicitly
56 * marked as safe.
57 *
58 * @see NfcAdapter#ACTION_NDEF_DISCOVERED
59 * @see NdefRecord
Nick Pellydc993792010-10-04 11:17:25 -070060 */
Nick Pelly11b075e2010-10-28 13:39:37 -070061public final class NdefMessage implements Parcelable {
Nick Pelly590b73b2010-10-12 13:00:50 -070062 private final NdefRecord[] mRecords;
63
Nick Pellydc993792010-10-04 11:17:25 -070064 /**
Nick Pellya356bf12011-12-13 15:36:31 -080065 * Construct an NDEF Message by parsing raw bytes.<p>
66 * Strict validation of the NDEF binary structure is performed:
67 * there must be at least one record, every record flag must
68 * be correct, and the total length of the message must match
69 * the length of the input data.<p>
70 * This parser can handle chunked records, and converts them
71 * into logical {@link NdefRecord}s within the message.<p>
72 * Once the input data has been parsed to one or more logical
73 * records, basic validation of the tnf, type, id, and payload fields
74 * of each record is performed, as per the documentation on
75 * on {@link NdefRecord#NdefRecord(short, byte[], byte[], byte[])}<p>
76 * If either strict validation of the binary format fails, or
77 * basic validation during record construction fails, a
78 * {@link FormatException} is thrown<p>
79 * Deep inspection of the type, id and payload fields of
80 * each record is not performed, so it is possible to parse input
81 * that has a valid binary format and confirms to the basic
82 * validation requirements of
83 * {@link NdefRecord#NdefRecord(short, byte[], byte[], byte[])},
84 * but fails more strict requirements as specified by the
85 * NFC Forum.
86 *
87 * <p class="note">
88 * It is safe to re-use the data byte array after construction:
89 * this constructor will make an internal copy of all necessary fields.
90 *
91 * @param data raw bytes to parse
92 * @throws FormatException if the data cannot be parsed
Nick Pellydc993792010-10-04 11:17:25 -070093 */
Nick Pelly590b73b2010-10-12 13:00:50 -070094 public NdefMessage(byte[] data) throws FormatException {
Nick Pellyc97a5522012-01-05 15:13:01 +110095 if (data == null) throw new NullPointerException("data is null");
Nick Pellya356bf12011-12-13 15:36:31 -080096 ByteBuffer buffer = ByteBuffer.wrap(data);
97
98 mRecords = NdefRecord.parse(buffer, false);
99
100 if (buffer.remaining() > 0) {
101 throw new FormatException("trailing data");
Nick Pelly590b73b2010-10-12 13:00:50 -0700102 }
Nick Pellydc993792010-10-04 11:17:25 -0700103 }
104
105 /**
Nick Pellya356bf12011-12-13 15:36:31 -0800106 * Construct an NDEF Message from one or more NDEF Records.
107 *
108 * @param record first record (mandatory)
109 * @param records additional records (optional)
110 */
111 public NdefMessage(NdefRecord record, NdefRecord ... records) {
112 // validate
Nick Pellyc97a5522012-01-05 15:13:01 +1100113 if (record == null) throw new NullPointerException("record cannot be null");
114
Nick Pellya356bf12011-12-13 15:36:31 -0800115 for (NdefRecord r : records) {
116 if (r == null) {
117 throw new NullPointerException("record cannot be null");
118 }
119 }
120
121 mRecords = new NdefRecord[1 + records.length];
122 mRecords[0] = record;
123 System.arraycopy(records, 0, mRecords, 1, records.length);
124 }
125
126 /**
127 * Construct an NDEF Message from one or more NDEF Records.
128 *
129 * @param records one or more records
Nick Pellydc993792010-10-04 11:17:25 -0700130 */
131 public NdefMessage(NdefRecord[] records) {
Nick Pellya356bf12011-12-13 15:36:31 -0800132 // validate
133 if (records.length < 1) {
134 throw new IllegalArgumentException("must have at least one record");
135 }
136 for (NdefRecord r : records) {
137 if (r == null) {
138 throw new NullPointerException("records cannot contain null");
Nick Pelly590b73b2010-10-12 13:00:50 -0700139 }
Nick Pelly590b73b2010-10-12 13:00:50 -0700140 }
141
Nick Pellya356bf12011-12-13 15:36:31 -0800142 mRecords = records;
143 }
144
145 /**
146 * Get the NDEF Records inside this NDEF Message.<p>
Nick Pellyc97a5522012-01-05 15:13:01 +1100147 * An {@link NdefMessage} always has one or more NDEF Records: so the
148 * following code to retrieve the first record is always safe
149 * (no need to check for null or array length >= 1):
150 * <pre>
151 * NdefRecord firstRecord = ndefMessage.getRecords()[0];
152 * </pre>
Nick Pellya356bf12011-12-13 15:36:31 -0800153 *
154 * @return array of one or more NDEF records.
155 */
156 public NdefRecord[] getRecords() {
157 return mRecords;
158 }
159
160 /**
Nick Pelly1f5badc2012-01-24 13:22:58 -0800161 * Return the length of this NDEF Message if it is written to a byte array
162 * with {@link #toByteArray}.<p>
163 * An NDEF Message can be formatted to bytes in different ways
164 * depending on chunking, SR, and ID flags, so the length returned
165 * by this method may not be equal to the length of the original
166 * byte array used to construct this NDEF Message. However it will
167 * always be equal to the length of the byte array produced by
168 * {@link #toByteArray}.
169 *
Nick Pellyd3cb80d2012-01-27 11:03:05 -0800170 * @return length of this NDEF Message when written to bytes with {@link #toByteArray}
Nick Pelly1f5badc2012-01-24 13:22:58 -0800171 * @see #toByteArray
172 */
173 public int getByteArrayLength() {
174 int length = 0;
175 for (NdefRecord r : mRecords) {
176 length += r.getByteLength();
177 }
178 return length;
179 }
180
181 /**
182 * Return this NDEF Message as raw bytes.<p>
Nick Pellya356bf12011-12-13 15:36:31 -0800183 * The NDEF Message is formatted as per the NDEF 1.0 specification,
184 * and the byte array is suitable for network transmission or storage
185 * in an NFC Forum NDEF compatible tag.<p>
186 * This method will not chunk any records, and will always use the
187 * short record (SR) format and omit the identifier field when possible.
188 *
189 * @return NDEF Message in binary format
Nick Pelly1f5badc2012-01-24 13:22:58 -0800190 * @see getByteArrayLength
Nick Pellya356bf12011-12-13 15:36:31 -0800191 */
192 public byte[] toByteArray() {
Nick Pelly1f5badc2012-01-24 13:22:58 -0800193 int length = getByteArrayLength();
Nick Pellya356bf12011-12-13 15:36:31 -0800194 ByteBuffer buffer = ByteBuffer.allocate(length);
195
196 for (int i=0; i<mRecords.length; i++) {
197 boolean mb = (i == 0); // first record
198 boolean me = (i == mRecords.length - 1); // last record
199 mRecords[i].writeToByteBuffer(buffer, mb, me);
200 }
201
202 return buffer.array();
Nick Pellydc993792010-10-04 11:17:25 -0700203 }
204
Jason parks3ebd59b2010-11-02 20:03:52 -0500205 @Override
Nick Pellydc993792010-10-04 11:17:25 -0700206 public int describeContents() {
207 return 0;
208 }
209
Jason parks3ebd59b2010-11-02 20:03:52 -0500210 @Override
Nick Pellydc993792010-10-04 11:17:25 -0700211 public void writeToParcel(Parcel dest, int flags) {
Nick Pelly590b73b2010-10-12 13:00:50 -0700212 dest.writeInt(mRecords.length);
213 dest.writeTypedArray(mRecords, flags);
Nick Pellydc993792010-10-04 11:17:25 -0700214 }
215
216 public static final Parcelable.Creator<NdefMessage> CREATOR =
217 new Parcelable.Creator<NdefMessage>() {
Jason parks3ebd59b2010-11-02 20:03:52 -0500218 @Override
Nick Pellydc993792010-10-04 11:17:25 -0700219 public NdefMessage createFromParcel(Parcel in) {
Nick Pelly590b73b2010-10-12 13:00:50 -0700220 int recordsLength = in.readInt();
221 NdefRecord[] records = new NdefRecord[recordsLength];
222 in.readTypedArray(records, NdefRecord.CREATOR);
223 return new NdefMessage(records);
Nick Pellydc993792010-10-04 11:17:25 -0700224 }
Jason parks3ebd59b2010-11-02 20:03:52 -0500225 @Override
Nick Pellydc993792010-10-04 11:17:25 -0700226 public NdefMessage[] newArray(int size) {
Nick Pelly590b73b2010-10-12 13:00:50 -0700227 return new NdefMessage[size];
Nick Pellydc993792010-10-04 11:17:25 -0700228 }
229 };
Nick Pelly590b73b2010-10-12 13:00:50 -0700230
Nick Pellya356bf12011-12-13 15:36:31 -0800231 @Override
232 public int hashCode() {
233 return Arrays.hashCode(mRecords);
234 }
235
236 /**
237 * Returns true if the specified NDEF Message contains
238 * identical NDEF Records.
239 */
240 @Override
241 public boolean equals(Object obj) {
242 if (this == obj) return true;
243 if (obj == null) return false;
244 if (getClass() != obj.getClass()) return false;
245 NdefMessage other = (NdefMessage) obj;
246 return Arrays.equals(mRecords, other.mRecords);
247 }
248
249 @Override
250 public String toString() {
251 return "NdefMessage " + Arrays.toString(mRecords);
252 }
Nick Pellydc993792010-10-04 11:17:25 -0700253}