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