1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.nfc.snep; 18 19 import android.nfc.FormatException; 20 import android.nfc.NdefMessage; 21 import android.nfc.NdefRecord; 22 23 import com.android.nfc.NfcService; 24 import com.android.nfc.sneptest.DtaSnepClient; 25 26 import java.io.ByteArrayOutputStream; 27 import java.io.DataOutputStream; 28 import java.io.IOException; 29 import java.io.UnsupportedEncodingException; 30 import java.nio.ByteBuffer; 31 32 public final class SnepMessage { 33 public static final byte VERSION_MAJOR = (byte) 0x1; 34 public static final byte VERSION_MINOR = (byte) 0x0; 35 public static final byte VERSION = (0xF0 & (VERSION_MAJOR << 4)) | (0x0F & VERSION_MINOR); 36 37 public static final byte REQUEST_CONTINUE = (byte) 0x00; 38 public static final byte REQUEST_GET = (byte) 0x01; 39 public static final byte REQUEST_PUT = (byte) 0x02; 40 public static final byte REQUEST_RFU = (byte) 0x03; 41 public static final byte REQUEST_REJECT = (byte) 0x7F; 42 43 public static final byte RESPONSE_CONTINUE = (byte) 0x80; 44 public static final byte RESPONSE_SUCCESS = (byte) 0x81; 45 public static final byte RESPONSE_NOT_FOUND = (byte) 0xC0; 46 public static final byte RESPONSE_EXCESS_DATA = (byte) 0xC1; 47 public static final byte RESPONSE_BAD_REQUEST = (byte) 0xC2; 48 public static final byte RESPONSE_NOT_IMPLEMENTED = (byte) 0xE0; 49 public static final byte RESPONSE_UNSUPPORTED_VERSION = (byte) 0xE1; 50 public static final byte RESPONSE_REJECT = (byte) 0xFF; 51 52 private static final byte[] NDEF_SHORT_TEST_RECORD = new byte[]{(byte)0xD1,(byte)0x01,(byte)0x1E,(byte)0x54,(byte)0x02,(byte)0x6C,(byte)0x61, // NDEF Header 53 (byte)0x4C,(byte)0x6F,(byte)0x72,(byte)0x65,(byte)0x6D,(byte)0x20,(byte)0x69,(byte)0x70,(byte)0x73,(byte)0x75, // Payload 54 (byte)0x6D,(byte)0x20,(byte)0x64,(byte)0x6F,(byte)0x6C,(byte)0x6F,(byte)0x72,(byte)0x20,(byte)0x73,(byte)0x69, 55 (byte)0x74,(byte)0x20,(byte)0x61,(byte)0x6D,(byte)0x65,(byte)0x74,(byte)0x2E}; 56 57 private static final byte[] NDEF_TEST_RECORD = new byte[]{(byte)0xC1,(byte)0x01,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x1E,(byte)0x54,(byte)0x02,(byte)0x6C,(byte)0x61, // NDEF Header 58 (byte)0x4C,(byte)0x6F,(byte)0x72,(byte)0x65,(byte)0x6D,(byte)0x20,(byte)0x69,(byte)0x70,(byte)0x73,(byte)0x75, // Payload 59 (byte)0x6D,(byte)0x20,(byte)0x64,(byte)0x6F,(byte)0x6C,(byte)0x6F,(byte)0x72,(byte)0x20,(byte)0x73,(byte)0x69, 60 (byte)0x74,(byte)0x20,(byte)0x61,(byte)0x6D,(byte)0x65,(byte)0x74,(byte)0x2E}; 61 62 private static final int HEADER_LENGTH = 6; 63 public static final int MAL_IUT = 0x0400; 64 public static final int MAL = 0xFFFFFFFF; 65 private final byte mVersion; 66 private final byte mField; 67 private final int mLength; 68 private final int mAcceptableLength; 69 private final NdefMessage mNdefMessage; 70 getGetRequest(int acceptableLength, NdefMessage ndef)71 public static SnepMessage getGetRequest(int acceptableLength, NdefMessage ndef) { 72 return new SnepMessage(VERSION, REQUEST_GET, 4 + ndef.toByteArray().length, 73 acceptableLength, ndef); 74 } 75 getPutRequest(NdefMessage ndef)76 public static SnepMessage getPutRequest(NdefMessage ndef) { 77 return new SnepMessage(VERSION, REQUEST_PUT, ndef.toByteArray().length, 0, ndef); 78 } 79 getMessage(byte field)80 public static SnepMessage getMessage(byte field) { 81 return new SnepMessage(VERSION, field, 0, 0, null); 82 } 83 getSuccessResponse(NdefMessage ndef)84 public static SnepMessage getSuccessResponse(NdefMessage ndef) { 85 if (ndef == null) { 86 return new SnepMessage(VERSION, RESPONSE_SUCCESS, 0, 0, null); 87 } else { 88 return new SnepMessage(VERSION, RESPONSE_SUCCESS, ndef.toByteArray().length, 0, ndef); 89 } 90 } 91 fromByteArray(byte[] data)92 public static SnepMessage fromByteArray(byte[] data) throws FormatException { 93 return new SnepMessage(data); 94 } 95 getLargeNdef()96 public static NdefMessage getLargeNdef() throws UnsupportedEncodingException { 97 String snepTestData2 = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus at" 98 +" lorem nunc, ut venenatis quam. Etiam id dolor quam, at viverra dolor." 99 +" Phasellus eu lacus ligula, quis euismod erat. Sed feugiat, ligula at" 100 +" mollis aliquet, justo lacus condimentum eros, non tincidunt neque" 101 +" ipsum eu risus. Sed adipiscing dui euismod tellus ullamcorper ornare." 102 +" Phasellus mattis risus et lectus euismod eu fermentum sem cursus." 103 +" Phasellus tristique consectetur mauris eu porttitor. Sed lobortis" 104 +" porttitor orci."; 105 String lang = "la"; 106 byte[] textBytes = snepTestData2.getBytes(); 107 byte[] langBytes = lang.getBytes("US-ASCII"); 108 int langLength = langBytes.length; 109 int textLength = textBytes.length; 110 111 byte[] payload = new byte[1 + langLength + textLength]; 112 payload[0] = (byte) langLength; 113 114 System.arraycopy(langBytes, 0, payload, 1, langLength); 115 System.arraycopy(textBytes, 0, payload, 1 + langLength, textLength); 116 117 NdefRecord data2 = new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], payload); 118 return new NdefMessage(new NdefRecord[]{data2}); 119 } 120 getSmallNdef()121 public static NdefMessage getSmallNdef() throws UnsupportedEncodingException { 122 String snepTestData1 = "Lorem ipsum dolor sit amet."; 123 String lang = "la"; 124 byte[] textBytes = snepTestData1.getBytes(); 125 byte[] langBytes = lang.getBytes("US-ASCII"); 126 int langLength = langBytes.length; 127 int textLength = textBytes.length; 128 129 byte[] payload = new byte[1 + langLength + textLength]; 130 payload[0] = (byte) langLength; 131 132 System.arraycopy(langBytes, 0, payload, 1, langLength); 133 System.arraycopy(textBytes, 0, payload, 1 + langLength, textLength); 134 135 NdefRecord data1 = new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], payload); 136 return new NdefMessage(new NdefRecord[]{data1}); 137 } 138 SnepMessage(byte[] data)139 private SnepMessage(byte[] data) throws FormatException { 140 ByteBuffer input = ByteBuffer.wrap(data); 141 int ndefOffset; 142 int ndefLength; 143 144 mVersion = input.get(); 145 mField = input.get(); 146 mLength = input.getInt(); 147 if (mField == REQUEST_GET) { 148 mAcceptableLength = input.getInt(); 149 ndefOffset = HEADER_LENGTH + 4; 150 ndefLength = mLength - 4; 151 } else { 152 mAcceptableLength = -1; 153 ndefOffset = HEADER_LENGTH; 154 ndefLength = mLength; 155 } 156 157 if (ndefLength > 0) { 158 byte[] bytes = new byte[ndefLength]; 159 System.arraycopy(data, ndefOffset, bytes, 0, ndefLength); 160 mNdefMessage = new NdefMessage(bytes); 161 } else { 162 mNdefMessage = null; 163 } 164 } 165 SnepMessage(byte version, byte field, int length, int acceptableLength, NdefMessage ndefMessage)166 SnepMessage(byte version, byte field, int length, int acceptableLength, 167 NdefMessage ndefMessage) { 168 mVersion = version; 169 mField = field; 170 mLength = length; 171 mAcceptableLength = acceptableLength; 172 mNdefMessage = ndefMessage; 173 } 174 toByteArray()175 public byte[] toByteArray() { 176 byte[] bytes; 177 if (mNdefMessage != null) { 178 if (NfcService.sIsDtaMode && DtaSnepClient.mTestCaseId != 0) { 179 if (DtaSnepClient.mTestCaseId == 5 || DtaSnepClient.mTestCaseId == 6) { 180 bytes = mNdefMessage.toByteArray(); 181 } else { 182 if (NfcService.sIsShortRecordLayout) { 183 bytes = NDEF_SHORT_TEST_RECORD; 184 } else { 185 bytes = NDEF_TEST_RECORD; 186 } 187 } 188 } else { 189 bytes = mNdefMessage.toByteArray(); 190 } 191 } else { 192 bytes = new byte[0]; 193 } 194 195 ByteArrayOutputStream buffer; 196 try { 197 if (mField == REQUEST_GET) { 198 buffer = new ByteArrayOutputStream(bytes.length + HEADER_LENGTH + 4); 199 } else { 200 buffer = new ByteArrayOutputStream(bytes.length + HEADER_LENGTH); 201 } 202 203 DataOutputStream output = new DataOutputStream(buffer); 204 output.writeByte(mVersion); 205 output.writeByte(mField); 206 if (mField == REQUEST_GET) { 207 output.writeInt(bytes.length + 4); 208 output.writeInt(mAcceptableLength); 209 } else { 210 output.writeInt(bytes.length); 211 } 212 output.write(bytes); 213 } catch(IOException e) { 214 return null; 215 } 216 217 return buffer.toByteArray(); 218 } 219 getNdefMessage()220 public NdefMessage getNdefMessage() { 221 return mNdefMessage; 222 } 223 getField()224 public byte getField() { 225 return mField; 226 } 227 getVersion()228 public byte getVersion() { 229 return mVersion; 230 } 231 getLength()232 public int getLength() { 233 return mLength; 234 } 235 getAcceptableLength()236 public int getAcceptableLength() { 237 if (mField != REQUEST_GET) { 238 throw new UnsupportedOperationException( 239 "Acceptable Length only available on get request messages."); 240 } 241 return mAcceptableLength; 242 } 243 } 244