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