blob: 5078fe8cc4c051033327653b2de041d20ed42bf2 [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 com.android.nfc.NfcService;
import com.android.nfc.sneptest.DtaSnepClient;
import com.android.nfc.sneptest.ExtDtaSnepServer;
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() + ")");
}
// Look for wrong/invalid request or response from peer
if (NfcService.sIsDtaMode) {
if (mIsClient && (DtaSnepClient.mTestCaseId == 6)) {
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;
mSocket.receive(responseBytes);
try {
snepResponse = SnepMessage.fromByteArray(responseBytes);
} catch (FormatException e) {
throw new IOException("Invalid SNEP message", e);
}
if (DBG) Log.d(TAG, "Got response from second fragment: " + snepResponse.getField());
if (snepResponse.getField() == remoteContinue) {
close();
return;
}
}
}
// 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);
if (NfcService.sIsDtaMode) {
if (!mIsClient && ExtDtaSnepServer.mTestCaseId == 0x01) {
mSocket.receive(responseBytes);
try {
snepResponse = SnepMessage.fromByteArray(responseBytes);
} catch (FormatException e) {
throw new IOException("Invalid SNEP message", e);
}
if (DBG) Log.d(TAG, "Got continue response after second fragment: and now disconnecting..." + snepResponse.getField());
if (snepResponse.getField() == remoteContinue) {
close();
return;
}
}
}
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;
byte requestField = 0; // for DTA Mode
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 {
if (NfcService.sIsDtaMode && mIsClient) {
if (DBG) Log.d(TAG, "Invalid header length");
close();
} else {
mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
}
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();
requestField = dataIn.readByte();
requestSize = dataIn.readInt();
if (DBG) Log.d(TAG, "read " + readSize + " of " + requestSize);
if (((requestVersion & 0xF0) >> 4) != SnepMessage.VERSION_MAJOR) {
if (NfcService.sIsDtaMode) {
sendMessage(SnepMessage.getMessage(SnepMessage.RESPONSE_UNSUPPORTED_VERSION));
close();
} else {
if (NfcService.sIsDtaMode) {
sendMessage(SnepMessage.getMessage(SnepMessage.RESPONSE_UNSUPPORTED_VERSION));
close();
} else {
// Invalid protocol version; treat message as complete.
return new SnepMessage(requestVersion, requestField, 0, 0, null);
}
}
}
if (NfcService.sIsDtaMode) {
if (!mIsClient && (requestField == SnepMessage.RESPONSE_CONTINUE)|| // added for TC_S_BIT_B1_01_X
requestField == SnepMessage.RESPONSE_SUCCESS ||
requestField == SnepMessage.RESPONSE_NOT_FOUND) {
if (DBG) Log.d(TAG, "errorneous response received, disconnecting client");
close();
}
if (!mIsClient && requestField == SnepMessage.REQUEST_RFU) {
if (DBG) Log.d(TAG, "unknown request received, disconnecting client");
sendMessage(SnepMessage.getMessage(SnepMessage.RESPONSE_BAD_REQUEST));
close();
}
// added for TC_C_BIT_BI_01_0
if (mIsClient && requestField == SnepMessage.REQUEST_PUT) {
if (DBG) Log.d(TAG, "errorneous PUT request received, disconnecting from server");
close();
}
// added for TC_C_GET_BV_03
if (mIsClient && (requestSize > SnepMessage.MAL_IUT)) {
if (DBG) Log.d(TAG, "responding reject");
return new SnepMessage(requestVersion, requestField, requestSize, 0, null);
}
//added for TC_S_ACC_BV_05_0&1 and TC_S_ACC_BV_06_0&1
if (!mIsClient && ((requestSize > SnepMessage.MAL_IUT) ||
requestSize == SnepMessage.MAL)) {
if (DBG) Log.d(TAG, "responding reject");
return new SnepMessage(requestVersion, requestField, requestSize, 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();
}
}