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