Home | History | Annotate | Download | only in snep
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.nfc.snep;
     18 
     19 import com.android.nfc.DeviceHost.LlcpSocket;
     20 
     21 import android.nfc.FormatException;
     22 import android.util.Log;
     23 
     24 import java.io.ByteArrayInputStream;
     25 import java.io.ByteArrayOutputStream;
     26 import java.io.DataInputStream;
     27 import java.io.IOException;
     28 import java.util.Arrays;
     29 
     30 public class SnepMessenger {
     31     private static final String TAG = "SnepMessager";
     32     private static final boolean DBG = false;
     33     private static final int HEADER_LENGTH = 6;
     34     final LlcpSocket mSocket;
     35     final int mFragmentLength;
     36     final boolean mIsClient;
     37 
     38     public SnepMessenger(boolean isClient, LlcpSocket socket, int fragmentLength) {
     39         mSocket = socket;
     40         mFragmentLength = fragmentLength;
     41         mIsClient = isClient;
     42     }
     43 
     44     public void sendMessage(SnepMessage msg) throws IOException {
     45         byte[] buffer = msg.toByteArray();
     46         byte remoteContinue;
     47         if (mIsClient) {
     48             remoteContinue = SnepMessage.RESPONSE_CONTINUE;
     49         } else {
     50             remoteContinue = SnepMessage.REQUEST_CONTINUE;
     51         }
     52         if (DBG) Log.d(TAG, "about to send a " + buffer.length + " byte message");
     53 
     54         // Send first fragment
     55         int length = Math.min(buffer.length, mFragmentLength);
     56         byte[] tmpBuffer = Arrays.copyOfRange(buffer, 0, length);
     57         if (DBG) Log.d(TAG, "about to send a " + length + " byte fragment");
     58         mSocket.send(tmpBuffer);
     59 
     60         if (length == buffer.length) {
     61             return;
     62         }
     63 
     64         // Look for Continue or Reject from peer.
     65         int offset = length;
     66         byte[] responseBytes = new byte[HEADER_LENGTH];
     67         mSocket.receive(responseBytes);
     68         SnepMessage snepResponse;
     69         try {
     70             snepResponse = SnepMessage.fromByteArray(responseBytes);
     71         } catch (FormatException e) {
     72             throw new IOException("Invalid SNEP message", e);
     73         }
     74 
     75         if (DBG) Log.d(TAG, "Got response from first fragment: " + snepResponse.getField());
     76         if (snepResponse.getField() != remoteContinue) {
     77             throw new IOException("Invalid response from server (" +
     78                     snepResponse.getField() + ")");
     79         }
     80 
     81         // Send remaining fragments.
     82         while (offset < buffer.length) {
     83             length = Math.min(buffer.length - offset, mFragmentLength);
     84             tmpBuffer = Arrays.copyOfRange(buffer, offset, offset + length);
     85             if (DBG) Log.d(TAG, "about to send a " + length + " byte fragment");
     86             mSocket.send(tmpBuffer);
     87             offset += length;
     88         }
     89     }
     90 
     91     public SnepMessage getMessage() throws IOException, SnepException {
     92         ByteArrayOutputStream buffer = new ByteArrayOutputStream(mFragmentLength);
     93         byte[] partial = new byte[mFragmentLength];
     94         int size;
     95         int requestSize = 0;
     96         int readSize = 0;
     97         byte requestVersion = 0;
     98         boolean doneReading = false;
     99         byte fieldContinue;
    100         byte fieldReject;
    101         if (mIsClient) {
    102             fieldContinue = SnepMessage.REQUEST_CONTINUE;
    103             fieldReject = SnepMessage.REQUEST_REJECT;
    104         } else {
    105             fieldContinue = SnepMessage.RESPONSE_CONTINUE;
    106             fieldReject = SnepMessage.RESPONSE_REJECT;
    107         }
    108 
    109         size = mSocket.receive(partial);
    110         if (DBG) Log.d(TAG, "read " + size + " bytes");
    111         if (size < 0) {
    112             try {
    113                 mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
    114             } catch (IOException e) {
    115                 // Ignore
    116             }
    117             throw new IOException("Error reading SNEP message.");
    118         } else if (size < HEADER_LENGTH) {
    119             try {
    120                 mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
    121             } catch (IOException e) {
    122                 // Ignore
    123             }
    124             throw new IOException("Invalid fragment from sender.");
    125         } else {
    126             readSize = size - HEADER_LENGTH;
    127             buffer.write(partial, 0, size);
    128         }
    129 
    130         DataInputStream dataIn = new DataInputStream(new ByteArrayInputStream(partial));
    131         requestVersion = dataIn.readByte();
    132         byte requestField = dataIn.readByte();
    133         requestSize = dataIn.readInt();
    134 
    135         if (DBG) Log.d(TAG, "read " + readSize + " of " + requestSize);
    136 
    137         if (((requestVersion & 0xF0) >> 4) != SnepMessage.VERSION_MAJOR) {
    138             // Invalid protocol version; treat message as complete.
    139             return new SnepMessage(requestVersion, requestField, 0, 0, null);
    140         }
    141 
    142         if (requestSize > readSize) {
    143             if (DBG) Log.d(TAG, "requesting continuation");
    144             mSocket.send(SnepMessage.getMessage(fieldContinue).toByteArray());
    145         } else {
    146             doneReading = true;
    147         }
    148 
    149         // Remaining fragments
    150         while (!doneReading) {
    151             try {
    152                 size = mSocket.receive(partial);
    153                 if (DBG) Log.d(TAG, "read " + size + " bytes");
    154                 if (size < 0) {
    155                     try {
    156                         mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
    157                     } catch (IOException e) {
    158                         // Ignore
    159                     }
    160                     throw new IOException();
    161                 } else {
    162                     readSize += size;
    163                     buffer.write(partial, 0, size);
    164                     if (readSize == requestSize) {
    165                         doneReading = true;
    166                     }
    167                 }
    168             } catch (IOException e) {
    169                 try {
    170                     mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
    171                 } catch (IOException e2) {
    172                     // Ignore
    173                 }
    174                 throw e;
    175             }
    176         }
    177 
    178         // Build NDEF message set from the stream
    179         try {
    180             return SnepMessage.fromByteArray(buffer.toByteArray());
    181         } catch (FormatException e) {
    182             Log.e(TAG, "Badly formatted NDEF message, ignoring", e);
    183             throw new SnepException(e);
    184         }
    185     }
    186 
    187     public void close() throws IOException {
    188         mSocket.close();
    189     }
    190 }
    191