• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
21 import android.nfc.FormatException;
22 import android.util.Log;
23 
24 import java.io.ByteArrayInputStream;
25 import java.io.ByteArrayOutputStream;
26 import java.io.DataInputStream;
27 import java.io.IOException;
28 import java.util.Arrays;
29 
30 public class SnepMessenger {
31     private static final String TAG = "SnepMessager";
32     private static final boolean DBG = false;
33     private static final int HEADER_LENGTH = 6;
34     final LlcpSocket mSocket;
35     final int mFragmentLength;
36     final boolean mIsClient;
37 
SnepMessenger(boolean isClient, LlcpSocket socket, int fragmentLength)38     public SnepMessenger(boolean isClient, LlcpSocket socket, int fragmentLength) {
39         mSocket = socket;
40         mFragmentLength = fragmentLength;
41         mIsClient = isClient;
42     }
43 
sendMessage(SnepMessage msg)44     public void sendMessage(SnepMessage msg) throws IOException {
45         byte[] buffer = msg.toByteArray();
46         byte remoteContinue;
47         if (mIsClient) {
48             remoteContinue = SnepMessage.RESPONSE_CONTINUE;
49         } else {
50             remoteContinue = SnepMessage.REQUEST_CONTINUE;
51         }
52         if (DBG) Log.d(TAG, "about to send a " + buffer.length + " byte message");
53 
54         // Send first fragment
55         int length = Math.min(buffer.length, mFragmentLength);
56         byte[] tmpBuffer = Arrays.copyOfRange(buffer, 0, length);
57         if (DBG) Log.d(TAG, "about to send a " + length + " byte fragment");
58         mSocket.send(tmpBuffer);
59 
60         if (length == buffer.length) {
61             return;
62         }
63 
64         // Look for Continue or Reject from peer.
65         int offset = length;
66         byte[] responseBytes = new byte[HEADER_LENGTH];
67         mSocket.receive(responseBytes);
68         SnepMessage snepResponse;
69         try {
70             snepResponse = SnepMessage.fromByteArray(responseBytes);
71         } catch (FormatException e) {
72             throw new IOException("Invalid SNEP message", e);
73         }
74 
75         if (DBG) Log.d(TAG, "Got response from first fragment: " + snepResponse.getField());
76         if (snepResponse.getField() != remoteContinue) {
77             throw new IOException("Invalid response from server (" +
78                     snepResponse.getField() + ")");
79         }
80 
81         // Send remaining fragments.
82         while (offset < buffer.length) {
83             length = Math.min(buffer.length - offset, mFragmentLength);
84             tmpBuffer = Arrays.copyOfRange(buffer, offset, offset + length);
85             if (DBG) Log.d(TAG, "about to send a " + length + " byte fragment");
86             mSocket.send(tmpBuffer);
87             offset += length;
88         }
89     }
90 
getMessage()91     public SnepMessage getMessage() throws IOException, SnepException {
92         ByteArrayOutputStream buffer = new ByteArrayOutputStream(mFragmentLength);
93         byte[] partial = new byte[mFragmentLength];
94         int size;
95         int requestSize = 0;
96         int readSize = 0;
97         byte requestVersion = 0;
98         boolean doneReading = false;
99         byte fieldContinue;
100         byte fieldReject;
101         if (mIsClient) {
102             fieldContinue = SnepMessage.REQUEST_CONTINUE;
103             fieldReject = SnepMessage.REQUEST_REJECT;
104         } else {
105             fieldContinue = SnepMessage.RESPONSE_CONTINUE;
106             fieldReject = SnepMessage.RESPONSE_REJECT;
107         }
108 
109         size = mSocket.receive(partial);
110         if (DBG) Log.d(TAG, "read " + size + " bytes");
111         if (size < 0) {
112             try {
113                 mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
114             } catch (IOException e) {
115                 // Ignore
116             }
117             throw new IOException("Error reading SNEP message.");
118         } else if (size < HEADER_LENGTH) {
119             try {
120                 mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
121             } catch (IOException e) {
122                 // Ignore
123             }
124             throw new IOException("Invalid fragment from sender.");
125         } else {
126             readSize = size - HEADER_LENGTH;
127             buffer.write(partial, 0, size);
128         }
129 
130         DataInputStream dataIn = new DataInputStream(new ByteArrayInputStream(partial));
131         requestVersion = dataIn.readByte();
132         byte requestField = dataIn.readByte();
133         requestSize = dataIn.readInt();
134 
135         if (DBG) Log.d(TAG, "read " + readSize + " of " + requestSize);
136 
137         if (((requestVersion & 0xF0) >> 4) != SnepMessage.VERSION_MAJOR) {
138             // Invalid protocol version; treat message as complete.
139             return new SnepMessage(requestVersion, requestField, 0, 0, null);
140         }
141 
142         if (requestSize > readSize) {
143             if (DBG) Log.d(TAG, "requesting continuation");
144             mSocket.send(SnepMessage.getMessage(fieldContinue).toByteArray());
145         } else {
146             doneReading = true;
147         }
148 
149         // Remaining fragments
150         while (!doneReading) {
151             try {
152                 size = mSocket.receive(partial);
153                 if (DBG) Log.d(TAG, "read " + size + " bytes");
154                 if (size < 0) {
155                     try {
156                         mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
157                     } catch (IOException e) {
158                         // Ignore
159                     }
160                     throw new IOException();
161                 } else {
162                     readSize += size;
163                     buffer.write(partial, 0, size);
164                     if (readSize == requestSize) {
165                         doneReading = true;
166                     }
167                 }
168             } catch (IOException e) {
169                 try {
170                     mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
171                 } catch (IOException e2) {
172                     // Ignore
173                 }
174                 throw e;
175             }
176         }
177 
178         // Build NDEF message set from the stream
179         try {
180             return SnepMessage.fromByteArray(buffer.toByteArray());
181         } catch (FormatException e) {
182             Log.e(TAG, "Badly formatted NDEF message, ignoring", e);
183             throw new SnepException(e);
184         }
185     }
186 
close()187     public void close() throws IOException {
188         mSocket.close();
189     }
190 }
191