From 1857b982c320311e10e109064c204e7894aa0730 Mon Sep 17 00:00:00 2001 From: Etan Cohen Date: Wed, 23 Nov 2016 11:11:23 -0800 Subject: [PATCH] AWARE: Abstract structure of match filters Replace byte[] representation with a List representation which abstracts the LV (TLV without a type field) structure of the match filters. Tlv utilities: remove tag indicating that will be made public API. That is no longer expected (will be an internal utility) since the match filter structure is now parsed when exposed to API. Lv utilities: removed. Was a simple wrapper around Tlv utilities - no longer needed since will not be exposed as API. (cherry-pick of commit c9870d0d431471bf1362e46fc4f2f1bc6722896a) Bug: 32840891 Test: unit-tests and integration (sl4a) tests passing Change-Id: I6d792c4d7584ce002f6c11692e83e7654ef3e764 --- .../android/net/wifi/aware/ConfigRequest.java | 2 +- .../android/net/wifi/aware/LvBufferUtils.java | 340 ------------------ .../android/net/wifi/aware/PublishConfig.java | 23 +- .../net/wifi/aware/SubscribeConfig.java | 26 +- .../net/wifi/aware/TlvBufferUtils.java | 47 ++- .../wifi/aware/WifiAwareCharacteristics.java | 5 +- .../aware/WifiAwareDiscoveryBaseSession.java | 12 +- .../WifiAwareDiscoverySessionCallback.java | 6 +- .../net/wifi/aware/WifiAwareManager.java | 22 +- .../net/wifi/aware/TlvBufferUtilsTest.java | 71 +++- .../net/wifi/aware/WifiAwareManagerTest.java | 65 ++-- 11 files changed, 202 insertions(+), 417 deletions(-) delete mode 100644 wifi/java/android/net/wifi/aware/LvBufferUtils.java diff --git a/wifi/java/android/net/wifi/aware/ConfigRequest.java b/wifi/java/android/net/wifi/aware/ConfigRequest.java index 4aacbae6272f9..4b21b15d49942 100644 --- a/wifi/java/android/net/wifi/aware/ConfigRequest.java +++ b/wifi/java/android/net/wifi/aware/ConfigRequest.java @@ -22,7 +22,7 @@ import android.os.Parcelable; /** * Defines a request object to configure a Wi-Fi Aware network. Built using * {@link ConfigRequest.Builder}. Configuration is requested using - * {@link WifiAwareManager#attach(android.os.Handler, WifiAwareAttachCallback)}. + * {@link WifiAwareManager#attach(WifiAwareAttachCallback, android.os.Handler)}. * Note that the actual achieved configuration may be different from the * requested configuration - since different applications may request different * configurations. diff --git a/wifi/java/android/net/wifi/aware/LvBufferUtils.java b/wifi/java/android/net/wifi/aware/LvBufferUtils.java deleted file mode 100644 index 3265243e28d60..0000000000000 --- a/wifi/java/android/net/wifi/aware/LvBufferUtils.java +++ /dev/null @@ -1,340 +0,0 @@ -/* - * 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.aware; - -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_AWARE_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/aware/PublishConfig.java b/wifi/java/android/net/wifi/aware/PublishConfig.java index ba493a024a13c..3925bd71ab9ed 100644 --- a/wifi/java/android/net/wifi/aware/PublishConfig.java +++ b/wifi/java/android/net/wifi/aware/PublishConfig.java @@ -28,6 +28,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.nio.charset.StandardCharsets; import java.util.Arrays; +import java.util.List; /** * Defines the configuration of a Aware publish session. Built using @@ -84,7 +85,8 @@ public final class PublishConfig implements Parcelable { /** @hide */ public final boolean mEnableTerminateNotification; - private PublishConfig(byte[] serviceName, byte[] serviceSpecificInfo, byte[] matchFilter, + /** @hide */ + public PublishConfig(byte[] serviceName, byte[] serviceSpecificInfo, byte[] matchFilter, int publishType, int publichCount, int ttlSec, boolean enableTerminateNotification) { mServiceName = serviceName; mServiceSpecificInfo = serviceSpecificInfo; @@ -99,9 +101,9 @@ public final class PublishConfig implements Parcelable { public String toString() { return "PublishConfig [mServiceName='" + mServiceName + ", mServiceSpecificInfo='" + ( (mServiceSpecificInfo == null) ? "null" : HexEncoding.encode(mServiceSpecificInfo)) - + ", mTxFilter=" + (new LvBufferUtils.LvIterable(1, mMatchFilter)).toString() - + ", mPublishType=" + mPublishType + ", mPublishCount=" + mPublishCount - + ", mTtlSec=" + mTtlSec + ", mEnableTerminateNotification=" + + ", mMatchFilter=" + (new TlvBufferUtils.TlvIterable(0, 1, + mMatchFilter)).toString() + ", mPublishType=" + mPublishType + ", mPublishCount=" + + mPublishCount + ", mTtlSec=" + mTtlSec + ", mEnableTerminateNotification=" + mEnableTerminateNotification + "]"; } @@ -186,7 +188,7 @@ public final class PublishConfig implements Parcelable { throws IllegalArgumentException { WifiAwareUtils.validateServiceName(mServiceName); - if (!LvBufferUtils.isValid(mMatchFilter, 1)) { + if (!TlvBufferUtils.isValid(mMatchFilter, 0, 1)) { throw new IllegalArgumentException( "Invalid txFilter configuration - LV fields do not match up to length"); } @@ -281,18 +283,17 @@ public final class PublishConfig implements Parcelable { * The match filter for a publish session. Used to determine whether a service * discovery occurred - in addition to relying on the service name. *

- * Format is an LV byte array: a single byte Length field followed by L bytes (the value of - * the Length field) of a value blob. - *

* Optional. Empty by default. * - * @param matchFilter The byte-array containing the LV formatted match filter. + * @param matchFilter A list of match filter entries (each of which is an arbitrary byte + * array). * * @return The builder to facilitate chaining * {@code builder.setXXX(..).setXXX(..)}. */ - public Builder setMatchFilter(@Nullable byte[] matchFilter) { - mMatchFilter = matchFilter; + public Builder setMatchFilter(@Nullable List matchFilter) { + mMatchFilter = new TlvBufferUtils.TlvConstructor(0, 1).allocateAndPut( + matchFilter).getArray(); return this; } diff --git a/wifi/java/android/net/wifi/aware/SubscribeConfig.java b/wifi/java/android/net/wifi/aware/SubscribeConfig.java index 5e14f8f761e05..bf35445ff88af 100644 --- a/wifi/java/android/net/wifi/aware/SubscribeConfig.java +++ b/wifi/java/android/net/wifi/aware/SubscribeConfig.java @@ -28,6 +28,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.nio.charset.StandardCharsets; import java.util.Arrays; +import java.util.List; /** * Defines the configuration of a Aware subscribe session. Built using @@ -106,7 +107,8 @@ public final class SubscribeConfig implements Parcelable { /** @hide */ public final boolean mEnableTerminateNotification; - private SubscribeConfig(byte[] serviceName, byte[] serviceSpecificInfo, byte[] matchFilter, + /** @hide */ + public SubscribeConfig(byte[] serviceName, byte[] serviceSpecificInfo, byte[] matchFilter, int subscribeType, int publichCount, int ttlSec, int matchStyle, boolean enableTerminateNotification) { mServiceName = serviceName; @@ -123,10 +125,11 @@ public final class SubscribeConfig implements Parcelable { public String toString() { return "SubscribeConfig [mServiceName='" + mServiceName + ", mServiceSpecificInfo='" + ( (mServiceSpecificInfo == null) ? "null" : HexEncoding.encode(mServiceSpecificInfo)) - + ", mMatchFilter=" + (new LvBufferUtils.LvIterable(1, mMatchFilter)).toString() - + ", mSubscribeType=" + mSubscribeType + ", mSubscribeCount=" + mSubscribeCount - + ", mTtlSec=" + mTtlSec + ", mMatchType=" + mMatchStyle - + ", mEnableTerminateNotification=" + mEnableTerminateNotification + "]"; + + ", mMatchFilter=" + (new TlvBufferUtils.TlvIterable(0, 1, + mMatchFilter)).toString() + ", mSubscribeType=" + mSubscribeType + + ", mSubscribeCount=" + mSubscribeCount + ", mTtlSec=" + mTtlSec + ", mMatchType=" + + mMatchStyle + ", mEnableTerminateNotification=" + mEnableTerminateNotification + + "]"; } @Override @@ -213,7 +216,7 @@ public final class SubscribeConfig implements Parcelable { throws IllegalArgumentException { WifiAwareUtils.validateServiceName(mServiceName); - if (!LvBufferUtils.isValid(mMatchFilter, 1)) { + if (!TlvBufferUtils.isValid(mMatchFilter, 0, 1)) { throw new IllegalArgumentException( "Invalid matchFilter configuration - LV fields do not match up to length"); } @@ -313,18 +316,17 @@ public final class SubscribeConfig implements Parcelable { * The match filter for a subscribe session. Used to determine whether a service * discovery occurred - in addition to relying on the service name. *

- * Format is an LV byte array: a single byte Length field followed by L bytes (the value of - * the Length field) of a value blob. - *

* Optional. Empty by default. * - * @param matchFilter The byte-array containing the LV formatted match filter. + * @param matchFilter A list of match filter entries (each of which is an arbitrary byte + * array). * * @return The builder to facilitate chaining * {@code builder.setXXX(..).setXXX(..)}. */ - public Builder setMatchFilter(@Nullable byte[] matchFilter) { - mMatchFilter = matchFilter; + public Builder setMatchFilter(@Nullable List matchFilter) { + mMatchFilter = new TlvBufferUtils.TlvConstructor(0, 1).allocateAndPut( + matchFilter).getArray(); return this; } diff --git a/wifi/java/android/net/wifi/aware/TlvBufferUtils.java b/wifi/java/android/net/wifi/aware/TlvBufferUtils.java index 56c90692f1b83..29f10e932e48d 100644 --- a/wifi/java/android/net/wifi/aware/TlvBufferUtils.java +++ b/wifi/java/android/net/wifi/aware/TlvBufferUtils.java @@ -22,8 +22,10 @@ import libcore.io.Memory; import java.nio.BufferOverflowException; import java.nio.ByteOrder; +import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; +import java.util.List; import java.util.NoSuchElementException; /** @@ -32,7 +34,7 @@ import java.util.NoSuchElementException; * the Type field and the Length field. A Type field size of 0 is allowed - * allowing usage for LV (no T) array formats. * - * @hide PROPOSED_AWARE_API + * @hide */ public class TlvBufferUtils { private TlvBufferUtils() { @@ -110,6 +112,31 @@ public class TlvBufferUtils { return this; } + /** + * Creates a TLV array (of the previously specified Type and Length sizes) from the input + * list. Allocates an array matching the contents (and required Type and Length + * fields), copies the contents, and set the Length fields. The Type field is set to 0. + * + * @param list A list of fields to be added to the TLV buffer. + * @return The constructor of the TLV. + */ + public TlvConstructor allocateAndPut(@Nullable List list) { + if (list != null) { + int size = 0; + for (byte[] field : list) { + size += mTypeSize + mLengthSize; + if (field != null) { + size += field.length; + } + } + allocate(size); + for (byte[] field : list) { + putByteArray(0, field); + } + } + return this; + } + /** * Copies a byte into the TLV with the indicated type. For an LV * formatted structure (i.e. typeLength=0 in {@link TlvConstructor @@ -319,6 +346,10 @@ public class TlvBufferUtils { this.length = length; this.refArray = refArray; this.offset = offset; + + if (offset + length > refArray.length) { + throw new BufferOverflowException(); + } } /** @@ -393,7 +424,7 @@ public class TlvBufferUtils { * @param typeSize Number of bytes used for the Type (T) field. Valid * values are 0 (i.e. indicating the format is LV rather than * TLV), 1, and 2 bytes. - * @param lengthSize Number of bytes sued for the Length (L) field. + * @param lengthSize Number of bytes used for the Length (L) field. * Values values are 1 or 2 bytes. * @param array The TLV formatted byte-array to parse. */ @@ -449,6 +480,18 @@ public class TlvBufferUtils { return builder.toString(); } + /** + * Returns a List with the raw contents (no types) of the iterator. + */ + public List toList() { + List list = new ArrayList<>(); + for (TlvElement tlv : this) { + list.add(Arrays.copyOfRange(tlv.refArray, tlv.offset, tlv.offset + tlv.length)); + } + + return list; + } + /** * Returns an iterator to step through a TLV formatted byte-array. The * individual elements returned by the iterator are {@link TlvElement}. diff --git a/wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.java b/wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.java index 072ccab1eb962..95d128d8e5147 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.java @@ -69,8 +69,9 @@ public class WifiAwareCharacteristics implements Parcelable { /** * Returns the maximum length of byte array that can be used to specify a Aware match filter. - * Restricts the parameters of the {@link PublishConfig.Builder#setMatchFilter(byte[])} and - * {@link SubscribeConfig.Builder#setMatchFilter(byte[])}. + * Restricts the parameters of the + * {@link PublishConfig.Builder#setMatchFilter(java.util.List)} and + * {@link SubscribeConfig.Builder#setMatchFilter(java.util.List)}. * * @return A positive integer, maximum legngth of byte array for Aware discovery match filter. */ diff --git a/wifi/java/android/net/wifi/aware/WifiAwareDiscoveryBaseSession.java b/wifi/java/android/net/wifi/aware/WifiAwareDiscoveryBaseSession.java index 01e77dadcba36..451d8a594cc59 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareDiscoveryBaseSession.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareDiscoveryBaseSession.java @@ -140,7 +140,7 @@ public class WifiAwareDiscoveryBaseSession { * Sends a message to the specified destination. Aware messages are transmitted in the context * of a discovery session - executed subsequent to a publish/subscribe * {@link WifiAwareDiscoverySessionCallback#onServiceDiscovered(WifiAwareManager.PeerHandle, - * byte[], byte[])} event. + * byte[], java.util.List)} event. *

* Aware messages are not guaranteed delivery. Callbacks on * {@link WifiAwareDiscoverySessionCallback} indicate message was transmitted successfully, @@ -154,7 +154,7 @@ public class WifiAwareDiscoveryBaseSession { * * @param peerHandle The peer's handle for the message. Must be a result of an * {@link WifiAwareDiscoverySessionCallback#onServiceDiscovered(WifiAwareManager.PeerHandle, - * byte[], byte[])} or + * byte[], java.util.List)} or * {@link WifiAwareDiscoverySessionCallback#onMessageReceived(WifiAwareManager.PeerHandle, * byte[])} events. * @param messageId An arbitrary integer used by the caller to identify the message. The same @@ -187,7 +187,7 @@ public class WifiAwareDiscoveryBaseSession { * Sends a message to the specified destination. Aware messages are transmitted in the context * of a discovery session - executed subsequent to a publish/subscribe * {@link WifiAwareDiscoverySessionCallback#onServiceDiscovered(WifiAwareManager.PeerHandle, - * byte[], byte[])} event. + * byte[], java.util.List)} event. *

* Aware messages are not guaranteed delivery. Callbacks on * {@link WifiAwareDiscoverySessionCallback} indicate message was transmitted successfully, @@ -203,7 +203,7 @@ public class WifiAwareDiscoveryBaseSession { * * @param peerHandle The peer's handle for the message. Must be a result of an * {@link WifiAwareDiscoverySessionCallback#onServiceDiscovered(WifiAwareManager.PeerHandle, - * byte[], byte[])} or + * byte[], java.util.List)} or * {@link WifiAwareDiscoverySessionCallback#onMessageReceived(WifiAwareManager.PeerHandle, * byte[])} events. * @param messageId An arbitrary integer used by the caller to identify the message. The same @@ -220,7 +220,7 @@ public class WifiAwareDiscoveryBaseSession { /** * Start a ranging operation with the specified peers. The peer IDs are obtained from an * {@link WifiAwareDiscoverySessionCallback#onServiceDiscovered(WifiAwareManager.PeerHandle, - * byte[], byte[])} or + * byte[], java.util.List)} or * {@link WifiAwareDiscoverySessionCallback#onMessageReceived(WifiAwareManager.PeerHandle, * byte[])} operation - can * only range devices which are part of an ongoing discovery session. @@ -266,7 +266,7 @@ public class WifiAwareDiscoveryBaseSession { * * @param peerHandle The peer's handle obtained through * {@link WifiAwareDiscoverySessionCallback#onServiceDiscovered(WifiAwareManager.PeerHandle, - * byte[], byte[])} or + * byte[], java.util.List)} or * {@link WifiAwareDiscoverySessionCallback#onMessageReceived(WifiAwareManager.PeerHandle, * byte[])}. On a RESPONDER this value is used to gate the acceptance of a connection request * from only that peer. A RESPONDER may specified a null - indicating that diff --git a/wifi/java/android/net/wifi/aware/WifiAwareDiscoverySessionCallback.java b/wifi/java/android/net/wifi/aware/WifiAwareDiscoverySessionCallback.java index 6331c9c726c9f..fdf0d013334d1 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareDiscoverySessionCallback.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareDiscoverySessionCallback.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.List; /** * Base class for Aware session events callbacks. Should be extended by @@ -130,11 +131,10 @@ public class WifiAwareDiscoverySessionCallback { * @param serviceSpecificInfo The service specific information (arbitrary * byte array) provided by the peer as part of its discovery * configuration. - * @param matchFilter The filter (Tx on advertiser and Rx on listener) which - * resulted in this service discovery. + * @param matchFilter The filter which resulted in this service discovery. */ public void onServiceDiscovered(WifiAwareManager.PeerHandle peerHandle, - byte[] serviceSpecificInfo, byte[] matchFilter) { + byte[] serviceSpecificInfo, List matchFilter) { /* empty */ } diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java index cc247047758ea..029794dfa3801 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java @@ -45,7 +45,9 @@ import org.json.JSONObject; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; +import java.nio.BufferOverflowException; import java.util.Arrays; +import java.util.List; /** * This class provides the primary API for managing Wi-Fi Aware operations: @@ -874,12 +876,22 @@ public class WifiAwareManager { case CALLBACK_SESSION_TERMINATED: onProxySessionTerminated(msg.arg1); break; - case CALLBACK_MATCH: - mOriginalCallback.onServiceDiscovered( - new PeerHandle(msg.arg1), + case CALLBACK_MATCH: { + List matchFilter = null; + byte[] arg = msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE2); + try { + matchFilter = new TlvBufferUtils.TlvIterable(0, 1, arg).toList(); + } catch (BufferOverflowException e) { + matchFilter = null; + Log.e(TAG, "onServiceDiscovered: invalid match filter byte array '" + + new String(HexEncoding.encode(arg)) + + "' - cannot be parsed: e=" + e); + } + mOriginalCallback.onServiceDiscovered(new PeerHandle(msg.arg1), msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE), - msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE2)); + matchFilter); break; + } case CALLBACK_MESSAGE_SEND_SUCCESS: mOriginalCallback.onMessageSendSucceeded(msg.arg1); break; @@ -966,7 +978,7 @@ public class WifiAwareManager { @Override public void onMessageReceived(int peerId, byte[] message) { if (VDBG) { - Log.v(TAG, "onMessageReceived: peerId='" + peerId); + Log.v(TAG, "onMessageReceived: peerId=" + peerId); } Message msg = mHandler.obtainMessage(CALLBACK_MESSAGE_RECEIVED); diff --git a/wifi/tests/src/android/net/wifi/aware/TlvBufferUtilsTest.java b/wifi/tests/src/android/net/wifi/aware/TlvBufferUtilsTest.java index 4b6957bfa3f63..15641abd11f90 100644 --- a/wifi/tests/src/android/net/wifi/aware/TlvBufferUtilsTest.java +++ b/wifi/tests/src/android/net/wifi/aware/TlvBufferUtilsTest.java @@ -24,6 +24,10 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ErrorCollector; +import java.nio.BufferOverflowException; +import java.util.ArrayList; +import java.util.List; + /** * Unit test harness for TlvBufferUtils class. */ @@ -47,9 +51,9 @@ public class TlvBufferUtilsTest { collector.checkThat("tlv11-correct-construction", tlv11.getArray(), equalTo(new byte[]{0, 1, 2, 2, 3, 0, 1, 2})); - LvBufferUtils.LvConstructor tlv01 = new LvBufferUtils.LvConstructor(1); + TlvBufferUtils.TlvConstructor tlv01 = new TlvBufferUtils.TlvConstructor(0, 1); tlv01.allocate(15); - tlv01.putByte((byte) 2); + tlv01.putByte(0, (byte) 2); tlv01.putByteArray(2, new byte[] { 0, 1, 2 }); @@ -60,10 +64,63 @@ public class TlvBufferUtilsTest { TlvBufferUtils.isValid(tlv11.getArray(), 1, 1), equalTo(true)); collector.checkThat("tlv01-valid", - LvBufferUtils.isValid(tlv01.getArray(), 1), + TlvBufferUtils.isValid(tlv01.getArray(), 0, 1), equalTo(true)); } + /** + * Verify that can build a valid TLV from a List of byte[]. + */ + @Test + public void testTlvListOperations() { + byte[] entry1 = { 1, 2, 3 }; + byte[] entry2 = { 4, 5 }; + byte[] entry3 = new byte[0]; + List data = new ArrayList<>(); + data.add(entry1); + data.add(entry2); + data.add(entry3); + data.add(null); // zero-length should work + + TlvBufferUtils.TlvConstructor tlv01 = new TlvBufferUtils.TlvConstructor(0, 1); + tlv01.allocateAndPut(data); + byte[] tlvData = tlv01.getArray(); + List parsedList = new TlvBufferUtils.TlvIterable(0, 1, tlvData).toList(); + + collector.checkThat("tlvData-correct-length", tlvData.length, + equalTo(entry1.length + 1 + entry2.length + 1 + entry3.length + 1 + 1)); + collector.checkThat("parsedList-correct-length", parsedList.size(), equalTo(4)); + collector.checkThat("parsedList-entry1", parsedList.get(0), equalTo(entry1)); + collector.checkThat("parsedList-entry2", parsedList.get(1), equalTo(entry2)); + collector.checkThat("parsedList-entry3", parsedList.get(2), equalTo(entry3)); + collector.checkThat("parsedList-entry4", parsedList.get(3), equalTo(new byte[0])); + } + + /** + * Verify that can parse a (correctly formatted) byte array to a list. + */ + @Test + public void testTlvParseToList() { + byte[] validTlv01 = { 0, 1, 55, 2, 33, 66, 0 }; + + List parsedList = new TlvBufferUtils.TlvIterable(0, 1, validTlv01).toList(); + + collector.checkThat("parsedList-entry1", parsedList.get(0), equalTo(new byte[0])); + collector.checkThat("parsedList-entry2", parsedList.get(1), equalTo(new byte[] { 55 })); + collector.checkThat("parsedList-entry3", parsedList.get(2), equalTo(new byte[] { 33, 66 })); + collector.checkThat("parsedList-entry4", parsedList.get(3), equalTo(new byte[0])); + } + + /** + * Verify that an exception is thrown when trying to parse an invalid array. + */ + @Test(expected = BufferOverflowException.class) + public void testTlvParseToListError() { + byte[] invalidTlv01 = { 0, 1, 55, 2, 55, 66, 3 }; // bad data + + List data = new TlvBufferUtils.TlvIterable(0, 1, invalidTlv01).toList(); + } + @Test public void testTlvIterate() { final String ascii = "ABC"; @@ -137,7 +194,7 @@ public class TlvBufferUtilsTest { TlvBufferUtils.isValid(tlv22.getArray(), 2, 2), equalTo(true)); collector.checkThat("tlv02-valid", - LvBufferUtils.isValid(tlv02.getArray(), 2), + TlvBufferUtils.isValid(tlv02.getArray(), 0, 2), equalTo(true)); } @@ -211,15 +268,15 @@ public class TlvBufferUtilsTest { */ @Test public void testTlvInvalidByteArray() { - LvBufferUtils.LvConstructor tlv01 = new LvBufferUtils.LvConstructor(1); + TlvBufferUtils.TlvConstructor tlv01 = new TlvBufferUtils.TlvConstructor(0, 1); tlv01.allocate(15); - tlv01.putByte((byte) 2); + tlv01.putByte(0, (byte) 2); tlv01.putByteArray(2, new byte[]{0, 1, 2}); byte[] array = tlv01.getArray(); array[0] = 10; collector.checkThat("tlv01-invalid", - LvBufferUtils.isValid(array, 1), equalTo(false)); + TlvBufferUtils.isValid(array, 0, 1), equalTo(false)); } } diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java index e1613106751ee..24c0127cb11b9 100644 --- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java +++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java @@ -49,6 +49,8 @@ import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.List; + /** * Unit test harness for WifiAwareManager class. */ @@ -276,7 +278,7 @@ public class WifiAwareManagerTest { final PublishConfig publishConfig = new PublishConfig.Builder().build(); final WifiAwareManager.PeerHandle peerHandle = new WifiAwareManager.PeerHandle(873); final String string1 = "hey from here..."; - final String string2 = "some other arbitrary string..."; + final byte[] matchFilter = { 1, 12, 2, 31, 32 }; final int messageId = 2123; final int reason = AWARE_STATUS_ERROR; @@ -292,6 +294,8 @@ public class WifiAwareManagerTest { .forClass(WifiAwarePublishDiscoverySession.class); ArgumentCaptor peerIdCaptor = ArgumentCaptor.forClass( WifiAwareManager.PeerHandle.class); + ArgumentCaptor> matchFilterCaptor = ArgumentCaptor.forClass( + (Class) List.class); // (0) connect + success mDut.attach(mMockLooperHandler, configRequest, mockCallback, null); @@ -314,8 +318,7 @@ public class WifiAwareManagerTest { // (3) ... publishSession.getValue().sendMessage(peerHandle, messageId, string1.getBytes()); - sessionProxyCallback.getValue().onMatch(peerHandle.peerId, string1.getBytes(), - string2.getBytes()); + sessionProxyCallback.getValue().onMatch(peerHandle.peerId, string1.getBytes(), matchFilter); sessionProxyCallback.getValue().onMessageReceived(peerHandle.peerId, string1.getBytes()); sessionProxyCallback.getValue().onMessageSendFail(messageId, reason); sessionProxyCallback.getValue().onMessageSendSuccess(messageId); @@ -324,13 +327,22 @@ public class WifiAwareManagerTest { inOrder.verify(mockAwareService).sendMessage(eq(clientId), eq(sessionId), eq(peerHandle.peerId), eq(string1.getBytes()), eq(messageId), eq(0)); inOrder.verify(mockSessionCallback).onServiceDiscovered(peerIdCaptor.capture(), - eq(string1.getBytes()), eq(string2.getBytes())); - assertEquals(((WifiAwareManager.PeerHandle) peerIdCaptor.getValue()).peerId, - peerHandle.peerId); + eq(string1.getBytes()), + matchFilterCaptor.capture()); + + // note: need to capture/compare elements since the Mockito eq() is a shallow comparator + List parsedMatchFilter = new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList(); + collector.checkThat("match-filter-size", parsedMatchFilter.size(), + equalTo(matchFilterCaptor.getValue().size())); + collector.checkThat("match-filter-entry0", parsedMatchFilter.get(0), + equalTo(matchFilterCaptor.getValue().get(0))); + collector.checkThat("match-filter-entry1", parsedMatchFilter.get(1), + equalTo(matchFilterCaptor.getValue().get(1))); + + assertEquals(peerIdCaptor.getValue().peerId, peerHandle.peerId); inOrder.verify(mockSessionCallback).onMessageReceived(peerIdCaptor.capture(), eq(string1.getBytes())); - assertEquals(((WifiAwareManager.PeerHandle) peerIdCaptor.getValue()).peerId, - peerHandle.peerId); + assertEquals(peerIdCaptor.getValue().peerId, peerHandle.peerId); inOrder.verify(mockSessionCallback).onMessageSendFailed(eq(messageId)); inOrder.verify(mockSessionCallback).onMessageSendSucceeded(eq(messageId)); @@ -418,7 +430,7 @@ public class WifiAwareManagerTest { final SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build(); final WifiAwareManager.PeerHandle peerHandle = new WifiAwareManager.PeerHandle(873); final String string1 = "hey from here..."; - final String string2 = "some other arbitrary string..."; + final byte[] matchFilter = { 1, 12, 3, 31, 32 }; // bad data! final int messageId = 2123; final int reason = AWARE_STATUS_ERROR; @@ -456,8 +468,7 @@ public class WifiAwareManagerTest { // (3) ... subscribeSession.getValue().sendMessage(peerHandle, messageId, string1.getBytes()); - sessionProxyCallback.getValue().onMatch(peerHandle.peerId, string1.getBytes(), - string2.getBytes()); + sessionProxyCallback.getValue().onMatch(peerHandle.peerId, string1.getBytes(), matchFilter); sessionProxyCallback.getValue().onMessageReceived(peerHandle.peerId, string1.getBytes()); sessionProxyCallback.getValue().onMessageSendFail(messageId, reason); sessionProxyCallback.getValue().onMessageSendSuccess(messageId); @@ -466,13 +477,11 @@ public class WifiAwareManagerTest { inOrder.verify(mockAwareService).sendMessage(eq(clientId), eq(sessionId), eq(peerHandle.peerId), eq(string1.getBytes()), eq(messageId), eq(0)); inOrder.verify(mockSessionCallback).onServiceDiscovered(peerIdCaptor.capture(), - eq(string1.getBytes()), eq(string2.getBytes())); - assertEquals(((WifiAwareManager.PeerHandle) peerIdCaptor.getValue()).peerId, - peerHandle.peerId); + eq(string1.getBytes()), (List) isNull()); + assertEquals((peerIdCaptor.getValue()).peerId, peerHandle.peerId); inOrder.verify(mockSessionCallback).onMessageReceived(peerIdCaptor.capture(), eq(string1.getBytes())); - assertEquals(((WifiAwareManager.PeerHandle) peerIdCaptor.getValue()).peerId, - peerHandle.peerId); + assertEquals((peerIdCaptor.getValue()).peerId, peerHandle.peerId); inOrder.verify(mockSessionCallback).onMessageSendFailed(eq(messageId)); inOrder.verify(mockSessionCallback).onMessageSendSucceeded(eq(messageId)); @@ -676,8 +685,7 @@ public class WifiAwareManagerTest { public void testSubscribeConfigBuilder() { final String serviceName = "some_service_or_other"; final String serviceSpecificInfo = "long arbitrary string with some info"; - final byte[] matchFilter = { - 0, 1, 16, 1, 22 }; + final byte[] matchFilter = { 1, 16, 1, 22 }; final int subscribeType = SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE; final int subscribeCount = 10; final int subscribeTtl = 15; @@ -685,7 +693,8 @@ public class WifiAwareManagerTest { final boolean enableTerminateNotification = false; SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName) - .setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(matchFilter) + .setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter( + new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList()) .setSubscribeType(subscribeType) .setSubscribeCount(subscribeCount).setTtlSec(subscribeTtl).setMatchStyle(matchStyle) .setTerminateNotificationEnabled(enableTerminateNotification).build(); @@ -709,8 +718,7 @@ public class WifiAwareManagerTest { public void testSubscribeConfigParcel() { final String serviceName = "some_service_or_other"; final String serviceSpecificInfo = "long arbitrary string with some info"; - final byte[] matchFilter = { - 0, 1, 16, 1, 22 }; + final byte[] matchFilter = { 1, 16, 1, 22 }; final int subscribeType = SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE; final int subscribeCount = 10; final int subscribeTtl = 15; @@ -718,7 +726,8 @@ public class WifiAwareManagerTest { final boolean enableTerminateNotification = true; SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName) - .setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(matchFilter) + .setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter( + new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList()) .setSubscribeType(subscribeType) .setSubscribeCount(subscribeCount).setTtlSec(subscribeTtl).setMatchStyle(matchStyle) .setTerminateNotificationEnabled(enableTerminateNotification).build(); @@ -780,15 +789,15 @@ public class WifiAwareManagerTest { public void testPublishConfigBuilder() { final String serviceName = "some_service_or_other"; final String serviceSpecificInfo = "long arbitrary string with some info"; - final byte[] matchFilter = { - 0, 1, 16, 1, 22 }; + final byte[] matchFilter = { 1, 16, 1, 22 }; final int publishType = PublishConfig.PUBLISH_TYPE_SOLICITED; final int publishCount = 10; final int publishTtl = 15; final boolean enableTerminateNotification = false; PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName) - .setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(matchFilter) + .setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter( + new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList()) .setPublishType(publishType) .setPublishCount(publishCount).setTtlSec(publishTtl) .setTerminateNotificationEnabled(enableTerminateNotification).build(); @@ -809,15 +818,15 @@ public class WifiAwareManagerTest { public void testPublishConfigParcel() { final String serviceName = "some_service_or_other"; final String serviceSpecificInfo = "long arbitrary string with some info"; - final byte[] matchFilter = { - 0, 1, 16, 1, 22 }; + final byte[] matchFilter = { 1, 16, 1, 22 }; final int publishType = PublishConfig.PUBLISH_TYPE_SOLICITED; final int publishCount = 10; final int publishTtl = 15; final boolean enableTerminateNotification = false; PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName) - .setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(matchFilter) + .setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter( + new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList()) .setPublishType(publishType) .setPublishCount(publishCount).setTtlSec(publishTtl) .setTerminateNotificationEnabled(enableTerminateNotification).build();