/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.nfc.snep; import com.android.nfc.DeviceHost.LlcpSocket; import android.nfc.FormatException; import android.util.Log; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.IOException; import java.util.Arrays; public class SnepMessenger { private static final String TAG = "SnepMessager"; private static final boolean DBG = false; private static final int HEADER_LENGTH = 6; final LlcpSocket mSocket; final int mFragmentLength; final boolean mIsClient; public SnepMessenger(boolean isClient, LlcpSocket socket, int fragmentLength) { mSocket = socket; mFragmentLength = fragmentLength; mIsClient = isClient; } public void sendMessage(SnepMessage msg) throws IOException { byte[] buffer = msg.toByteArray(); byte remoteContinue; if (mIsClient) { remoteContinue = SnepMessage.RESPONSE_CONTINUE; } else { remoteContinue = SnepMessage.REQUEST_CONTINUE; } if (DBG) Log.d(TAG, "about to send a " + buffer.length + " byte message"); // Send first fragment int length = Math.min(buffer.length, mFragmentLength); byte[] tmpBuffer = Arrays.copyOfRange(buffer, 0, length); if (DBG) Log.d(TAG, "about to send a " + length + " byte fragment"); mSocket.send(tmpBuffer); if (length == buffer.length) { return; } // Look for Continue or Reject from peer. int offset = length; byte[] responseBytes = new byte[HEADER_LENGTH]; mSocket.receive(responseBytes); SnepMessage snepResponse; try { snepResponse = SnepMessage.fromByteArray(responseBytes); } catch (FormatException e) { throw new IOException("Invalid SNEP message", e); } if (DBG) Log.d(TAG, "Got response from first fragment: " + snepResponse.getField()); if (snepResponse.getField() != remoteContinue) { throw new IOException("Invalid response from server (" + snepResponse.getField() + ")"); } // Send remaining fragments. while (offset < buffer.length) { length = Math.min(buffer.length - offset, mFragmentLength); tmpBuffer = Arrays.copyOfRange(buffer, offset, offset + length); if (DBG) Log.d(TAG, "about to send a " + length + " byte fragment"); mSocket.send(tmpBuffer); offset += length; } } public SnepMessage getMessage() throws IOException, SnepException { ByteArrayOutputStream buffer = new ByteArrayOutputStream(mFragmentLength); byte[] partial = new byte[mFragmentLength]; int size; int requestSize = 0; int readSize = 0; byte requestVersion = 0; boolean doneReading = false; byte fieldContinue; byte fieldReject; if (mIsClient) { fieldContinue = SnepMessage.REQUEST_CONTINUE; fieldReject = SnepMessage.REQUEST_REJECT; } else { fieldContinue = SnepMessage.RESPONSE_CONTINUE; fieldReject = SnepMessage.RESPONSE_REJECT; } size = mSocket.receive(partial); if (DBG) Log.d(TAG, "read " + size + " bytes"); if (size < 0) { try { mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray()); } catch (IOException e) { // Ignore } throw new IOException("Error reading SNEP message."); } else if (size < HEADER_LENGTH) { try { mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray()); } catch (IOException e) { // Ignore } throw new IOException("Invalid fragment from sender."); } else { readSize = size - HEADER_LENGTH; buffer.write(partial, 0, size); } DataInputStream dataIn = new DataInputStream(new ByteArrayInputStream(partial)); requestVersion = dataIn.readByte(); byte requestField = dataIn.readByte(); requestSize = dataIn.readInt(); if (DBG) Log.d(TAG, "read " + readSize + " of " + requestSize); if (((requestVersion & 0xF0) >> 4) != SnepMessage.VERSION_MAJOR) { // Invalid protocol version; treat message as complete. return new SnepMessage(requestVersion, requestField, 0, 0, null); } if (requestSize > readSize) { if (DBG) Log.d(TAG, "requesting continuation"); mSocket.send(SnepMessage.getMessage(fieldContinue).toByteArray()); } else { doneReading = true; } // Remaining fragments while (!doneReading) { try { size = mSocket.receive(partial); if (DBG) Log.d(TAG, "read " + size + " bytes"); if (size < 0) { try { mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray()); } catch (IOException e) { // Ignore } throw new IOException(); } else { readSize += size; buffer.write(partial, 0, size); if (readSize == requestSize) { doneReading = true; } } } catch (IOException e) { try { mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray()); } catch (IOException e2) { // Ignore } throw e; } } // Build NDEF message set from the stream try { return SnepMessage.fromByteArray(buffer.toByteArray()); } catch (FormatException e) { Log.e(TAG, "Badly formatted NDEF message, ignoring", e); throw new SnepException(e); } } public void close() throws IOException { mSocket.close(); } }