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