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 com.android.nfc.DeviceHost.LlcpSocket;
20 import com.android.nfc.NfcService;
21 import com.android.nfc.sneptest.DtaSnepClient;
22 import com.android.nfc.sneptest.ExtDtaSnepServer;
23 
24 import android.nfc.FormatException;
25 import android.util.Log;
26 
27 import java.io.ByteArrayInputStream;
28 import java.io.ByteArrayOutputStream;
29 import java.io.DataInputStream;
30 import java.io.IOException;
31 import java.util.Arrays;
32 
33 public class SnepMessenger {
34     private static final String TAG = "SnepMessager";
35     private static final boolean DBG = false;
36     private static final int HEADER_LENGTH = 6;
37     final LlcpSocket mSocket;
38     final int mFragmentLength;
39     final boolean mIsClient;
40 
SnepMessenger(boolean isClient, LlcpSocket socket, int fragmentLength)41     public SnepMessenger(boolean isClient, LlcpSocket socket, int fragmentLength) {
42         mSocket = socket;
43         mFragmentLength = fragmentLength;
44         mIsClient = isClient;
45     }
46 
sendMessage(SnepMessage msg)47     public void sendMessage(SnepMessage msg) throws IOException {
48         byte[] buffer = msg.toByteArray();
49         byte remoteContinue;
50         if (mIsClient) {
51             remoteContinue = SnepMessage.RESPONSE_CONTINUE;
52         } else {
53             remoteContinue = SnepMessage.REQUEST_CONTINUE;
54         }
55         if (DBG) Log.d(TAG, "about to send a " + buffer.length + " byte message");
56 
57         // Send first fragment
58         int length = Math.min(buffer.length, mFragmentLength);
59         byte[] tmpBuffer = Arrays.copyOfRange(buffer, 0, length);
60         if (DBG) Log.d(TAG, "about to send a " + length + " byte fragment");
61         mSocket.send(tmpBuffer);
62 
63         if (length == buffer.length) {
64             return;
65         }
66 
67         // Look for Continue or Reject from peer.
68         int offset = length;
69         byte[] responseBytes = new byte[HEADER_LENGTH];
70         mSocket.receive(responseBytes);
71         SnepMessage snepResponse;
72         try {
73             snepResponse = SnepMessage.fromByteArray(responseBytes);
74         } catch (FormatException e) {
75             throw new IOException("Invalid SNEP message", e);
76         }
77 
78         if (DBG) Log.d(TAG, "Got response from first fragment: " + snepResponse.getField());
79         if (snepResponse.getField() != remoteContinue) {
80             throw new IOException("Invalid response from server (" +
81                     snepResponse.getField() + ")");
82         }
83         // Look for wrong/invalid request or response from peer
84        if (NfcService.sIsDtaMode) {
85             if (mIsClient && (DtaSnepClient.mTestCaseId == 6)) {
86                 length = Math.min(buffer.length - offset, mFragmentLength);
87                 tmpBuffer = Arrays.copyOfRange(buffer, offset, offset + length);
88                 if (DBG) Log.d(TAG, "about to send a " + length + " byte fragment");
89                 mSocket.send(tmpBuffer);
90                 offset += length;
91 
92                 mSocket.receive(responseBytes);
93 
94                 try {
95                     snepResponse = SnepMessage.fromByteArray(responseBytes);
96                 } catch (FormatException e) {
97                     throw new IOException("Invalid SNEP message", e);
98                 }
99                 if (DBG) Log.d(TAG, "Got response from second fragment: " + snepResponse.getField());
100                 if (snepResponse.getField() == remoteContinue) {
101                     close();
102                     return;
103                 }
104             }
105         }
106 
107         // Send remaining fragments.
108         while (offset < buffer.length) {
109             length = Math.min(buffer.length - offset, mFragmentLength);
110             tmpBuffer = Arrays.copyOfRange(buffer, offset, offset + length);
111             if (DBG) Log.d(TAG, "about to send a " + length + " byte fragment");
112             mSocket.send(tmpBuffer);
113 
114             if (NfcService.sIsDtaMode) {
115                 if (!mIsClient && ExtDtaSnepServer.mTestCaseId == 0x01) {
116                     mSocket.receive(responseBytes);
117                     try {
118                         snepResponse = SnepMessage.fromByteArray(responseBytes);
119                     } catch (FormatException e) {
120                         throw new IOException("Invalid SNEP message", e);
121                     }
122                     if (DBG) Log.d(TAG, "Got continue response after second fragment: and now disconnecting..." + snepResponse.getField());
123                     if (snepResponse.getField() == remoteContinue) {
124                         close();
125                         return;
126                     }
127                 }
128             }
129 
130             offset += length;
131         }
132     }
133 
getMessage()134     public SnepMessage getMessage() throws IOException, SnepException {
135         ByteArrayOutputStream buffer = new ByteArrayOutputStream(mFragmentLength);
136         byte[] partial = new byte[mFragmentLength];
137         int size;
138         int requestSize = 0;
139         int readSize = 0;
140         byte requestVersion = 0;
141         byte requestField = 0; // for DTA Mode
142         boolean doneReading = false;
143         byte fieldContinue;
144         byte fieldReject;
145         if (mIsClient) {
146             fieldContinue = SnepMessage.REQUEST_CONTINUE;
147             fieldReject = SnepMessage.REQUEST_REJECT;
148         } else {
149             fieldContinue = SnepMessage.RESPONSE_CONTINUE;
150             fieldReject = SnepMessage.RESPONSE_REJECT;
151         }
152 
153         size = mSocket.receive(partial);
154         if (DBG) Log.d(TAG, "read " + size + " bytes");
155         if (size < 0) {
156             try {
157                 mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
158             } catch (IOException e) {
159                 // Ignore
160             }
161             throw new IOException("Error reading SNEP message.");
162         } else if (size < HEADER_LENGTH) {
163             try {
164                 if (NfcService.sIsDtaMode && mIsClient) {
165                     if (DBG) Log.d(TAG, "Invalid header length");
166                     close();
167                 } else {
168                     mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
169 
170                 }
171                 mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
172             } catch (IOException e) {
173                 // Ignore
174             }
175             throw new IOException("Invalid fragment from sender.");
176         } else {
177             readSize = size - HEADER_LENGTH;
178             buffer.write(partial, 0, size);
179         }
180 
181         DataInputStream dataIn = new DataInputStream(new ByteArrayInputStream(partial));
182         requestVersion = dataIn.readByte();
183         requestField = dataIn.readByte();
184         requestSize = dataIn.readInt();
185 
186         if (DBG) Log.d(TAG, "read " + readSize + " of " + requestSize);
187 
188         if (((requestVersion & 0xF0) >> 4) != SnepMessage.VERSION_MAJOR) {
189             if (NfcService.sIsDtaMode) {
190                 sendMessage(SnepMessage.getMessage(SnepMessage.RESPONSE_UNSUPPORTED_VERSION));
191                 close();
192             } else {
193             if (NfcService.sIsDtaMode) {
194                 sendMessage(SnepMessage.getMessage(SnepMessage.RESPONSE_UNSUPPORTED_VERSION));
195                 close();
196             } else {
197                 // Invalid protocol version; treat message as complete.
198                 return new SnepMessage(requestVersion, requestField, 0, 0, null);
199             }
200             }
201 
202         }
203 
204         if (NfcService.sIsDtaMode) {
205             // added for TC_S_BIT_B1_01_X
206             if (!mIsClient && (requestField == SnepMessage.RESPONSE_CONTINUE ||
207                               requestField == SnepMessage.RESPONSE_SUCCESS ||
208                               requestField == SnepMessage.RESPONSE_NOT_FOUND)) {
209                 if (DBG) Log.d(TAG, "errorneous response received, disconnecting client");
210                 close();
211             }
212             if (!mIsClient && requestField == SnepMessage.REQUEST_RFU) {
213                 if (DBG) Log.d(TAG, "unknown request received, disconnecting client");
214                 sendMessage(SnepMessage.getMessage(SnepMessage.RESPONSE_BAD_REQUEST));
215                 close();
216             }
217             // added for TC_C_BIT_BI_01_0
218             if (mIsClient && requestField == SnepMessage.REQUEST_PUT) {
219                 if (DBG) Log.d(TAG, "errorneous PUT request received, disconnecting from server");
220                     close();
221             }
222             // added for TC_C_GET_BV_03
223             if (mIsClient && (requestSize > SnepMessage.MAL_IUT)) {
224                 if (DBG) Log.d(TAG, "responding reject");
225                     return new SnepMessage(requestVersion, requestField, requestSize, 0, null);
226             }
227             //added for TC_S_ACC_BV_05_0&1 and TC_S_ACC_BV_06_0&1
228             if (!mIsClient && ((requestSize > SnepMessage.MAL_IUT) ||
229                                 requestSize == SnepMessage.MAL)) {
230                 if (DBG) Log.d(TAG, "responding reject");
231                     return new SnepMessage(requestVersion, requestField, requestSize, 0, null);
232             }
233         }
234 
235         if (requestSize > readSize) {
236             if (DBG) Log.d(TAG, "requesting continuation");
237             mSocket.send(SnepMessage.getMessage(fieldContinue).toByteArray());
238         } else {
239             doneReading = true;
240         }
241 
242         // Remaining fragments
243         while (!doneReading) {
244             try {
245                 size = mSocket.receive(partial);
246                 if (DBG) Log.d(TAG, "read " + size + " bytes");
247                 if (size < 0) {
248                     try {
249                         mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
250                     } catch (IOException e) {
251                         // Ignore
252                     }
253                     throw new IOException();
254                 } else {
255                     readSize += size;
256                     buffer.write(partial, 0, size);
257                     if (readSize == requestSize) {
258                         doneReading = true;
259                     }
260                 }
261             } catch (IOException e) {
262                 try {
263                     mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
264                 } catch (IOException e2) {
265                     // Ignore
266                 }
267                 throw e;
268             }
269         }
270 
271         // Build NDEF message set from the stream
272         try {
273             return SnepMessage.fromByteArray(buffer.toByteArray());
274         } catch (FormatException e) {
275             Log.e(TAG, "Badly formatted NDEF message, ignoring", e);
276             throw new SnepException(e);
277         }
278     }
279 
close()280     public void close() throws IOException {
281         mSocket.close();
282     }
283 }
284