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 
22 import java.io.ByteArrayOutputStream;
23 import java.io.DataOutputStream;
24 import java.io.IOException;
25 import java.nio.ByteBuffer;
26 
27 public final class SnepMessage {
28     public static final byte VERSION_MAJOR = (byte) 0x1;
29     public static final byte VERSION_MINOR = (byte) 0x0;
30     public static final byte VERSION = (0xF0 & (VERSION_MAJOR << 4)) | (0x0F & VERSION_MINOR);
31 
32     public static final byte REQUEST_CONTINUE = (byte) 0x00;
33     public static final byte REQUEST_GET = (byte) 0x01;
34     public static final byte REQUEST_PUT = (byte) 0x02;
35     public static final byte REQUEST_REJECT = (byte) 0x7F;
36 
37     public static final byte RESPONSE_CONTINUE = (byte) 0x80;
38     public static final byte RESPONSE_SUCCESS = (byte) 0x81;
39     public static final byte RESPONSE_NOT_FOUND = (byte) 0xC0;
40     public static final byte RESPONSE_EXCESS_DATA = (byte) 0xC1;
41     public static final byte RESPONSE_BAD_REQUEST = (byte) 0xC2;
42     public static final byte RESPONSE_NOT_IMPLEMENTED = (byte) 0xE0;
43     public static final byte RESPONSE_UNSUPPORTED_VERSION = (byte) 0xE1;
44     public static final byte RESPONSE_REJECT = (byte) 0xFF;
45 
46     private static final int HEADER_LENGTH = 6;
47     private final byte mVersion;
48     private final byte mField;
49     private final int mLength;
50     private final int mAcceptableLength;
51     private final NdefMessage mNdefMessage;
52 
getGetRequest(int acceptableLength, NdefMessage ndef)53     public static SnepMessage getGetRequest(int acceptableLength, NdefMessage ndef) {
54         return new SnepMessage(VERSION, REQUEST_GET, 4 + ndef.toByteArray().length,
55                 acceptableLength, ndef);
56     }
57 
getPutRequest(NdefMessage ndef)58     public static SnepMessage getPutRequest(NdefMessage ndef) {
59         return new SnepMessage(VERSION, REQUEST_PUT, ndef.toByteArray().length, 0, ndef);
60     }
61 
getMessage(byte field)62     public static SnepMessage getMessage(byte field) {
63         return new SnepMessage(VERSION, field, 0, 0, null);
64     }
65 
getSuccessResponse(NdefMessage ndef)66     public static SnepMessage getSuccessResponse(NdefMessage ndef) {
67         if (ndef == null) {
68             return new SnepMessage(VERSION, RESPONSE_SUCCESS, 0, 0, null);
69         } else {
70             return new SnepMessage(VERSION, RESPONSE_SUCCESS, ndef.toByteArray().length, 0, ndef);
71         }
72     }
73 
fromByteArray(byte[] data)74     public static SnepMessage fromByteArray(byte[] data) throws FormatException {
75         return new SnepMessage(data);
76     }
77 
SnepMessage(byte[] data)78     private SnepMessage(byte[] data) throws FormatException {
79         ByteBuffer input = ByteBuffer.wrap(data);
80         int ndefOffset;
81         int ndefLength;
82 
83         mVersion = input.get();
84         mField = input.get();
85         mLength = input.getInt();
86         if (mField == REQUEST_GET) {
87             mAcceptableLength = input.getInt();
88             ndefOffset = HEADER_LENGTH + 4;
89             ndefLength = mLength - 4;
90         } else {
91             mAcceptableLength = -1;
92             ndefOffset = HEADER_LENGTH;
93             ndefLength = mLength;
94         }
95 
96         if (ndefLength > 0) {
97             byte[] bytes = new byte[ndefLength];
98             System.arraycopy(data, ndefOffset, bytes, 0, ndefLength);
99             mNdefMessage = new NdefMessage(bytes);
100         } else {
101             mNdefMessage = null;
102         }
103     }
104 
SnepMessage(byte version, byte field, int length, int acceptableLength, NdefMessage ndefMessage)105     SnepMessage(byte version, byte field, int length, int acceptableLength,
106             NdefMessage ndefMessage) {
107         mVersion = version;
108         mField = field;
109         mLength = length;
110         mAcceptableLength = acceptableLength;
111         mNdefMessage = ndefMessage;
112     }
113 
toByteArray()114     public byte[] toByteArray() {
115         byte[] bytes;
116         if (mNdefMessage != null) {
117             bytes = mNdefMessage.toByteArray();
118         } else {
119             bytes = new byte[0];
120         }
121 
122         ByteArrayOutputStream buffer;
123         try {
124             if (mField == REQUEST_GET) {
125                 buffer = new ByteArrayOutputStream(bytes.length + HEADER_LENGTH + 4);
126             } else {
127                 buffer = new ByteArrayOutputStream(bytes.length + HEADER_LENGTH);
128             }
129 
130             DataOutputStream output = new DataOutputStream(buffer);
131             output.writeByte(mVersion);
132             output.writeByte(mField);
133             if (mField == REQUEST_GET) {
134                 output.writeInt(bytes.length + 4);
135                 output.writeInt(mAcceptableLength);
136             } else {
137                 output.writeInt(bytes.length);
138             }
139             output.write(bytes);
140         } catch(IOException e) {
141             return null;
142         }
143 
144         return buffer.toByteArray();
145     }
146 
getNdefMessage()147     public NdefMessage getNdefMessage() {
148         return mNdefMessage;
149     }
150 
getField()151     public byte getField() {
152         return mField;
153     }
154 
getVersion()155     public byte getVersion() {
156         return mVersion;
157     }
158 
getLength()159     public int getLength() {
160         return mLength;
161     }
162 
getAcceptableLength()163     public int getAcceptableLength() {
164         if (mField != REQUEST_GET) {
165             throw new UnsupportedOperationException(
166                     "Acceptable Length only available on get request messages.");
167         }
168         return mAcceptableLength;
169     }
170 }
171