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.aware;
18 
19 import android.annotation.Nullable;
20 
21 import libcore.io.Memory;
22 
23 import java.nio.BufferOverflowException;
24 import java.nio.ByteOrder;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.NoSuchElementException;
30 
31 /**
32  * Utility class to construct and parse byte arrays using the TLV format -
33  * Type/Length/Value format. The utilities accept a configuration of the size of
34  * the Type field and the Length field. A Type field size of 0 is allowed -
35  * allowing usage for LV (no T) array formats.
36  *
37  * @hide
38  */
39 public class TlvBufferUtils {
TlvBufferUtils()40     private TlvBufferUtils() {
41         // no reason to ever create this class
42     }
43 
44     /**
45      * Utility class to construct byte arrays using the TLV format -
46      * Type/Length/Value.
47      * <p>
48      * A constructor is created specifying the size of the Type (T) and Length
49      * (L) fields. A specification of zero size T field is allowed - resulting
50      * in LV type format.
51      * <p>
52      * The byte array is either provided (using
53      * {@link TlvConstructor#wrap(byte[])}) or allocated (using
54      * {@link TlvConstructor#allocate(int)}).
55      * <p>
56      * Values are added to the structure using the {@code TlvConstructor.put*()}
57      * methods.
58      * <p>
59      * The final byte array is obtained using {@link TlvConstructor#getArray()}.
60      */
61     public static class TlvConstructor {
62         private int mTypeSize;
63         private int mLengthSize;
64         private ByteOrder mByteOrder = ByteOrder.BIG_ENDIAN;
65 
66         private byte[] mArray;
67         private int mArrayLength;
68         private int mPosition;
69 
70         /**
71          * Define a TLV constructor with the specified size of the Type (T) and
72          * Length (L) fields.
73          *
74          * @param typeSize Number of bytes used for the Type (T) field. Values
75          *            of 0, 1, or 2 bytes are allowed. A specification of 0
76          *            bytes implies that the field being constructed has the LV
77          *            format rather than the TLV format.
78          * @param lengthSize Number of bytes used for the Length (L) field.
79          *            Values of 1 or 2 bytes are allowed.
80          */
TlvConstructor(int typeSize, int lengthSize)81         public TlvConstructor(int typeSize, int lengthSize) {
82             if (typeSize < 0 || typeSize > 2 || lengthSize <= 0 || lengthSize > 2) {
83                 throw new IllegalArgumentException(
84                         "Invalid sizes - typeSize=" + typeSize + ", lengthSize=" + lengthSize);
85             }
86             mTypeSize = typeSize;
87             mLengthSize = lengthSize;
88             mPosition = 0;
89         }
90 
91         /**
92          * Configure the TLV constructor to use a particular byte order. Should be
93          * {@link ByteOrder#BIG_ENDIAN} (the default at construction) or
94          * {@link ByteOrder#LITTLE_ENDIAN}.
95          *
96          * @return The constructor to facilitate chaining
97          *         {@code ctr.putXXX(..).putXXX(..)}.
98          */
setByteOrder(ByteOrder byteOrder)99         public TlvConstructor setByteOrder(ByteOrder byteOrder) {
100             mByteOrder = byteOrder;
101             return this;
102         }
103 
104         /**
105          * Set the byte array to be used to construct the TLV.
106          *
107          * @param array Byte array to be formatted.
108          * @return The constructor to facilitate chaining
109          *         {@code ctr.putXXX(..).putXXX(..)}.
110          */
wrap(@ullable byte[] array)111         public TlvConstructor wrap(@Nullable byte[] array) {
112             mArray = array;
113             mArrayLength = (array == null) ? 0 : array.length;
114             mPosition = 0;
115             return this;
116         }
117 
118         /**
119          * Allocates a new byte array to be used ot construct a TLV.
120          *
121          * @param capacity The size of the byte array to be allocated.
122          * @return The constructor to facilitate chaining
123          *         {@code ctr.putXXX(..).putXXX(..)}.
124          */
allocate(int capacity)125         public TlvConstructor allocate(int capacity) {
126             mArray = new byte[capacity];
127             mArrayLength = capacity;
128             mPosition = 0;
129             return this;
130         }
131 
132         /**
133          * Creates a TLV array (of the previously specified Type and Length sizes) from the input
134          * list. Allocates an array matching the contents (and required Type and Length
135          * fields), copies the contents, and set the Length fields. The Type field is set to 0.
136          *
137          * @param list A list of fields to be added to the TLV buffer.
138          * @return The constructor of the TLV.
139          */
allocateAndPut(@ullable List<byte[]> list)140         public TlvConstructor allocateAndPut(@Nullable List<byte[]> list) {
141             if (list != null) {
142                 int size = 0;
143                 for (byte[] field : list) {
144                     size += mTypeSize + mLengthSize;
145                     if (field != null) {
146                         size += field.length;
147                     }
148                 }
149                 allocate(size);
150                 for (byte[] field : list) {
151                     putByteArray(0, field);
152                 }
153             }
154             return this;
155         }
156 
157         /**
158          * Copies a byte into the TLV with the indicated type. For an LV
159          * formatted structure (i.e. typeLength=0 in {@link TlvConstructor
160          * TlvConstructor(int, int)} ) the type field is ignored.
161          *
162          * @param type The value to be placed into the Type field.
163          * @param b The byte to be inserted into the structure.
164          * @return The constructor to facilitate chaining
165          *         {@code ctr.putXXX(..).putXXX(..)}.
166          */
putByte(int type, byte b)167         public TlvConstructor putByte(int type, byte b) {
168             checkLength(1);
169             addHeader(type, 1);
170             mArray[mPosition++] = b;
171             return this;
172         }
173 
174         /**
175          * Copies a raw byte into the TLV buffer - without a type or a length.
176          *
177          * @param b The byte to be inserted into the structure.
178          * @return The constructor to facilitate chaining {@code cts.putXXX(..).putXXX(..)}.
179          */
putRawByte(byte b)180         public TlvConstructor putRawByte(byte b) {
181             checkRawLength(1);
182             mArray[mPosition++] = b;
183             return this;
184         }
185 
186         /**
187          * Copies a byte array into the TLV with the indicated type. For an LV
188          * formatted structure (i.e. typeLength=0 in {@link TlvConstructor
189          * TlvConstructor(int, int)} ) the type field is ignored.
190          *
191          * @param type The value to be placed into the Type field.
192          * @param array The array to be copied into the TLV structure.
193          * @param offset Start copying from the array at the specified offset.
194          * @param length Copy the specified number (length) of bytes from the
195          *            array.
196          * @return The constructor to facilitate chaining
197          *         {@code ctr.putXXX(..).putXXX(..)}.
198          */
putByteArray(int type, @Nullable byte[] array, int offset, int length)199         public TlvConstructor putByteArray(int type, @Nullable byte[] array, int offset,
200                 int length) {
201             checkLength(length);
202             addHeader(type, length);
203             if (length != 0) {
204                 System.arraycopy(array, offset, mArray, mPosition, length);
205             }
206             mPosition += length;
207             return this;
208         }
209 
210         /**
211          * Copies a byte array into the TLV with the indicated type. For an LV
212          * formatted structure (i.e. typeLength=0 in {@link TlvConstructor
213          * TlvConstructor(int, int)} ) the type field is ignored.
214          *
215          * @param type The value to be placed into the Type field.
216          * @param array The array to be copied (in full) into the TLV structure.
217          * @return The constructor to facilitate chaining
218          *         {@code ctr.putXXX(..).putXXX(..)}.
219          */
putByteArray(int type, @Nullable byte[] array)220         public TlvConstructor putByteArray(int type, @Nullable byte[] array) {
221             return putByteArray(type, array, 0, (array == null) ? 0 : array.length);
222         }
223 
224         /**
225          * Copies a byte array into the TLV - without a type or a length.
226          *
227          * @param array The array to be copied (in full) into the TLV structure.
228          * @return The constructor to facilitate chaining
229          *         {@code ctr.putXXX(..).putXXX(..)}.
230          */
putRawByteArray(@ullable byte[] array)231         public TlvConstructor putRawByteArray(@Nullable byte[] array) {
232             if (array == null) return this;
233 
234             checkRawLength(array.length);
235             System.arraycopy(array, 0, mArray, mPosition, array.length);
236             mPosition += array.length;
237             return this;
238         }
239 
240         /**
241          * Places a zero length element (i.e. Length field = 0) into the TLV.
242          * For an LV formatted structure (i.e. typeLength=0 in
243          * {@link TlvConstructor TlvConstructor(int, int)} ) the type field is
244          * ignored.
245          *
246          * @param type The value to be placed into the Type field.
247          * @return The constructor to facilitate chaining
248          *         {@code ctr.putXXX(..).putXXX(..)}.
249          */
putZeroLengthElement(int type)250         public TlvConstructor putZeroLengthElement(int type) {
251             checkLength(0);
252             addHeader(type, 0);
253             return this;
254         }
255 
256         /**
257          * Copies short into the TLV with the indicated type. For an LV
258          * formatted structure (i.e. typeLength=0 in {@link TlvConstructor
259          * TlvConstructor(int, int)} ) the type field is ignored.
260          *
261          * @param type The value to be placed into the Type field.
262          * @param data The short to be inserted into the structure.
263          * @return The constructor to facilitate chaining
264          *         {@code ctr.putXXX(..).putXXX(..)}.
265          */
putShort(int type, short data)266         public TlvConstructor putShort(int type, short data) {
267             checkLength(2);
268             addHeader(type, 2);
269             Memory.pokeShort(mArray, mPosition, data, mByteOrder);
270             mPosition += 2;
271             return this;
272         }
273 
274         /**
275          * Copies integer into the TLV with the indicated type. For an LV
276          * formatted structure (i.e. typeLength=0 in {@link TlvConstructor
277          * TlvConstructor(int, int)} ) the type field is ignored.
278          *
279          * @param type The value to be placed into the Type field.
280          * @param data The integer to be inserted into the structure.
281          * @return The constructor to facilitate chaining
282          *         {@code ctr.putXXX(..).putXXX(..)}.
283          */
putInt(int type, int data)284         public TlvConstructor putInt(int type, int data) {
285             checkLength(4);
286             addHeader(type, 4);
287             Memory.pokeInt(mArray, mPosition, data, mByteOrder);
288             mPosition += 4;
289             return this;
290         }
291 
292         /**
293          * Copies a String's byte representation into the TLV with the indicated
294          * type. For an LV formatted structure (i.e. typeLength=0 in
295          * {@link TlvConstructor TlvConstructor(int, int)} ) the type field is
296          * ignored.
297          *
298          * @param type The value to be placed into the Type field.
299          * @param data The string whose bytes are to be inserted into the
300          *            structure.
301          * @return The constructor to facilitate chaining
302          *         {@code ctr.putXXX(..).putXXX(..)}.
303          */
putString(int type, @Nullable String data)304         public TlvConstructor putString(int type, @Nullable String data) {
305             byte[] bytes = null;
306             int length = 0;
307             if (data != null) {
308                 bytes = data.getBytes();
309                 length = bytes.length;
310             }
311             return putByteArray(type, bytes, 0, length);
312         }
313 
314         /**
315          * Returns the constructed TLV formatted byte-array. This array is a copy of the wrapped
316          * or allocated array - truncated to just the significant bytes - i.e. those written into
317          * the (T)LV.
318          *
319          * @return The byte array containing the TLV formatted structure.
320          */
getArray()321         public byte[] getArray() {
322             return Arrays.copyOf(mArray, getActualLength());
323         }
324 
325         /**
326          * Returns the size of the TLV formatted portion of the wrapped or
327          * allocated byte array. The array itself is returned with
328          * {@link TlvConstructor#getArray()}.
329          *
330          * @return The size of the TLV formatted portion of the byte array.
331          */
getActualLength()332         private int getActualLength() {
333             return mPosition;
334         }
335 
checkLength(int dataLength)336         private void checkLength(int dataLength) {
337             if (mPosition + mTypeSize + mLengthSize + dataLength > mArrayLength) {
338                 throw new BufferOverflowException();
339             }
340         }
341 
checkRawLength(int dataLength)342         private void checkRawLength(int dataLength) {
343             if (mPosition + dataLength > mArrayLength) {
344                 throw new BufferOverflowException();
345             }
346         }
347 
addHeader(int type, int length)348         private void addHeader(int type, int length) {
349             if (mTypeSize == 1) {
350                 mArray[mPosition] = (byte) type;
351             } else if (mTypeSize == 2) {
352                 Memory.pokeShort(mArray, mPosition, (short) type, mByteOrder);
353             }
354             mPosition += mTypeSize;
355 
356             if (mLengthSize == 1) {
357                 mArray[mPosition] = (byte) length;
358             } else if (mLengthSize == 2) {
359                 Memory.pokeShort(mArray, mPosition, (short) length, mByteOrder);
360             }
361             mPosition += mLengthSize;
362         }
363     }
364 
365     /**
366      * Utility class used when iterating over a TLV formatted byte-array. Use
367      * {@link TlvIterable} to iterate over array. A {@link TlvElement}
368      * represents each entry in a TLV formatted byte-array.
369      */
370     public static class TlvElement {
371         /**
372          * The Type (T) field of the current TLV element. Note that for LV
373          * formatted byte-arrays (i.e. TLV whose Type/T size is 0) the value of
374          * this field is undefined.
375          */
376         public int type;
377 
378         /**
379          * The Length (L) field of the current TLV element.
380          */
381         public int length;
382 
383         /**
384          * Control of the endianess of the TLV element - true for big-endian, false for little-
385          * endian.
386          */
387         public ByteOrder byteOrder = ByteOrder.BIG_ENDIAN;
388 
389         /**
390          * The Value (V) field - a raw byte array representing the current TLV
391          * element where the entry starts at {@link TlvElement#offset}.
392          */
393         private byte[] mRefArray;
394 
395         /**
396          * The offset to be used into {@link TlvElement#mRefArray} to access the
397          * raw data representing the current TLV element.
398          */
399         public int offset;
400 
TlvElement(int type, int length, @Nullable byte[] refArray, int offset)401         private TlvElement(int type, int length, @Nullable byte[] refArray, int offset) {
402             this.type = type;
403             this.length = length;
404             mRefArray = refArray;
405             this.offset = offset;
406 
407             if (offset + length > refArray.length) {
408                 throw new BufferOverflowException();
409             }
410         }
411 
412         /**
413          * Return the raw byte array of the Value (V) field.
414          *
415          * @return The Value (V) field as a byte array.
416          */
getRawData()417         public byte[] getRawData() {
418             return Arrays.copyOfRange(mRefArray, offset, offset + length);
419         }
420 
421         /**
422          * Utility function to return a byte representation of a TLV element of
423          * length 1. Note: an attempt to call this function on a TLV item whose
424          * {@link TlvElement#length} is != 1 will result in an exception.
425          *
426          * @return byte representation of current TLV element.
427          */
getByte()428         public byte getByte() {
429             if (length != 1) {
430                 throw new IllegalArgumentException(
431                         "Accesing a byte from a TLV element of length " + length);
432             }
433             return mRefArray[offset];
434         }
435 
436         /**
437          * Utility function to return a short representation of a TLV element of
438          * length 2. Note: an attempt to call this function on a TLV item whose
439          * {@link TlvElement#length} is != 2 will result in an exception.
440          *
441          * @return short representation of current TLV element.
442          */
getShort()443         public short getShort() {
444             if (length != 2) {
445                 throw new IllegalArgumentException(
446                         "Accesing a short from a TLV element of length " + length);
447             }
448             return Memory.peekShort(mRefArray, offset, byteOrder);
449         }
450 
451         /**
452          * Utility function to return an integer representation of a TLV element
453          * of length 4. Note: an attempt to call this function on a TLV item
454          * whose {@link TlvElement#length} is != 4 will result in an exception.
455          *
456          * @return integer representation of current TLV element.
457          */
getInt()458         public int getInt() {
459             if (length != 4) {
460                 throw new IllegalArgumentException(
461                         "Accesing an int from a TLV element of length " + length);
462             }
463             return Memory.peekInt(mRefArray, offset, byteOrder);
464         }
465 
466         /**
467          * Utility function to return a String representation of a TLV element.
468          *
469          * @return String repersentation of the current TLV element.
470          */
getString()471         public String getString() {
472             return new String(mRefArray, offset, length);
473         }
474     }
475 
476     /**
477      * Utility class to iterate over a TLV formatted byte-array.
478      */
479     public static class TlvIterable implements Iterable<TlvElement> {
480         private int mTypeSize;
481         private int mLengthSize;
482         private ByteOrder mByteOrder = ByteOrder.BIG_ENDIAN;
483         private byte[] mArray;
484         private int mArrayLength;
485 
486         /**
487          * Constructs a TlvIterable object - specifying the format of the TLV
488          * (the sizes of the Type and Length fields), and the byte array whose
489          * data is to be parsed.
490          *
491          * @param typeSize Number of bytes used for the Type (T) field. Valid
492          *            values are 0 (i.e. indicating the format is LV rather than
493          *            TLV), 1, and 2 bytes.
494          * @param lengthSize Number of bytes used for the Length (L) field.
495          *            Values values are 1 or 2 bytes.
496          * @param array The TLV formatted byte-array to parse.
497          */
TlvIterable(int typeSize, int lengthSize, @Nullable byte[] array)498         public TlvIterable(int typeSize, int lengthSize, @Nullable byte[] array) {
499             if (typeSize < 0 || typeSize > 2 || lengthSize <= 0 || lengthSize > 2) {
500                 throw new IllegalArgumentException(
501                         "Invalid sizes - typeSize=" + typeSize + ", lengthSize=" + lengthSize);
502             }
503             mTypeSize = typeSize;
504             mLengthSize = lengthSize;
505             mArray = array;
506             mArrayLength = (array == null) ? 0 : array.length;
507         }
508 
509         /**
510          * Configure the TLV iterator to use little-endian byte ordering.
511          */
setByteOrder(ByteOrder byteOrder)512         public void setByteOrder(ByteOrder byteOrder) {
513             mByteOrder = byteOrder;
514         }
515 
516         /**
517          * Prints out a parsed representation of the TLV-formatted byte array.
518          * Whenever possible bytes, shorts, and integer are printed out (for
519          * fields whose length is 1, 2, or 4 respectively).
520          */
521         @Override
toString()522         public String toString() {
523             StringBuilder builder = new StringBuilder();
524 
525             builder.append("[");
526             boolean first = true;
527             for (TlvElement tlv : this) {
528                 if (!first) {
529                     builder.append(",");
530                 }
531                 first = false;
532                 builder.append(" (");
533                 if (mTypeSize != 0) {
534                     builder.append("T=" + tlv.type + ",");
535                 }
536                 builder.append("L=" + tlv.length + ") ");
537                 if (tlv.length == 0) {
538                     builder.append("<null>");
539                 } else if (tlv.length == 1) {
540                     builder.append(tlv.getByte());
541                 } else if (tlv.length == 2) {
542                     builder.append(tlv.getShort());
543                 } else if (tlv.length == 4) {
544                     builder.append(tlv.getInt());
545                 } else {
546                     builder.append("<bytes>");
547                 }
548                 if (tlv.length != 0) {
549                     builder.append(" (S='" + tlv.getString() + "')");
550                 }
551             }
552             builder.append("]");
553 
554             return builder.toString();
555         }
556 
557         /**
558          * Returns a List with the raw contents (no types) of the iterator.
559          */
toList()560         public List<byte[]> toList() {
561             List<byte[]> list = new ArrayList<>();
562             for (TlvElement tlv : this) {
563                 list.add(Arrays.copyOfRange(tlv.mRefArray, tlv.offset, tlv.offset + tlv.length));
564             }
565 
566             return list;
567         }
568 
569         /**
570          * Returns an iterator to step through a TLV formatted byte-array. The
571          * individual elements returned by the iterator are {@link TlvElement}.
572          */
573         @Override
iterator()574         public Iterator<TlvElement> iterator() {
575             return new Iterator<TlvElement>() {
576                 private int mOffset = 0;
577 
578                 @Override
579                 public boolean hasNext() {
580                     return mOffset < mArrayLength;
581                 }
582 
583                 @Override
584                 public TlvElement next() {
585                     if (!hasNext()) {
586                         throw new NoSuchElementException();
587                     }
588 
589                     int type = 0;
590                     if (mTypeSize == 1) {
591                         type = mArray[mOffset];
592                     } else if (mTypeSize == 2) {
593                         type = Memory.peekShort(mArray, mOffset, mByteOrder);
594                     }
595                     mOffset += mTypeSize;
596 
597                     int length = 0;
598                     if (mLengthSize == 1) {
599                         length = mArray[mOffset];
600                     } else if (mLengthSize == 2) {
601                         length = Memory.peekShort(mArray, mOffset, mByteOrder);
602                     }
603                     mOffset += mLengthSize;
604 
605                     TlvElement tlv = new TlvElement(type, length, mArray, mOffset);
606                     tlv.byteOrder = mByteOrder;
607                     mOffset += length;
608                     return tlv;
609                 }
610 
611                 @Override
612                 public void remove() {
613                     throw new UnsupportedOperationException();
614                 }
615             };
616         }
617     }
618 
619     /**
620      * Validates that a (T)LV array is constructed correctly. I.e. that its specified Length
621      * fields correctly fill the specified length (and do not overshoot). Uses big-endian
622      * byte ordering.
623      *
624      * @param array The (T)LV array to verify.
625      * @param typeSize The size (in bytes) of the type field. Valid values are 0, 1, or 2.
626      * @param lengthSize The size (in bytes) of the length field. Valid values are 1 or 2.
627      * @return A boolean indicating whether the array is valid (true) or invalid (false).
628      */
isValid(@ullable byte[] array, int typeSize, int lengthSize)629     public static boolean isValid(@Nullable byte[] array, int typeSize, int lengthSize) {
630         return isValidEndian(array, typeSize, lengthSize, ByteOrder.BIG_ENDIAN);
631     }
632 
633     /**
634      * Validates that a (T)LV array is constructed correctly. I.e. that its specified Length
635      * fields correctly fill the specified length (and do not overshoot).
636      *
637      * @param array The (T)LV array to verify.
638      * @param typeSize The size (in bytes) of the type field. Valid values are 0, 1, or 2.
639      * @param lengthSize The size (in bytes) of the length field. Valid values are 1 or 2.
640      * @param byteOrder The endianness of the byte array: {@link ByteOrder#BIG_ENDIAN} or
641      *                  {@link ByteOrder#LITTLE_ENDIAN}.
642      * @return A boolean indicating whether the array is valid (true) or invalid (false).
643      */
isValidEndian(@ullable byte[] array, int typeSize, int lengthSize, ByteOrder byteOrder)644     public static boolean isValidEndian(@Nullable byte[] array, int typeSize, int lengthSize,
645             ByteOrder byteOrder) {
646         if (typeSize < 0 || typeSize > 2) {
647             throw new IllegalArgumentException(
648                     "Invalid arguments - typeSize must be 0, 1, or 2: typeSize=" + typeSize);
649         }
650         if (lengthSize <= 0 || lengthSize > 2) {
651             throw new IllegalArgumentException(
652                     "Invalid arguments - lengthSize must be 1 or 2: lengthSize=" + lengthSize);
653         }
654         if (array == null) {
655             return true;
656         }
657 
658         int nextTlvIndex = 0;
659         while (nextTlvIndex + typeSize + lengthSize <= array.length) {
660             nextTlvIndex += typeSize;
661             if (lengthSize == 1) {
662                 nextTlvIndex += lengthSize + array[nextTlvIndex];
663             } else {
664                 nextTlvIndex += lengthSize + Memory.peekShort(array, nextTlvIndex, byteOrder);
665             }
666         }
667 
668         return nextTlvIndex == array.length;
669     }
670 }
671