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 import com.android.nfc.NfcService;
     21 import com.android.nfc.sneptest.DtaSnepClient;
     22 import com.android.nfc.sneptest.ExtDtaSnepServer;
     23 
     24 import android.nfc.FormatException;
     25 import android.util.Log;
     26 
     27 import java.io.ByteArrayInputStream;
     28 import java.io.ByteArrayOutputStream;
     29 import java.io.DataInputStream;
     30 import java.io.IOException;
     31 import java.util.Arrays;
     32 
     33 public class SnepMessenger {
     34     private static final String TAG = "SnepMessager";
     35     private static final boolean DBG = false;
     36     private static final int HEADER_LENGTH = 6;
     37     final LlcpSocket mSocket;
     38     final int mFragmentLength;
     39     final boolean mIsClient;
     40 
     41     public SnepMessenger(boolean isClient, LlcpSocket socket, int fragmentLength) {
     42         mSocket = socket;
     43         mFragmentLength = fragmentLength;
     44         mIsClient = isClient;
     45     }
     46 
     47     public void sendMessage(SnepMessage msg) throws IOException {
     48         byte[] buffer = msg.toByteArray();
     49         byte remoteContinue;
     50         if (mIsClient) {
     51             remoteContinue = SnepMessage.RESPONSE_CONTINUE;
     52         } else {
     53             remoteContinue = SnepMessage.REQUEST_CONTINUE;
     54         }
     55         if (DBG) Log.d(TAG, "about to send a " + buffer.length + " byte message");
     56 
     57         // Send first fragment
     58         int length = Math.min(buffer.length, mFragmentLength);
     59         byte[] tmpBuffer = Arrays.copyOfRange(buffer, 0, length);
     60         if (DBG) Log.d(TAG, "about to send a " + length + " byte fragment");
     61         mSocket.send(tmpBuffer);
     62 
     63         if (length == buffer.length) {
     64             return;
     65         }
     66 
     67         // Look for Continue or Reject from peer.
     68         int offset = length;
     69         byte[] responseBytes = new byte[HEADER_LENGTH];
     70         mSocket.receive(responseBytes);
     71         SnepMessage snepResponse;
     72         try {
     73             snepResponse = SnepMessage.fromByteArray(responseBytes);
     74         } catch (FormatException e) {
     75             throw new IOException("Invalid SNEP message", e);
     76         }
     77 
     78         if (DBG) Log.d(TAG, "Got response from first fragment: " + snepResponse.getField());
     79         if (snepResponse.getField() != remoteContinue) {
     80             throw new IOException("Invalid response from server (" +
     81                     snepResponse.getField() + ")");
     82         }
     83         // Look for wrong/invalid request or response from peer
     84        if (NfcService.sIsDtaMode) {
     85             if (mIsClient && (DtaSnepClient.mTestCaseId == 6)) {
     86                 length = Math.min(buffer.length - offset, mFragmentLength);
     87                 tmpBuffer = Arrays.copyOfRange(buffer, offset, offset + length);
     88                 if (DBG) Log.d(TAG, "about to send a " + length + " byte fragment");
     89                 mSocket.send(tmpBuffer);
     90                 offset += length;
     91 
     92                 mSocket.receive(responseBytes);
     93 
     94                 try {
     95                     snepResponse = SnepMessage.fromByteArray(responseBytes);
     96                 } catch (FormatException e) {
     97                     throw new IOException("Invalid SNEP message", e);
     98                 }
     99                 if (DBG) Log.d(TAG, "Got response from second fragment: " + snepResponse.getField());
    100                 if (snepResponse.getField() == remoteContinue) {
    101                     close();
    102                     return;
    103                 }
    104             }
    105         }
    106 
    107         // Send remaining fragments.
    108         while (offset < buffer.length) {
    109             length = Math.min(buffer.length - offset, mFragmentLength);
    110             tmpBuffer = Arrays.copyOfRange(buffer, offset, offset + length);
    111             if (DBG) Log.d(TAG, "about to send a " + length + " byte fragment");
    112             mSocket.send(tmpBuffer);
    113 
    114             if (NfcService.sIsDtaMode) {
    115                 if (!mIsClient && ExtDtaSnepServer.mTestCaseId == 0x01) {
    116                     mSocket.receive(responseBytes);
    117                     try {
    118                         snepResponse = SnepMessage.fromByteArray(responseBytes);
    119                     } catch (FormatException e) {
    120                         throw new IOException("Invalid SNEP message", e);
    121                     }
    122                     if (DBG) Log.d(TAG, "Got continue response after second fragment: and now disconnecting..." + snepResponse.getField());
    123                     if (snepResponse.getField() == remoteContinue) {
    124                         close();
    125                         return;
    126                     }
    127                 }
    128             }
    129 
    130             offset += length;
    131         }
    132     }
    133 
    134     public SnepMessage getMessage() throws IOException, SnepException {
    135         ByteArrayOutputStream buffer = new ByteArrayOutputStream(mFragmentLength);
    136         byte[] partial = new byte[mFragmentLength];
    137         int size;
    138         int requestSize = 0;
    139         int readSize = 0;
    140         byte requestVersion = 0;
    141         byte requestField = 0; // for DTA Mode
    142         boolean doneReading = false;
    143         byte fieldContinue;
    144         byte fieldReject;
    145         if (mIsClient) {
    146             fieldContinue = SnepMessage.REQUEST_CONTINUE;
    147             fieldReject = SnepMessage.REQUEST_REJECT;
    148         } else {
    149             fieldContinue = SnepMessage.RESPONSE_CONTINUE;
    150             fieldReject = SnepMessage.RESPONSE_REJECT;
    151         }
    152 
    153         size = mSocket.receive(partial);
    154         if (DBG) Log.d(TAG, "read " + size + " bytes");
    155         if (size < 0) {
    156             try {
    157                 mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
    158             } catch (IOException e) {
    159                 // Ignore
    160             }
    161             throw new IOException("Error reading SNEP message.");
    162         } else if (size < HEADER_LENGTH) {
    163             try {
    164                 if (NfcService.sIsDtaMode && mIsClient) {
    165                     if (DBG) Log.d(TAG, "Invalid header length");
    166                     close();
    167                 } else {
    168                     mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
    169 
    170                 }
    171                 mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
    172             } catch (IOException e) {
    173                 // Ignore
    174             }
    175             throw new IOException("Invalid fragment from sender.");
    176         } else {
    177             readSize = size - HEADER_LENGTH;
    178             buffer.write(partial, 0, size);
    179         }
    180 
    181         DataInputStream dataIn = new DataInputStream(new ByteArrayInputStream(partial));
    182         requestVersion = dataIn.readByte();
    183         requestField = dataIn.readByte();
    184         requestSize = dataIn.readInt();
    185 
    186         if (DBG) Log.d(TAG, "read " + readSize + " of " + requestSize);
    187 
    188         if (((requestVersion & 0xF0) >> 4) != SnepMessage.VERSION_MAJOR) {
    189             if (NfcService.sIsDtaMode) {
    190                 sendMessage(SnepMessage.getMessage(SnepMessage.RESPONSE_UNSUPPORTED_VERSION));
    191                 close();
    192             } else {
    193             if (NfcService.sIsDtaMode) {
    194                 sendMessage(SnepMessage.getMessage(SnepMessage.RESPONSE_UNSUPPORTED_VERSION));
    195                 close();
    196             } else {
    197                 // Invalid protocol version; treat message as complete.
    198                 return new SnepMessage(requestVersion, requestField, 0, 0, null);
    199             }
    200             }
    201 
    202         }
    203 
    204         if (NfcService.sIsDtaMode) {
    205             if (!mIsClient && (requestField == SnepMessage.RESPONSE_CONTINUE)||  // added for TC_S_BIT_B1_01_X
    206                               requestField == SnepMessage.RESPONSE_SUCCESS ||
    207                               requestField == SnepMessage.RESPONSE_NOT_FOUND) {
    208                 if (DBG) Log.d(TAG, "errorneous response received, disconnecting client");
    209                 close();
    210             }
    211             if (!mIsClient && requestField == SnepMessage.REQUEST_RFU) {
    212                 if (DBG) Log.d(TAG, "unknown request received, disconnecting client");
    213                 sendMessage(SnepMessage.getMessage(SnepMessage.RESPONSE_BAD_REQUEST));
    214                 close();
    215             }
    216             // added for TC_C_BIT_BI_01_0
    217             if (mIsClient && requestField == SnepMessage.REQUEST_PUT) {
    218                 if (DBG) Log.d(TAG, "errorneous PUT request received, disconnecting from server");
    219                     close();
    220             }
    221             // added for TC_C_GET_BV_03
    222             if (mIsClient && (requestSize > SnepMessage.MAL_IUT)) {
    223                 if (DBG) Log.d(TAG, "responding reject");
    224                     return new SnepMessage(requestVersion, requestField, requestSize, 0, null);
    225             }
    226             //added for TC_S_ACC_BV_05_0&1 and TC_S_ACC_BV_06_0&1
    227             if (!mIsClient && ((requestSize > SnepMessage.MAL_IUT) ||
    228                                 requestSize == SnepMessage.MAL)) {
    229                 if (DBG) Log.d(TAG, "responding reject");
    230                     return new SnepMessage(requestVersion, requestField, requestSize, 0, null);
    231             }
    232         }
    233 
    234         if (requestSize > readSize) {
    235             if (DBG) Log.d(TAG, "requesting continuation");
    236             mSocket.send(SnepMessage.getMessage(fieldContinue).toByteArray());
    237         } else {
    238             doneReading = true;
    239         }
    240 
    241         // Remaining fragments
    242         while (!doneReading) {
    243             try {
    244                 size = mSocket.receive(partial);
    245                 if (DBG) Log.d(TAG, "read " + size + " bytes");
    246                 if (size < 0) {
    247                     try {
    248                         mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
    249                     } catch (IOException e) {
    250                         // Ignore
    251                     }
    252                     throw new IOException();
    253                 } else {
    254                     readSize += size;
    255                     buffer.write(partial, 0, size);
    256                     if (readSize == requestSize) {
    257                         doneReading = true;
    258                     }
    259                 }
    260             } catch (IOException e) {
    261                 try {
    262                     mSocket.send(SnepMessage.getMessage(fieldReject).toByteArray());
    263                 } catch (IOException e2) {
    264                     // Ignore
    265                 }
    266                 throw e;
    267             }
    268         }
    269 
    270         // Build NDEF message set from the stream
    271         try {
    272             return SnepMessage.fromByteArray(buffer.toByteArray());
    273         } catch (FormatException e) {
    274             Log.e(TAG, "Badly formatted NDEF message, ignoring", e);
    275             throw new SnepException(e);
    276         }
    277     }
    278 
    279     public void close() throws IOException {
    280         mSocket.close();
    281     }
    282 }
    283