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 android.nfc.FormatException;
     20 import android.nfc.NdefMessage;
     21 import android.nfc.NdefRecord;
     22 import com.android.nfc.NfcService;
     23 import com.android.nfc.sneptest.DtaSnepClient;
     24 
     25 import java.io.ByteArrayOutputStream;
     26 import java.io.DataOutputStream;
     27 import java.io.IOException;
     28 import java.io.UnsupportedEncodingException;
     29 import java.nio.ByteBuffer;
     30 
     31 public final class SnepMessage {
     32     public static final byte VERSION_MAJOR = (byte) 0x1;
     33     public static final byte VERSION_MINOR = (byte) 0x0;
     34     public static final byte VERSION = (0xF0 & (VERSION_MAJOR << 4)) | (0x0F & VERSION_MINOR);
     35 
     36     public static final byte REQUEST_CONTINUE = (byte) 0x00;
     37     public static final byte REQUEST_GET = (byte) 0x01;
     38     public static final byte REQUEST_PUT = (byte) 0x02;
     39     public static final byte REQUEST_RFU = (byte) 0x03;
     40     public static final byte REQUEST_REJECT = (byte) 0x7F;
     41 
     42     public static final byte RESPONSE_CONTINUE = (byte) 0x80;
     43     public static final byte RESPONSE_SUCCESS = (byte) 0x81;
     44     public static final byte RESPONSE_NOT_FOUND = (byte) 0xC0;
     45     public static final byte RESPONSE_EXCESS_DATA = (byte) 0xC1;
     46     public static final byte RESPONSE_BAD_REQUEST = (byte) 0xC2;
     47     public static final byte RESPONSE_NOT_IMPLEMENTED = (byte) 0xE0;
     48     public static final byte RESPONSE_UNSUPPORTED_VERSION = (byte) 0xE1;
     49     public static final byte RESPONSE_REJECT = (byte) 0xFF;
     50 
     51     private static final byte[] NDEF_SHORT_TEST_RECORD = new byte[]{(byte)0xD1,(byte)0x01,(byte)0x1E,(byte)0x54,(byte)0x02,(byte)0x6C,(byte)0x61, // NDEF Header
     52             (byte)0x4C,(byte)0x6F,(byte)0x72,(byte)0x65,(byte)0x6D,(byte)0x20,(byte)0x69,(byte)0x70,(byte)0x73,(byte)0x75, // Payload
     53             (byte)0x6D,(byte)0x20,(byte)0x64,(byte)0x6F,(byte)0x6C,(byte)0x6F,(byte)0x72,(byte)0x20,(byte)0x73,(byte)0x69,
     54             (byte)0x74,(byte)0x20,(byte)0x61,(byte)0x6D,(byte)0x65,(byte)0x74,(byte)0x2E};
     55 
     56     private static final byte[] NDEF_TEST_RECORD = new byte[]{(byte)0xC1,(byte)0x01,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x1E,(byte)0x54,(byte)0x02,(byte)0x6C,(byte)0x61, // NDEF Header
     57             (byte)0x4C,(byte)0x6F,(byte)0x72,(byte)0x65,(byte)0x6D,(byte)0x20,(byte)0x69,(byte)0x70,(byte)0x73,(byte)0x75, // Payload
     58             (byte)0x6D,(byte)0x20,(byte)0x64,(byte)0x6F,(byte)0x6C,(byte)0x6F,(byte)0x72,(byte)0x20,(byte)0x73,(byte)0x69,
     59             (byte)0x74,(byte)0x20,(byte)0x61,(byte)0x6D,(byte)0x65,(byte)0x74,(byte)0x2E};
     60 
     61     private static final int HEADER_LENGTH = 6;
     62     public static final int MAL_IUT = 0x0400;
     63     public static final int MAL = 0xFFFFFFFF;
     64     private final byte mVersion;
     65     private final byte mField;
     66     private final int mLength;
     67     private final int mAcceptableLength;
     68     private final NdefMessage mNdefMessage;
     69 
     70     public static SnepMessage getGetRequest(int acceptableLength, NdefMessage ndef) {
     71         return new SnepMessage(VERSION, REQUEST_GET, 4 + ndef.toByteArray().length,
     72                 acceptableLength, ndef);
     73     }
     74 
     75     public static SnepMessage getPutRequest(NdefMessage ndef) {
     76         return new SnepMessage(VERSION, REQUEST_PUT, ndef.toByteArray().length, 0, ndef);
     77     }
     78 
     79     public static SnepMessage getMessage(byte field) {
     80         return new SnepMessage(VERSION, field, 0, 0, null);
     81     }
     82 
     83     public static SnepMessage getSuccessResponse(NdefMessage ndef) {
     84         if (ndef == null) {
     85             return new SnepMessage(VERSION, RESPONSE_SUCCESS, 0, 0, null);
     86         } else {
     87             return new SnepMessage(VERSION, RESPONSE_SUCCESS, ndef.toByteArray().length, 0, ndef);
     88         }
     89     }
     90 
     91     public static SnepMessage fromByteArray(byte[] data) throws FormatException {
     92         return new SnepMessage(data);
     93     }
     94 
     95     public static NdefMessage getLargeNdef() throws UnsupportedEncodingException {
     96         String snepTestData2 = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus at"
     97                 +" lorem nunc, ut venenatis quam. Etiam id dolor quam, at viverra dolor."
     98                 +" Phasellus eu lacus ligula, quis euismod erat. Sed feugiat, ligula at"
     99                 +" mollis aliquet, justo lacus condimentum eros, non tincidunt neque"
    100                 +" ipsum eu risus. Sed adipiscing dui euismod tellus ullamcorper ornare."
    101                 +" Phasellus mattis risus et lectus euismod eu fermentum sem cursus."
    102                 +" Phasellus tristique consectetur mauris eu porttitor. Sed lobortis"
    103                 +" porttitor orci.";
    104         String lang = "la";
    105         byte[] textBytes = snepTestData2.getBytes();
    106         byte[] langBytes = lang.getBytes("US-ASCII");
    107         int langLength = langBytes.length;
    108         int textLength = textBytes.length;
    109 
    110         byte[] payload = new byte[1 + langLength + textLength];
    111         payload[0] = (byte) langLength;
    112 
    113         System.arraycopy(langBytes, 0, payload, 1, langLength);
    114         System.arraycopy(textBytes, 0, payload, 1 + langLength, textLength);
    115 
    116         NdefRecord data2 = new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], payload);
    117         return new NdefMessage(new NdefRecord[]{data2});
    118     }
    119 
    120     public static NdefMessage getSmallNdef() throws UnsupportedEncodingException {
    121         String snepTestData1 = "Lorem ipsum dolor sit amet.";
    122         String lang = "la";
    123         byte[] textBytes = snepTestData1.getBytes();
    124         byte[] langBytes = lang.getBytes("US-ASCII");
    125         int langLength = langBytes.length;
    126         int textLength = textBytes.length;
    127 
    128         byte[] payload = new byte[1 + langLength + textLength];
    129         payload[0] = (byte) langLength;
    130 
    131         System.arraycopy(langBytes, 0, payload, 1, langLength);
    132         System.arraycopy(textBytes, 0, payload, 1 + langLength, textLength);
    133 
    134         NdefRecord data1 = new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], payload);
    135         return new NdefMessage(new NdefRecord[]{data1});
    136     }
    137 
    138     private SnepMessage(byte[] data) throws FormatException {
    139         ByteBuffer input = ByteBuffer.wrap(data);
    140         int ndefOffset;
    141         int ndefLength;
    142 
    143         mVersion = input.get();
    144         mField = input.get();
    145         mLength = input.getInt();
    146         if (mField == REQUEST_GET) {
    147             mAcceptableLength = input.getInt();
    148             ndefOffset = HEADER_LENGTH + 4;
    149             ndefLength = mLength - 4;
    150         } else {
    151             mAcceptableLength = -1;
    152             ndefOffset = HEADER_LENGTH;
    153             ndefLength = mLength;
    154         }
    155 
    156         if (ndefLength > 0) {
    157             byte[] bytes = new byte[ndefLength];
    158             System.arraycopy(data, ndefOffset, bytes, 0, ndefLength);
    159             mNdefMessage = new NdefMessage(bytes);
    160         } else {
    161             mNdefMessage = null;
    162         }
    163     }
    164 
    165     SnepMessage(byte version, byte field, int length, int acceptableLength,
    166             NdefMessage ndefMessage) {
    167         mVersion = version;
    168         mField = field;
    169         mLength = length;
    170         mAcceptableLength = acceptableLength;
    171         mNdefMessage = ndefMessage;
    172     }
    173 
    174     public byte[] toByteArray() {
    175         byte[] bytes;
    176         if (mNdefMessage != null) {
    177             if (NfcService.sIsDtaMode && DtaSnepClient.mTestCaseId != 0) {
    178                if (DtaSnepClient.mTestCaseId == 5 || DtaSnepClient.mTestCaseId == 6) {
    179                    bytes = mNdefMessage.toByteArray();
    180                } else {
    181                    if (NfcService.sIsShortRecordLayout) {
    182                        bytes = NDEF_SHORT_TEST_RECORD;
    183                    } else {
    184                        bytes = NDEF_TEST_RECORD;
    185                    }
    186                }
    187             } else {
    188                 bytes = mNdefMessage.toByteArray();
    189             }
    190         } else {
    191             bytes = new byte[0];
    192         }
    193 
    194         ByteArrayOutputStream buffer;
    195         try {
    196             if (mField == REQUEST_GET) {
    197                 buffer = new ByteArrayOutputStream(bytes.length + HEADER_LENGTH + 4);
    198             } else {
    199                 buffer = new ByteArrayOutputStream(bytes.length + HEADER_LENGTH);
    200             }
    201 
    202             DataOutputStream output = new DataOutputStream(buffer);
    203             output.writeByte(mVersion);
    204             output.writeByte(mField);
    205             if (mField == REQUEST_GET) {
    206                 output.writeInt(bytes.length + 4);
    207                 output.writeInt(mAcceptableLength);
    208             } else {
    209                 output.writeInt(bytes.length);
    210             }
    211             output.write(bytes);
    212         } catch(IOException e) {
    213             return null;
    214         }
    215 
    216         return buffer.toByteArray();
    217     }
    218 
    219     public NdefMessage getNdefMessage() {
    220         return mNdefMessage;
    221     }
    222 
    223     public byte getField() {
    224         return mField;
    225     }
    226 
    227     public byte getVersion() {
    228         return mVersion;
    229     }
    230 
    231     public int getLength() {
    232         return mLength;
    233     }
    234 
    235     public int getAcceptableLength() {
    236         if (mField != REQUEST_GET) {
    237             throw new UnsupportedOperationException(
    238                     "Acceptable Length only available on get request messages.");
    239         }
    240         return mAcceptableLength;
    241     }
    242 }
    243