12ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson/* 22ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson * Copyright (C) 2011 The Android Open Source Project 32ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson * 42ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson * Licensed under the Apache License, Version 2.0 (the "License"); 52ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson * you may not use this file except in compliance with the License. 62ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson * You may obtain a copy of the License at 72ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson * 82ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson * http://www.apache.org/licenses/LICENSE-2.0 92ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson * 102ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson * Unless required by applicable law or agreed to in writing, software 112ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson * distributed under the License is distributed on an "AS IS" BASIS, 122ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 132ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson * See the License for the specific language governing permissions and 142ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson * limitations under the License. 152ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson */ 162ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson 172ef360deaff9f17aa72d5749ceee283cc80897afBen Dodsonpackage com.android.nfc.snep; 182ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson 194a61d3b45e81c0070538f94747a70a49c78f12faJeff Hamiltonimport com.android.nfc.DeviceHost.LlcpSocket; 204a61d3b45e81c0070538f94747a70a49c78f12faJeff Hamilton 214a61d3b45e81c0070538f94747a70a49c78f12faJeff Hamiltonimport android.nfc.FormatException; 224a61d3b45e81c0070538f94747a70a49c78f12faJeff Hamiltonimport android.util.Log; 234a61d3b45e81c0070538f94747a70a49c78f12faJeff Hamilton 242ef360deaff9f17aa72d5749ceee283cc80897afBen Dodsonimport java.io.ByteArrayInputStream; 252ef360deaff9f17aa72d5749ceee283cc80897afBen Dodsonimport java.io.ByteArrayOutputStream; 262ef360deaff9f17aa72d5749ceee283cc80897afBen Dodsonimport java.io.DataInputStream; 272ef360deaff9f17aa72d5749ceee283cc80897afBen Dodsonimport java.io.IOException; 282ef360deaff9f17aa72d5749ceee283cc80897afBen Dodsonimport java.util.Arrays; 292ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson 302ef360deaff9f17aa72d5749ceee283cc80897afBen Dodsonpublic class SnepMessenger { 312ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson private static final String TAG = "SnepMessager"; 322ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson private static final boolean DBG = false; 332ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson private static final int HEADER_LENGTH = 6; 342ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson final LlcpSocket mSocket; 352ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson final int mFragmentLength; 362ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson final boolean mIsClient; 372ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson 382ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson public SnepMessenger(boolean isClient, LlcpSocket socket, int fragmentLength) { 392ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson mSocket = socket; 402ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson mFragmentLength = fragmentLength; 412ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson mIsClient = isClient; 422ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson } 432ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson 442ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson public void sendMessage(SnepMessage msg) throws IOException { 452ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson byte[] buffer = msg.toByteArray(); 462ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson byte remoteContinue; 472ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson if (mIsClient) { 482ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson remoteContinue = SnepMessage.RESPONSE_CONTINUE; 492ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson } else { 502ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson remoteContinue = SnepMessage.REQUEST_CONTINUE; 512ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson } 522ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson if (DBG) Log.d(TAG, "about to send a " + buffer.length + " byte message"); 532ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson 542ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson // Send first fragment 552ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson int length = Math.min(buffer.length, mFragmentLength); 562ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson byte[] tmpBuffer = Arrays.copyOfRange(buffer, 0, length); 572ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson if (DBG) Log.d(TAG, "about to send a " + length + " byte fragment"); 582ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson mSocket.send(tmpBuffer); 592ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson 602ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson if (length == buffer.length) { 612ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson return; 622ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson } 632ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson 642ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson // Look for Continue or Reject from peer. 652ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson int offset = length; 662ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson byte[] responseBytes = new byte[HEADER_LENGTH]; 672ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson mSocket.receive(responseBytes); 682ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson SnepMessage snepResponse; 692ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson try { 702ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson snepResponse = SnepMessage.fromByteArray(responseBytes); 712ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson } catch (FormatException e) { 722ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson throw new IOException("Invalid SNEP message", e); 732ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson } 742ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson 752ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson if (DBG) Log.d(TAG, "Got response from first fragment: " + snepResponse.getField()); 762ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson if (snepResponse.getField() != remoteContinue) { 772ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson throw new IOException("Invalid response from server (" + 782ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson snepResponse.getField() + ")"); 792ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson } 802ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson 812ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson // Send remaining fragments. 822ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson while (offset < buffer.length) { 832ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson length = Math.min(buffer.length - offset, mFragmentLength); 842ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson tmpBuffer = Arrays.copyOfRange(buffer, offset, offset + length); 852ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson if (DBG) Log.d(TAG, "about to send a " + length + " byte fragment"); 862ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson mSocket.send(tmpBuffer); 872ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson offset += length; 882ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson } 892ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson } 902ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson 912ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson public SnepMessage getMessage() throws IOException, SnepException { 922ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson ByteArrayOutputStream buffer = new ByteArrayOutputStream(mFragmentLength); 932ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson byte[] partial = new byte[mFragmentLength]; 942ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson int size; 952ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson int requestSize = 0; 962ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson int readSize = 0; 972ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson byte requestVersion = 0; 982ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson boolean doneReading = false; 992ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson byte fieldContinue; 1002ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson byte fieldReject; 1012ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson if (mIsClient) { 1022ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson fieldContinue = SnepMessage.REQUEST_CONTINUE; 1032ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson fieldReject = SnepMessage.REQUEST_REJECT; 1042ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson } else { 1052ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson fieldContinue = SnepMessage.RESPONSE_CONTINUE; 1062ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson fieldReject = SnepMessage.RESPONSE_REJECT; 1072ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson } 1082ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson 1092ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson size = mSocket.receive(partial); 1102ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson if (DBG) Log.d(TAG, "read " + size + " bytes"); 1112ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson if (size < 0) { 1122ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson try { 1132ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray()); 1142ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson } catch (IOException e) { 1152ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson // Ignore 1162ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson } 1172ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson throw new IOException("Error reading SNEP message."); 1182ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson } else if (size < HEADER_LENGTH) { 1192ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson try { 1202ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray()); 1212ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson } catch (IOException e) { 1222ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson // Ignore 1232ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson } 1242ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson throw new IOException("Invalid fragment from sender."); 1252ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson } else { 1262ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson readSize = size - HEADER_LENGTH; 1272ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson buffer.write(partial, 0, size); 1282ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson } 1292ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson 1302ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson DataInputStream dataIn = new DataInputStream(new ByteArrayInputStream(partial)); 1312ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson requestVersion = dataIn.readByte(); 1322ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson byte requestField = dataIn.readByte(); 1332ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson requestSize = dataIn.readInt(); 1342ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson 1352ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson if (DBG) Log.d(TAG, "read " + readSize + " of " + requestSize); 1362ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson 1372ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson if (((requestVersion & 0xF0) >> 4) != SnepMessage.VERSION_MAJOR) { 1382ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson // Invalid protocol version; treat message as complete. 1392ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson return new SnepMessage(requestVersion, requestField, 0, 0, null); 1402ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson } 1412ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson 1422ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson if (requestSize > readSize) { 1432ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson if (DBG) Log.d(TAG, "requesting continuation"); 1442ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson mSocket.send(SnepMessage.getMessage(fieldContinue).toByteArray()); 1452ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson } else { 1462ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson doneReading = true; 1472ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson } 1482ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson 1492ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson // Remaining fragments 1502ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson while (!doneReading) { 1512ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson try { 1522ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson size = mSocket.receive(partial); 1532ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson if (DBG) Log.d(TAG, "read " + size + " bytes"); 1542ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson if (size < 0) { 1552ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson try { 1562ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray()); 1572ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson } catch (IOException e) { 1582ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson // Ignore 1592ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson } 1602ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson throw new IOException(); 1612ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson } else { 1622ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson readSize += size; 1632ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson buffer.write(partial, 0, size); 1642ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson if (readSize == requestSize) { 1652ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson doneReading = true; 1662ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson } 1672ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson } 1682ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson } catch (IOException e) { 1692ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson try { 1702ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray()); 1712ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson } catch (IOException e2) { 1722ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson // Ignore 1732ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson } 1742ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson throw e; 1752ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson } 1762ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson } 1772ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson 1782ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson // Build NDEF message set from the stream 1792ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson try { 1802ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson return SnepMessage.fromByteArray(buffer.toByteArray()); 1812ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson } catch (FormatException e) { 1822ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson Log.e(TAG, "Badly formatted NDEF message, ignoring", e); 1832ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson throw new SnepException(e); 1842ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson } 1852ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson } 186154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson 187154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson public void close() throws IOException { 188154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson mSocket.close(); 189154fcb2b2bca5dfb27bb251efee8f13f1ebe56f8Ben Dodson } 1902ef360deaff9f17aa72d5749ceee283cc80897afBen Dodson} 191