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