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 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 58 public static SnepMessage getPutRequest(NdefMessage ndef) { 59 return new SnepMessage(VERSION, REQUEST_PUT, ndef.toByteArray().length, 0, ndef); 60 } 61 62 public static SnepMessage getMessage(byte field) { 63 return new SnepMessage(VERSION, field, 0, 0, null); 64 } 65 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 74 public static SnepMessage fromByteArray(byte[] data) throws FormatException { 75 return new SnepMessage(data); 76 } 77 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 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 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 147 public NdefMessage getNdefMessage() { 148 return mNdefMessage; 149 } 150 151 public byte getField() { 152 return mField; 153 } 154 155 public byte getVersion() { 156 return mVersion; 157 } 158 159 public int getLength() { 160 return mLength; 161 } 162 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