From 009b1fcb85ab8f8eb4ab5f3e125127b449df77cb Mon Sep 17 00:00:00 2001 From: Etan Cohen Date: Mon, 18 Jul 2016 13:43:17 -0700 Subject: [PATCH] [NAN] Add LV utilities (TLV with T size of 0). NAN uses LV (not TLV). Expose an LV variant of TLV to make API cleaner. Bug: 30096512 Change-Id: I74b4c514d50f1df5750b521bd68a5e2d832d92c3 --- .../android/net/wifi/nan/LvBufferUtils.java | 340 ++++++++++++++++++ .../android/net/wifi/nan/PublishConfig.java | 12 +- .../android/net/wifi/nan/SubscribeConfig.java | 12 +- 3 files changed, 352 insertions(+), 12 deletions(-) create mode 100644 wifi/java/android/net/wifi/nan/LvBufferUtils.java diff --git a/wifi/java/android/net/wifi/nan/LvBufferUtils.java b/wifi/java/android/net/wifi/nan/LvBufferUtils.java new file mode 100644 index 0000000000000..eb5607052ceb3 --- /dev/null +++ b/wifi/java/android/net/wifi/nan/LvBufferUtils.java @@ -0,0 +1,340 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi.nan; + +import android.annotation.Nullable; + +import libcore.io.Memory; + +import java.nio.ByteOrder; +import java.util.Iterator; + +/** + * Utility class to construct and parse byte arrays using the LV format - + * Length/Value format. The utilities accept a configuration of the size of + * the Length field. + * + * @hide PROPOSED_NAN_API + */ +public class LvBufferUtils { + private LvBufferUtils() { + // no reason to ever create this class + } + + /** + * Utility class to construct byte arrays using the LV format - Length/Value. + *

+ * A constructor is created specifying the size of the Length (L) field. + *

+ * The byte array is either provided (using + * {@link LvBufferUtils.LvConstructor#wrap(byte[])}) or allocated (using + * {@link LvBufferUtils.LvConstructor#allocate(int)}). + *

+ * Values are added to the structure using the {@code LvConstructor.put*()} + * methods. + *

+ * The final byte array is obtained using {@link LvBufferUtils.LvConstructor#getArray()}. + */ + public static class LvConstructor { + private TlvBufferUtils.TlvConstructor mTlvImpl; + + /** + * Define a LV constructor with the specified size of the Length (L) field. + * + * @param lengthSize Number of bytes used for the Length (L) field. + * Values of 1 or 2 bytes are allowed. + */ + public LvConstructor(int lengthSize) { + mTlvImpl = new TlvBufferUtils.TlvConstructor(0, lengthSize); + } + + /** + * Set the byte array to be used to construct the LV. + * + * @param array Byte array to be formatted. + * @return The constructor to facilitate chaining + * {@code ctr.putXXX(..).putXXX(..)}. + */ + public LvBufferUtils.LvConstructor wrap(@Nullable byte[] array) { + mTlvImpl.wrap(array); + return this; + } + + /** + * Allocates a new byte array to be used ot construct a LV. + * + * @param capacity The size of the byte array to be allocated. + * @return The constructor to facilitate chaining + * {@code ctr.putXXX(..).putXXX(..)}. + */ + public LvBufferUtils.LvConstructor allocate(int capacity) { + mTlvImpl.allocate(capacity); + return this; + } + + /** + * Copies a byte into the LV array. + * + * @param b The byte to be inserted into the structure. + * @return The constructor to facilitate chaining + * {@code ctr.putXXX(..).putXXX(..)}. + */ + public LvBufferUtils.LvConstructor putByte(byte b) { + mTlvImpl.putByte(0, b); + return this; + } + + /** + * Copies a byte array into the LV. + * + * @param array The array to be copied into the LV structure. + * @param offset Start copying from the array at the specified offset. + * @param length Copy the specified number (length) of bytes from the + * array. + * @return The constructor to facilitate chaining + * {@code ctr.putXXX(..).putXXX(..)}. + */ + public LvBufferUtils.LvConstructor putByteArray(@Nullable byte[] array, int offset, + int length) { + mTlvImpl.putByteArray(0, array, offset, length); + return this; + } + + /** + * Copies a byte array into the LV. + * + * @param array The array to be copied (in full) into the LV structure. + * @return The constructor to facilitate chaining + * {@code ctr.putXXX(..).putXXX(..)}. + */ + public LvBufferUtils.LvConstructor putByteArray(int type, @Nullable byte[] array) { + return putByteArray(array, 0, (array == null) ? 0 : array.length); + } + + /** + * Places a zero length element (i.e. Length field = 0) into the LV. + * + * @return The constructor to facilitate chaining + * {@code ctr.putXXX(..).putXXX(..)}. + */ + public LvBufferUtils.LvConstructor putZeroLengthElement() { + mTlvImpl.putZeroLengthElement(0); + return this; + } + + /** + * Copies short into the LV. + * + * @param data The short to be inserted into the structure. + * @return The constructor to facilitate chaining + * {@code ctr.putXXX(..).putXXX(..)}. + */ + public LvBufferUtils.LvConstructor putShort(short data) { + mTlvImpl.putShort(0, data); + return this; + } + + /** + * Copies integer into the LV. + * + * @param data The integer to be inserted into the structure. + * @return The constructor to facilitate chaining + * {@code ctr.putXXX(..).putXXX(..)}. + */ + public LvBufferUtils.LvConstructor putInt(int data) { + mTlvImpl.putInt(0, data); + return this; + } + + /** + * Copies a String's byte representation into the LV. + * + * @param data The string whose bytes are to be inserted into the + * structure. + * @return The constructor to facilitate chaining + * {@code ctr.putXXX(..).putXXX(..)}. + */ + public LvBufferUtils.LvConstructor putString(@Nullable String data) { + mTlvImpl.putString(0, data); + return this; + } + + /** + * Returns the constructed LV formatted byte-array. This array is a copy of the wrapped + * or allocated array - truncated to just the significant bytes - i.e. those written into + * the LV. + * + * @return The byte array containing the LV formatted structure. + */ + public byte[] getArray() { + return mTlvImpl.getArray(); + } + } + + /** + * Utility class used when iterating over an LV formatted byte-array. Use + * {@link LvBufferUtils.LvIterable} to iterate over array. A {@link LvBufferUtils.LvElement} + * represents each entry in a LV formatted byte-array. + */ + public static class LvElement { + /** + * The Length (L) field of the current LV element. + */ + public int length; + + /** + * The Value (V) field - a raw byte array representing the current LV + * element where the entry starts at {@link LvBufferUtils.LvElement#offset}. + */ + public byte[] refArray; + + /** + * The offset to be used into {@link LvBufferUtils.LvElement#refArray} to access the + * raw data representing the current LV element. + */ + public int offset; + + private LvElement(int length, @Nullable byte[] refArray, int offset) { + this.length = length; + this.refArray = refArray; + this.offset = offset; + } + + /** + * Utility function to return a byte representation of a LV element of + * length 1. Note: an attempt to call this function on a LV item whose + * {@link LvBufferUtils.LvElement#length} is != 1 will result in an exception. + * + * @return byte representation of current LV element. + */ + public byte getByte() { + if (length != 1) { + throw new IllegalArgumentException( + "Accesing a byte from a LV element of length " + length); + } + return refArray[offset]; + } + + /** + * Utility function to return a short representation of a LV element of + * length 2. Note: an attempt to call this function on a LV item whose + * {@link LvBufferUtils.LvElement#length} is != 2 will result in an exception. + * + * @return short representation of current LV element. + */ + public short getShort() { + if (length != 2) { + throw new IllegalArgumentException( + "Accesing a short from a LV element of length " + length); + } + return Memory.peekShort(refArray, offset, ByteOrder.BIG_ENDIAN); + } + + /** + * Utility function to return an integer representation of a LV element + * of length 4. Note: an attempt to call this function on a LV item + * whose {@link LvBufferUtils.LvElement#length} is != 4 will result in an exception. + * + * @return integer representation of current LV element. + */ + public int getInt() { + if (length != 4) { + throw new IllegalArgumentException( + "Accesing an int from a LV element of length " + length); + } + return Memory.peekInt(refArray, offset, ByteOrder.BIG_ENDIAN); + } + + /** + * Utility function to return a String representation of a LV element. + * + * @return String representation of the current LV element. + */ + public String getString() { + return new String(refArray, offset, length); + } + } + + /** + * Utility class to iterate over a LV formatted byte-array. + */ + public static class LvIterable implements Iterable { + private final TlvBufferUtils.TlvIterable mTlvIterable; + + /** + * Constructs an LvIterable object - specifying the format of the LV + * (the size of the Length field), and the byte array whose data is to be parsed. + * + * @param lengthSize Number of bytes sued for the Length (L) field. + * Values values are 1 or 2 bytes. + * @param array The LV formatted byte-array to parse. + */ + public LvIterable(int lengthSize, @Nullable byte[] array) { + mTlvIterable = new TlvBufferUtils.TlvIterable(0, lengthSize, array); + } + + /** + * Prints out a parsed representation of the LV-formatted byte array. + * Whenever possible bytes, shorts, and integer are printed out (for + * fields whose length is 1, 2, or 4 respectively). + */ + @Override + public String toString() { + return mTlvIterable.toString(); + } + + /** + * Returns an iterator to step through a LV formatted byte-array. The + * individual elements returned by the iterator are {@link LvBufferUtils.LvElement}. + */ + @Override + public Iterator iterator() { + return new Iterator() { + private Iterator mTlvIterator = mTlvIterable.iterator(); + + @Override + public boolean hasNext() { + return mTlvIterator.hasNext(); + } + + @Override + public LvBufferUtils.LvElement next() { + TlvBufferUtils.TlvElement tlvE = mTlvIterator.next(); + + return new LvElement(tlvE.length, tlvE.refArray, tlvE.offset); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + } + + /** + * Validates that a LV array is constructed correctly. I.e. that its specified Length + * fields correctly fill the specified length (and do not overshoot). + * + * @param array The LV array to verify. + * @param lengthSize The size (in bytes) of the length field. Valid values are 1 or 2. + * @return A boolean indicating whether the array is valid (true) or invalid (false). + */ + public static boolean isValid(@Nullable byte[] array, int lengthSize) { + return TlvBufferUtils.isValid(array, 0, lengthSize); + } +} diff --git a/wifi/java/android/net/wifi/nan/PublishConfig.java b/wifi/java/android/net/wifi/nan/PublishConfig.java index 3bc5251f8cdf0..3fd756e99ce8a 100644 --- a/wifi/java/android/net/wifi/nan/PublishConfig.java +++ b/wifi/java/android/net/wifi/nan/PublishConfig.java @@ -116,8 +116,8 @@ public class PublishConfig implements Parcelable { public String toString() { return "PublishConfig [mServiceName='" + mServiceName + ", mServiceSpecificInfo='" + ( (mServiceSpecificInfo == null) ? "null" : HexEncoding.encode(mServiceSpecificInfo)) - + ", mTxFilter=" + (new TlvBufferUtils.TlvIterable(0, 1, mTxFilter)).toString() - + ", mRxFilter=" + (new TlvBufferUtils.TlvIterable(0, 1, mRxFilter)).toString() + + ", mTxFilter=" + (new LvBufferUtils.LvIterable(1, mTxFilter)).toString() + + ", mRxFilter=" + (new LvBufferUtils.LvIterable(1, mRxFilter)).toString() + ", mPublishType=" + mPublishType + ", mPublishCount=" + mPublishCount + ", mTtlSec=" + mTtlSec + ", mEnableTerminateNotification=" + mEnableTerminateNotification + "]"; @@ -206,11 +206,11 @@ public class PublishConfig implements Parcelable { public void validate() throws IllegalArgumentException { WifiNanUtils.validateServiceName(mServiceName); - if (!TlvBufferUtils.isValid(mTxFilter, 0, 1)) { + if (!LvBufferUtils.isValid(mTxFilter, 1)) { throw new IllegalArgumentException( "Invalid txFilter configuration - LV fields do not match up to length"); } - if (!TlvBufferUtils.isValid(mRxFilter, 0, 1)) { + if (!LvBufferUtils.isValid(mRxFilter, 1)) { throw new IllegalArgumentException( "Invalid rxFilter configuration - LV fields do not match up to length"); } @@ -310,7 +310,7 @@ public class PublishConfig implements Parcelable { * determine whether they match - in addition to just relying on the * service name. *

- * Format is an LV byte array - the {@link TlvBufferUtils} utility class + * Format is an LV byte array - the {@link LvBufferUtils} utility class * is available to form and parse. * * @param txFilter The byte-array containing the LV formatted transmit @@ -331,7 +331,7 @@ public class PublishConfig implements Parcelable { * (active subscribers) - in addition to just relying on the service * name. *

- * Format is an LV byte array - the {@link TlvBufferUtils} utility class + * Format is an LV byte array - the {@link LvBufferUtils} utility class * is available to form and parse. * * @param rxFilter The byte-array containing the LV formatted receive diff --git a/wifi/java/android/net/wifi/nan/SubscribeConfig.java b/wifi/java/android/net/wifi/nan/SubscribeConfig.java index 4e53073c7cb00..fd19ddbb7f242 100644 --- a/wifi/java/android/net/wifi/nan/SubscribeConfig.java +++ b/wifi/java/android/net/wifi/nan/SubscribeConfig.java @@ -140,8 +140,8 @@ public class SubscribeConfig implements Parcelable { public String toString() { return "SubscribeConfig [mServiceName='" + mServiceName + ", mServiceSpecificInfo='" + ( (mServiceSpecificInfo == null) ? "null" : HexEncoding.encode(mServiceSpecificInfo)) - + ", mTxFilter=" + (new TlvBufferUtils.TlvIterable(0, 1, mTxFilter)).toString() - + ", mRxFilter=" + (new TlvBufferUtils.TlvIterable(0, 1, mRxFilter)).toString() + + ", mTxFilter=" + (new LvBufferUtils.LvIterable(1, mTxFilter)).toString() + + ", mRxFilter=" + (new LvBufferUtils.LvIterable(1, mRxFilter)).toString() + ", mSubscribeType=" + mSubscribeType + ", mSubscribeCount=" + mSubscribeCount + ", mTtlSec=" + mTtlSec + ", mMatchType=" + mMatchStyle + ", mEnableTerminateNotification=" + mEnableTerminateNotification + "]"; @@ -234,11 +234,11 @@ public class SubscribeConfig implements Parcelable { public void validate() throws IllegalArgumentException { WifiNanUtils.validateServiceName(mServiceName); - if (!TlvBufferUtils.isValid(mTxFilter, 0, 1)) { + if (!LvBufferUtils.isValid(mTxFilter, 1)) { throw new IllegalArgumentException( "Invalid txFilter configuration - LV fields do not match up to length"); } - if (!TlvBufferUtils.isValid(mRxFilter, 0, 1)) { + if (!LvBufferUtils.isValid(mRxFilter, 1)) { throw new IllegalArgumentException( "Invalid rxFilter configuration - LV fields do not match up to length"); } @@ -344,7 +344,7 @@ public class SubscribeConfig implements Parcelable { * publishers) to determine whether they match - in addition to just * relying on the service name. *

- * Format is an LV byte array - the {@link TlvBufferUtils} utility class + * Format is an LV byte array - the {@link LvBufferUtils} utility class * is available to form and parse. * * @param txFilter The byte-array containing the LV formatted transmit @@ -364,7 +364,7 @@ public class SubscribeConfig implements Parcelable { * subscriber to determine whether they match transmitted publish * packets - in addition to just relying on the service name. *

- * Format is an LV byte array - the {@link TlvBufferUtils} utility class + * Format is an LV byte array - the {@link LvBufferUtils} utility class * is available to form and parse. * * @param rxFilter The byte-array containing the LV formatted receive