1 /* 2 * Copyright (C) 2016 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 android.net.wifi.nan; 18 19 import libcore.io.Memory; 20 21 import java.nio.BufferOverflowException; 22 import java.nio.ByteOrder; 23 import java.util.Iterator; 24 25 /** 26 * Utility class to construct and parse byte arrays using the TLV format - 27 * Type/Length/Value format. The utilities accept a configuration of the size of 28 * the Type field and the Length field. A Type field size of 0 is allowed - 29 * allowing usage for LV (no T) array formats. 30 * 31 * @hide PROPOSED_NAN_API 32 */ 33 public class TlvBufferUtils { TlvBufferUtils()34 private TlvBufferUtils() { 35 // no reason to ever create this class 36 } 37 38 /** 39 * Utility class to construct byte arrays using the TLV format - 40 * Type/Length/Value. 41 * <p> 42 * A constructor is created specifying the size of the Type (T) and Length 43 * (L) fields. A specification of zero size T field is allowed - resulting 44 * in LV type format. 45 * <p> 46 * The byte array is either provided (using 47 * {@link TlvConstructor#wrap(byte[])}) or allocated (using 48 * {@link TlvConstructor#allocate(int)}). 49 * <p> 50 * Values are added to the structure using the {@code TlvConstructor.put*()} 51 * methods. 52 * <p> 53 * The final byte array is obtained using {@link TlvConstructor#getArray()} 54 * and {@link TlvConstructor#getActualLength()} methods. 55 */ 56 public static class TlvConstructor { 57 private int mTypeSize; 58 private int mLengthSize; 59 60 private byte[] mArray; 61 private int mArrayLength; 62 private int mPosition; 63 64 /** 65 * Define a TLV constructor with the specified size of the Type (T) and 66 * Length (L) fields. 67 * 68 * @param typeSize Number of bytes used for the Type (T) field. Values 69 * of 0, 1, or 2 bytes are allowed. A specification of 0 70 * bytes implies that the field being constructed has the LV 71 * format rather than the TLV format. 72 * @param lengthSize Number of bytes used for the Length (L) field. 73 * Values of 1 or 2 bytes are allowed. 74 */ TlvConstructor(int typeSize, int lengthSize)75 public TlvConstructor(int typeSize, int lengthSize) { 76 if (typeSize < 0 || typeSize > 2 || lengthSize <= 0 || lengthSize > 2) { 77 throw new IllegalArgumentException( 78 "Invalid sizes - typeSize=" + typeSize + ", lengthSize=" + lengthSize); 79 } 80 mTypeSize = typeSize; 81 mLengthSize = lengthSize; 82 } 83 84 /** 85 * Set the byte array to be used to construct the TLV. 86 * 87 * @param array Byte array to be formatted. 88 * @return The constructor to facilitate chaining 89 * {@code ctr.putXXX(..).putXXX(..)}. 90 */ wrap(byte[] array)91 public TlvConstructor wrap(byte[] array) { 92 mArray = array; 93 mArrayLength = array.length; 94 return this; 95 } 96 97 /** 98 * Allocates a new byte array to be used ot construct a TLV. 99 * 100 * @param capacity The size of the byte array to be allocated. 101 * @return The constructor to facilitate chaining 102 * {@code ctr.putXXX(..).putXXX(..)}. 103 */ allocate(int capacity)104 public TlvConstructor allocate(int capacity) { 105 mArray = new byte[capacity]; 106 mArrayLength = capacity; 107 return this; 108 } 109 110 /** 111 * Copies a byte into the TLV with the indicated type. For an LV 112 * formatted structure (i.e. typeLength=0 in {@link TlvConstructor 113 * TlvConstructor(int, int)} ) the type field is ignored. 114 * 115 * @param type The value to be placed into the Type field. 116 * @param b The byte to be inserted into the structure. 117 * @return The constructor to facilitate chaining 118 * {@code ctr.putXXX(..).putXXX(..)}. 119 */ putByte(int type, byte b)120 public TlvConstructor putByte(int type, byte b) { 121 checkLength(1); 122 addHeader(type, 1); 123 mArray[mPosition++] = b; 124 return this; 125 } 126 127 /** 128 * Copies a byte array into the TLV with the indicated type. For an LV 129 * formatted structure (i.e. typeLength=0 in {@link TlvConstructor 130 * TlvConstructor(int, int)} ) the type field is ignored. 131 * 132 * @param type The value to be placed into the Type field. 133 * @param array The array to be copied into the TLV structure. 134 * @param offset Start copying from the array at the specified offset. 135 * @param length Copy the specified number (length) of bytes from the 136 * array. 137 * @return The constructor to facilitate chaining 138 * {@code ctr.putXXX(..).putXXX(..)}. 139 */ putByteArray(int type, byte[] array, int offset, int length)140 public TlvConstructor putByteArray(int type, byte[] array, int offset, int length) { 141 checkLength(length); 142 addHeader(type, length); 143 System.arraycopy(array, offset, mArray, mPosition, length); 144 mPosition += length; 145 return this; 146 } 147 148 /** 149 * Copies a byte array into the TLV with the indicated type. For an LV 150 * formatted structure (i.e. typeLength=0 in {@link TlvConstructor 151 * TlvConstructor(int, int)} ) the type field is ignored. 152 * 153 * @param type The value to be placed into the Type field. 154 * @param array The array to be copied (in full) into the TLV structure. 155 * @return The constructor to facilitate chaining 156 * {@code ctr.putXXX(..).putXXX(..)}. 157 */ putByteArray(int type, byte[] array)158 public TlvConstructor putByteArray(int type, byte[] array) { 159 return putByteArray(type, array, 0, array.length); 160 } 161 162 /** 163 * Places a zero length element (i.e. Length field = 0) into the TLV. 164 * For an LV formatted structure (i.e. typeLength=0 in 165 * {@link TlvConstructor TlvConstructor(int, int)} ) the type field is 166 * ignored. 167 * 168 * @param type The value to be placed into the Type field. 169 * @return The constructor to facilitate chaining 170 * {@code ctr.putXXX(..).putXXX(..)}. 171 */ putZeroLengthElement(int type)172 public TlvConstructor putZeroLengthElement(int type) { 173 checkLength(0); 174 addHeader(type, 0); 175 return this; 176 } 177 178 /** 179 * Copies short into the TLV with the indicated type. For an LV 180 * formatted structure (i.e. typeLength=0 in {@link TlvConstructor 181 * TlvConstructor(int, int)} ) the type field is ignored. 182 * 183 * @param type The value to be placed into the Type field. 184 * @param data The short to be inserted into the structure. 185 * @return The constructor to facilitate chaining 186 * {@code ctr.putXXX(..).putXXX(..)}. 187 */ putShort(int type, short data)188 public TlvConstructor putShort(int type, short data) { 189 checkLength(2); 190 addHeader(type, 2); 191 Memory.pokeShort(mArray, mPosition, data, ByteOrder.BIG_ENDIAN); 192 mPosition += 2; 193 return this; 194 } 195 196 /** 197 * Copies integer into the TLV with the indicated type. For an LV 198 * formatted structure (i.e. typeLength=0 in {@link TlvConstructor 199 * TlvConstructor(int, int)} ) the type field is ignored. 200 * 201 * @param type The value to be placed into the Type field. 202 * @param data The integer to be inserted into the structure. 203 * @return The constructor to facilitate chaining 204 * {@code ctr.putXXX(..).putXXX(..)}. 205 */ putInt(int type, int data)206 public TlvConstructor putInt(int type, int data) { 207 checkLength(4); 208 addHeader(type, 4); 209 Memory.pokeInt(mArray, mPosition, data, ByteOrder.BIG_ENDIAN); 210 mPosition += 4; 211 return this; 212 } 213 214 /** 215 * Copies a String's byte representation into the TLV with the indicated 216 * type. For an LV formatted structure (i.e. typeLength=0 in 217 * {@link TlvConstructor TlvConstructor(int, int)} ) the type field is 218 * ignored. 219 * 220 * @param type The value to be placed into the Type field. 221 * @param data The string whose bytes are to be inserted into the 222 * structure. 223 * @return The constructor to facilitate chaining 224 * {@code ctr.putXXX(..).putXXX(..)}. 225 */ putString(int type, String data)226 public TlvConstructor putString(int type, String data) { 227 return putByteArray(type, data.getBytes(), 0, data.length()); 228 } 229 230 /** 231 * Returns the constructed TLV formatted byte-array. Note that the 232 * returned array is the fully wrapped ( 233 * {@link TlvConstructor#wrap(byte[])}) or allocated ( 234 * {@link TlvConstructor#allocate(int)}) array - which isn't necessarily 235 * the actual size of the formatted data. Use 236 * {@link TlvConstructor#getActualLength()} to obtain the size of the 237 * formatted data. 238 * 239 * @return The byte array containing the TLV formatted structure. 240 */ getArray()241 public byte[] getArray() { 242 return mArray; 243 } 244 245 /** 246 * Returns the size of the TLV formatted portion of the wrapped or 247 * allocated byte array. The array itself is returned with 248 * {@link TlvConstructor#getArray()}. 249 * 250 * @return The size of the TLV formatted portion of the byte array. 251 */ getActualLength()252 public int getActualLength() { 253 return mPosition; 254 } 255 checkLength(int dataLength)256 private void checkLength(int dataLength) { 257 if (mPosition + mTypeSize + mLengthSize + dataLength > mArrayLength) { 258 throw new BufferOverflowException(); 259 } 260 } 261 addHeader(int type, int length)262 private void addHeader(int type, int length) { 263 if (mTypeSize == 1) { 264 mArray[mPosition] = (byte) type; 265 } else if (mTypeSize == 2) { 266 Memory.pokeShort(mArray, mPosition, (short) type, ByteOrder.BIG_ENDIAN); 267 } 268 mPosition += mTypeSize; 269 270 if (mLengthSize == 1) { 271 mArray[mPosition] = (byte) length; 272 } else if (mLengthSize == 2) { 273 Memory.pokeShort(mArray, mPosition, (short) length, ByteOrder.BIG_ENDIAN); 274 } 275 mPosition += mLengthSize; 276 } 277 } 278 279 /** 280 * Utility class used when iterating over a TLV formatted byte-array. Use 281 * {@link TlvIterable} to iterate over array. A {@link TlvElement} 282 * represents each entry in a TLV formatted byte-array. 283 */ 284 public static class TlvElement { 285 /** 286 * The Type (T) field of the current TLV element. Note that for LV 287 * formatted byte-arrays (i.e. TLV whose Type/T size is 0) the value of 288 * this field is undefined. 289 */ 290 public int mType; 291 292 /** 293 * The Length (L) field of the current TLV element. 294 */ 295 public int mLength; 296 297 /** 298 * The Value (V) field - a raw byte array representing the current TLV 299 * element where the entry starts at {@link TlvElement#mOffset}. 300 */ 301 public byte[] mRefArray; 302 303 /** 304 * The offset to be used into {@link TlvElement#mRefArray} to access the 305 * raw data representing the current TLV element. 306 */ 307 public int mOffset; 308 TlvElement(int type, int length, byte[] refArray, int offset)309 private TlvElement(int type, int length, byte[] refArray, int offset) { 310 mType = type; 311 mLength = length; 312 mRefArray = refArray; 313 mOffset = offset; 314 } 315 316 /** 317 * Utility function to return a byte representation of a TLV element of 318 * length 1. Note: an attempt to call this function on a TLV item whose 319 * {@link TlvElement#mLength} is != 1 will result in an exception. 320 * 321 * @return byte representation of current TLV element. 322 */ getByte()323 public byte getByte() { 324 if (mLength != 1) { 325 throw new IllegalArgumentException( 326 "Accesing a byte from a TLV element of length " + mLength); 327 } 328 return mRefArray[mOffset]; 329 } 330 331 /** 332 * Utility function to return a short representation of a TLV element of 333 * length 2. Note: an attempt to call this function on a TLV item whose 334 * {@link TlvElement#mLength} is != 2 will result in an exception. 335 * 336 * @return short representation of current TLV element. 337 */ getShort()338 public short getShort() { 339 if (mLength != 2) { 340 throw new IllegalArgumentException( 341 "Accesing a short from a TLV element of length " + mLength); 342 } 343 return Memory.peekShort(mRefArray, mOffset, ByteOrder.BIG_ENDIAN); 344 } 345 346 /** 347 * Utility function to return an integer representation of a TLV element 348 * of length 4. Note: an attempt to call this function on a TLV item 349 * whose {@link TlvElement#mLength} is != 4 will result in an exception. 350 * 351 * @return integer representation of current TLV element. 352 */ getInt()353 public int getInt() { 354 if (mLength != 4) { 355 throw new IllegalArgumentException( 356 "Accesing an int from a TLV element of length " + mLength); 357 } 358 return Memory.peekInt(mRefArray, mOffset, ByteOrder.BIG_ENDIAN); 359 } 360 361 /** 362 * Utility function to return a String representation of a TLV element. 363 * 364 * @return String repersentation of the current TLV element. 365 */ getString()366 public String getString() { 367 return new String(mRefArray, mOffset, mLength); 368 } 369 } 370 371 /** 372 * Utility class to iterate over a TLV formatted byte-array. 373 */ 374 public static class TlvIterable implements Iterable<TlvElement> { 375 private int mTypeSize; 376 private int mLengthSize; 377 private byte[] mArray; 378 private int mArrayLength; 379 380 /** 381 * Constructs a TlvIterable object - specifying the format of the TLV 382 * (the sizes of the Type and Length fields), and the byte array whose 383 * data is to be parsed. 384 * 385 * @param typeSize Number of bytes used for the Type (T) field. Valid 386 * values are 0 (i.e. indicating the format is LV rather than 387 * TLV), 1, and 2 bytes. 388 * @param lengthSize Number of bytes sued for the Length (L) field. 389 * Values values are 1 or 2 bytes. 390 * @param array The TLV formatted byte-array to parse. 391 * @param length The number of bytes of the array to be used in the 392 * parsing. 393 */ TlvIterable(int typeSize, int lengthSize, byte[] array, int length)394 public TlvIterable(int typeSize, int lengthSize, byte[] array, int length) { 395 if (typeSize < 0 || typeSize > 2 || lengthSize <= 0 || lengthSize > 2) { 396 throw new IllegalArgumentException( 397 "Invalid sizes - typeSize=" + typeSize + ", lengthSize=" + lengthSize); 398 } 399 mTypeSize = typeSize; 400 mLengthSize = lengthSize; 401 mArray = array; 402 mArrayLength = length; 403 } 404 405 /** 406 * Prints out a parsed representation of the TLV-formatted byte array. 407 * Whenever possible bytes, shorts, and integer are printed out (for 408 * fields whose length is 1, 2, or 4 respectively). 409 */ 410 @Override toString()411 public String toString() { 412 StringBuilder builder = new StringBuilder(); 413 414 builder.append("["); 415 boolean first = true; 416 for (TlvElement tlv : this) { 417 if (!first) { 418 builder.append(","); 419 } 420 first = false; 421 builder.append(" ("); 422 if (mTypeSize != 0) { 423 builder.append("T=" + tlv.mType + ","); 424 } 425 builder.append("L=" + tlv.mLength + ") "); 426 if (tlv.mLength == 0) { 427 builder.append("<null>"); 428 } else if (tlv.mLength == 1) { 429 builder.append(tlv.getByte()); 430 } else if (tlv.mLength == 2) { 431 builder.append(tlv.getShort()); 432 } else if (tlv.mLength == 4) { 433 builder.append(tlv.getInt()); 434 } else { 435 builder.append("<bytes>"); 436 } 437 if (tlv.mLength != 0) { 438 builder.append(" (S='" + tlv.getString() + "')"); 439 } 440 } 441 builder.append("]"); 442 443 return builder.toString(); 444 } 445 446 /** 447 * Returns an iterator to step through a TLV formatted byte-array. The 448 * individual elements returned by the iterator are {@link TlvElement}. 449 */ 450 @Override iterator()451 public Iterator<TlvElement> iterator() { 452 return new Iterator<TlvElement>() { 453 private int mOffset = 0; 454 455 @Override 456 public boolean hasNext() { 457 return mOffset < mArrayLength; 458 } 459 460 @Override 461 public TlvElement next() { 462 int type = 0; 463 if (mTypeSize == 1) { 464 type = mArray[mOffset]; 465 } else if (mTypeSize == 2) { 466 type = Memory.peekShort(mArray, mOffset, ByteOrder.BIG_ENDIAN); 467 } 468 mOffset += mTypeSize; 469 470 int length = 0; 471 if (mLengthSize == 1) { 472 length = mArray[mOffset]; 473 } else if (mLengthSize == 2) { 474 length = Memory.peekShort(mArray, mOffset, ByteOrder.BIG_ENDIAN); 475 } 476 mOffset += mLengthSize; 477 478 TlvElement tlv = new TlvElement(type, length, mArray, mOffset); 479 mOffset += length; 480 return tlv; 481 } 482 483 @Override 484 public void remove() { 485 throw new UnsupportedOperationException(); 486 } 487 }; 488 } 489 } 490 } 491