blob: 97ac1f0e3c6f583f52932cfef3463499c4c12018 [file] [log] [blame]
/*
* 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();
}
}