Merge "AWARE: Abstract structure of match filters"
am: f358a69cba

Change-Id: Ic7e87e47e9a9beebb11aa7465e4d2be9dd3e767a
diff --git a/wifi/java/android/net/wifi/aware/ConfigRequest.java b/wifi/java/android/net/wifi/aware/ConfigRequest.java
index 4aacbae..4b21b15 100644
--- a/wifi/java/android/net/wifi/aware/ConfigRequest.java
+++ b/wifi/java/android/net/wifi/aware/ConfigRequest.java
@@ -22,7 +22,7 @@
 /**
  * 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 3265243..0000000
--- 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.
-     * <p>
-     * A constructor is created specifying the size of the Length (L) field.
-     * <p>
-     * The byte array is either provided (using
-     * {@link LvBufferUtils.LvConstructor#wrap(byte[])}) or allocated (using
-     * {@link LvBufferUtils.LvConstructor#allocate(int)}).
-     * <p>
-     * Values are added to the structure using the {@code LvConstructor.put*()}
-     * methods.
-     * <p>
-     * 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<LvBufferUtils.LvElement> {
-        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<LvBufferUtils.LvElement> iterator() {
-            return new Iterator<LvBufferUtils.LvElement>() {
-                private Iterator<TlvBufferUtils.TlvElement> 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 ba493a0..3925bd7 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.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 @@
     /** @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 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 @@
             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 @@
          * The match filter for a publish session. Used to determine whether a service
          * discovery occurred - in addition to relying on the service name.
          * <p>
-         * 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.
-         * <p>
          *     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<byte[]> 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 5e14f8f..bf35445 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.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 @@
     /** @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 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 @@
             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 @@
          * The match filter for a subscribe session. Used to determine whether a service
          * discovery occurred - in addition to relying on the service name.
          * <p>
-         * 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.
-         * <p>
          *     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<byte[]> 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 56c9069..29f10e9 100644
--- a/wifi/java/android/net/wifi/aware/TlvBufferUtils.java
+++ b/wifi/java/android/net/wifi/aware/TlvBufferUtils.java
@@ -22,8 +22,10 @@
 
 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 @@
  * 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() {
@@ -111,6 +113,31 @@
         }
 
         /**
+         * 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<byte[]> 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
          * TlvConstructor(int, int)} ) the type field is ignored.
@@ -319,6 +346,10 @@
             this.length = length;
             this.refArray = refArray;
             this.offset = offset;
+
+            if (offset + length > refArray.length) {
+                throw new BufferOverflowException();
+            }
         }
 
         /**
@@ -393,7 +424,7 @@
          * @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.
          */
@@ -450,6 +481,18 @@
         }
 
         /**
+         * Returns a List with the raw contents (no types) of the iterator.
+         */
+        public List<byte[]> toList() {
+            List<byte[]> 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 072ccab..95d128d 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.java
@@ -69,8 +69,9 @@
 
     /**
      * 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<byte[]>)} and
+     * {@link SubscribeConfig.Builder#setMatchFilter(java.util.List<byte[]>)}.
      *
      * @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 01e77da..451d8a5 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareDiscoveryBaseSession.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareDiscoveryBaseSession.java
@@ -140,7 +140,7 @@
      * 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<byte[]>)} event.
      * <p>
      *     Aware messages are not guaranteed delivery. Callbacks on
      *     {@link WifiAwareDiscoverySessionCallback} indicate message was transmitted successfully,
@@ -154,7 +154,7 @@
      *
      * @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<byte[]>)} 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 @@
      * 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<byte[]>)} event.
      * <p>
      *     Aware messages are not guaranteed delivery. Callbacks on
      *     {@link WifiAwareDiscoverySessionCallback} indicate message was transmitted successfully,
@@ -203,7 +203,7 @@
      *
      * @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<byte[]>)} 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 @@
     /**
      * 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<byte[]>)} or
      * {@link WifiAwareDiscoverySessionCallback#onMessageReceived(WifiAwareManager.PeerHandle,
      * byte[])} operation - can
      * only range devices which are part of an ongoing discovery session.
@@ -266,7 +266,7 @@
      *
      * @param peerHandle The peer's handle obtained through
      * {@link WifiAwareDiscoverySessionCallback#onServiceDiscovered(WifiAwareManager.PeerHandle,
-     * byte[], byte[])} or
+     * byte[], java.util.List<byte[]>)} 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 6331c9c..fdf0d01 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareDiscoverySessionCallback.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareDiscoverySessionCallback.java
@@ -21,6 +21,7 @@
 
 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 @@
      * @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<byte[]> matchFilter) {
         /* empty */
     }
 
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
index cc24704..029794d 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -45,7 +45,9 @@
 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 @@
                         case CALLBACK_SESSION_TERMINATED:
                             onProxySessionTerminated(msg.arg1);
                             break;
-                        case CALLBACK_MATCH:
-                            mOriginalCallback.onServiceDiscovered(
-                                    new PeerHandle(msg.arg1),
+                        case CALLBACK_MATCH: {
+                            List<byte[]> 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 @@
         @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 4b6957b..15641ab 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.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 @@
         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 @@
                 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<byte[]> 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<byte[]> 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<byte[]> 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<byte[]> data = new TlvBufferUtils.TlvIterable(0, 1, invalidTlv01).toList();
+    }
+
     @Test
     public void testTlvIterate() {
         final String ascii = "ABC";
@@ -137,7 +194,7 @@
                 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 @@
      */
     @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 e161310..24c0127 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.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.List;
+
 /**
  * Unit test harness for WifiAwareManager class.
  */
@@ -276,7 +278,7 @@
         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 @@
                 .forClass(WifiAwarePublishDiscoverySession.class);
         ArgumentCaptor<WifiAwareManager.PeerHandle> peerIdCaptor = ArgumentCaptor.forClass(
                 WifiAwareManager.PeerHandle.class);
+        ArgumentCaptor<List<byte[]>> matchFilterCaptor = ArgumentCaptor.forClass(
+                (Class) List.class);
 
         // (0) connect + success
         mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
@@ -314,8 +318,7 @@
 
         // (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 @@
         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<byte[]> 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 @@
         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 @@
 
         // (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 @@
         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<byte[]>) 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 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 @@
         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 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 @@
         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 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 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();