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 17package com.android.nfc.snep; 18 19import com.android.nfc.DeviceHost.LlcpSocket; 20 21import android.nfc.FormatException; 22import android.util.Log; 23 24import java.io.ByteArrayInputStream; 25import java.io.ByteArrayOutputStream; 26import java.io.DataInputStream; 27import java.io.IOException; 28import java.util.Arrays; 29 30public 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 38 public SnepMessenger(boolean isClient, LlcpSocket socket, int fragmentLength) { 39 mSocket = socket; 40 mFragmentLength = fragmentLength; 41 mIsClient = isClient; 42 } 43 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 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 187 public void close() throws IOException { 188 mSocket.close(); 189 } 190} 191