Service data and manufacturer data can be repeated fields.

b/16635899

Change-Id: I73f1f4effd3f0e38cd427297eb9d22f3ba285d61
diff --git a/api/current.txt b/api/current.txt
index 56b0bf0..112ef01 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6580,22 +6580,20 @@
     method public int describeContents();
     method public boolean getIncludeDeviceName();
     method public boolean getIncludeTxPowerLevel();
-    method public int getManufacturerId();
-    method public byte[] getManufacturerSpecificData();
-    method public byte[] getServiceData();
-    method public android.os.ParcelUuid getServiceDataUuid();
+    method public android.util.SparseArray<byte[]> getManufacturerSpecificData();
+    method public java.util.Map<android.os.ParcelUuid, byte[]> getServiceData();
     method public java.util.List<android.os.ParcelUuid> getServiceUuids();
     method public void writeToParcel(android.os.Parcel, int);
   }
 
   public static final class AdvertiseData.Builder {
     ctor public AdvertiseData.Builder();
+    method public android.bluetooth.le.AdvertiseData.Builder addManufacturerData(int, byte[]);
+    method public android.bluetooth.le.AdvertiseData.Builder addServiceData(android.os.ParcelUuid, byte[]);
     method public android.bluetooth.le.AdvertiseData.Builder addServiceUuid(android.os.ParcelUuid);
     method public android.bluetooth.le.AdvertiseData build();
     method public android.bluetooth.le.AdvertiseData.Builder setIncludeDeviceName(boolean);
     method public android.bluetooth.le.AdvertiseData.Builder setIncludeTxPowerLevel(boolean);
-    method public android.bluetooth.le.AdvertiseData.Builder setManufacturerData(int, byte[]);
-    method public android.bluetooth.le.AdvertiseData.Builder setServiceData(android.os.ParcelUuid, byte[]);
   }
 
   public final class AdvertiseSettings implements android.os.Parcelable {
@@ -6656,6 +6654,7 @@
     method public int getManufacturerId();
     method public byte[] getServiceData();
     method public byte[] getServiceDataMask();
+    method public android.os.ParcelUuid getServiceDataUuid();
     method public android.os.ParcelUuid getServiceUuid();
     method public android.os.ParcelUuid getServiceUuidMask();
     method public boolean matches(android.bluetooth.le.ScanResult);
@@ -6679,10 +6678,10 @@
     method public int getAdvertiseFlags();
     method public byte[] getBytes();
     method public java.lang.String getDeviceName();
-    method public int getManufacturerId();
-    method public byte[] getManufacturerSpecificData();
-    method public byte[] getServiceData();
-    method public android.os.ParcelUuid getServiceDataUuid();
+    method public android.util.SparseArray<byte[]> getManufacturerSpecificData();
+    method public byte[] getManufacturerSpecificData(int);
+    method public java.util.Map<android.os.ParcelUuid, byte[]> getServiceData();
+    method public byte[] getServiceData(android.os.ParcelUuid);
     method public java.util.List<android.os.ParcelUuid> getServiceUuids();
     method public int getTxPowerLevel();
   }
diff --git a/core/java/android/bluetooth/le/AdvertiseData.java b/core/java/android/bluetooth/le/AdvertiseData.java
index b137eeb..843cd84 100644
--- a/core/java/android/bluetooth/le/AdvertiseData.java
+++ b/core/java/android/bluetooth/le/AdvertiseData.java
@@ -17,14 +17,15 @@
 package android.bluetooth.le;
 
 import android.annotation.Nullable;
-import android.bluetooth.BluetoothUuid;
 import android.os.Parcel;
 import android.os.ParcelUuid;
 import android.os.Parcelable;
+import android.util.ArrayMap;
+import android.util.SparseArray;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 
 /**
@@ -42,27 +43,18 @@
     @Nullable
     private final List<ParcelUuid> mServiceUuids;
 
-    private final int mManufacturerId;
-    @Nullable
-    private final byte[] mManufacturerSpecificData;
-
-    @Nullable
-    private final ParcelUuid mServiceDataUuid;
-    @Nullable
-    private final byte[] mServiceData;
-
+    private final SparseArray<byte[]> mManufacturerSpecificData;
+    private final Map<ParcelUuid, byte[]> mServiceData;
     private final boolean mIncludeTxPowerLevel;
     private final boolean mIncludeDeviceName;
 
     private AdvertiseData(List<ParcelUuid> serviceUuids,
-            ParcelUuid serviceDataUuid, byte[] serviceData,
-            int manufacturerId,
-            byte[] manufacturerSpecificData, boolean includeTxPowerLevel,
+            SparseArray<byte[]> manufacturerData,
+            Map<ParcelUuid, byte[]> serviceData,
+            boolean includeTxPowerLevel,
             boolean includeDeviceName) {
         mServiceUuids = serviceUuids;
-        mManufacturerId = manufacturerId;
-        mManufacturerSpecificData = manufacturerSpecificData;
-        mServiceDataUuid = serviceDataUuid;
+        mManufacturerSpecificData = manufacturerData;
         mServiceData = serviceData;
         mIncludeTxPowerLevel = includeTxPowerLevel;
         mIncludeDeviceName = includeDeviceName;
@@ -77,32 +69,17 @@
     }
 
     /**
-     * Returns the manufacturer identifier, which is a non-negative number assigned by Bluetooth
-     * SIG.
+     * Returns an array of manufacturer Id and the corresponding manufacturer specific data. The
+     * manufacturer id is a non-negative number assigned by Bluetooth SIG.
      */
-    public int getManufacturerId() {
-        return mManufacturerId;
-    }
-
-    /**
-     * Returns the manufacturer specific data which is the content of manufacturer specific data
-     * field. The first 2 bytes of the data contain the company id.
-     */
-    public byte[] getManufacturerSpecificData() {
+    public SparseArray<byte[]> getManufacturerSpecificData() {
         return mManufacturerSpecificData;
     }
 
     /**
-     * Returns a 16-bit UUID of the service that the service data is associated with.
+     * Returns a map of 16-bit UUID and its corresponding service data.
      */
-    public ParcelUuid getServiceDataUuid() {
-        return mServiceDataUuid;
-    }
-
-    /**
-     * Returns service data.
-     */
-    public byte[] getServiceData() {
+    public Map<ParcelUuid, byte[]> getServiceData() {
         return mServiceData;
     }
 
@@ -125,8 +102,8 @@
      */
     @Override
     public int hashCode() {
-        return Objects.hash(mServiceUuids, mManufacturerId, mManufacturerSpecificData,
-                mServiceDataUuid, mServiceData, mIncludeDeviceName, mIncludeTxPowerLevel);
+        return Objects.hash(mServiceUuids, mManufacturerSpecificData, mServiceData,
+                mIncludeDeviceName, mIncludeTxPowerLevel);
     }
 
     /**
@@ -142,20 +119,17 @@
         }
         AdvertiseData other = (AdvertiseData) obj;
         return Objects.equals(mServiceUuids, other.mServiceUuids) &&
-                mManufacturerId == other.mManufacturerId &&
-                Objects.deepEquals(mManufacturerSpecificData, other.mManufacturerSpecificData) &&
-                Objects.equals(mServiceDataUuid, other.mServiceDataUuid) &&
-                Objects.deepEquals(mServiceData, other.mServiceData) &&
+                Utils.equals(mManufacturerSpecificData, other.mManufacturerSpecificData) &&
+                Utils.equals(mServiceData, other.mServiceData) &&
                         mIncludeDeviceName == other.mIncludeDeviceName &&
                         mIncludeTxPowerLevel == other.mIncludeTxPowerLevel;
     }
 
     @Override
     public String toString() {
-        return "AdvertiseData [mServiceUuids=" + mServiceUuids + ", mManufacturerId="
-                + mManufacturerId + ", mManufacturerSpecificData="
-                + Arrays.toString(mManufacturerSpecificData) + ", mServiceDataUuid="
-                + mServiceDataUuid + ", mServiceData=" + Arrays.toString(mServiceData)
+        return "AdvertiseData [mServiceUuids=" + mServiceUuids + ", mManufacturerSpecificData="
+                + Utils.toString(mManufacturerSpecificData) + ", mServiceData="
+                + Utils.toString(mServiceData)
                 + ", mIncludeTxPowerLevel=" + mIncludeTxPowerLevel + ", mIncludeDeviceName="
                 + mIncludeDeviceName + "]";
     }
@@ -169,21 +143,30 @@
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeList(mServiceUuids);
 
-        dest.writeInt(mManufacturerId);
-        if (mManufacturerSpecificData == null) {
-            dest.writeInt(0);
-        } else {
-            dest.writeInt(1);
-            dest.writeInt(mManufacturerSpecificData.length);
-            dest.writeByteArray(mManufacturerSpecificData);
+        // mManufacturerSpecificData could not be null.
+        dest.writeInt(mManufacturerSpecificData.size());
+        for (int i = 0; i < mManufacturerSpecificData.size(); ++i) {
+            dest.writeInt(mManufacturerSpecificData.keyAt(i));
+            byte[] data = mManufacturerSpecificData.valueAt(i);
+            if (data == null) {
+                dest.writeInt(0);
+            } else {
+                dest.writeInt(1);
+                dest.writeInt(data.length);
+                dest.writeByteArray(data);
+            }
         }
-        dest.writeParcelable(mServiceDataUuid, flags);
-        if (mServiceData == null) {
-            dest.writeInt(0);
-        } else {
-            dest.writeInt(1);
-            dest.writeInt(mServiceData.length);
-            dest.writeByteArray(mServiceData);
+        dest.writeInt(mServiceData.size());
+        for (ParcelUuid uuid : mServiceData.keySet()) {
+            dest.writeParcelable(uuid, flags);
+            byte[] data = mServiceData.get(uuid);
+            if (data == null) {
+                dest.writeInt(0);
+            } else {
+                dest.writeInt(1);
+                dest.writeInt(data.length);
+                dest.writeByteArray(data);
+            }
         }
         dest.writeByte((byte) (getIncludeTxPowerLevel() ? 1 : 0));
         dest.writeByte((byte) (getIncludeDeviceName() ? 1 : 0));
@@ -209,20 +192,26 @@
                             builder.addServiceUuid(uuid);
                         }
                     }
-                    int manufacturerId = in.readInt();
-                    if (in.readInt() == 1) {
-                        int manufacturerDataLength = in.readInt();
-                        byte[] manufacturerData = new byte[manufacturerDataLength];
-                        in.readByteArray(manufacturerData);
-                        builder.setManufacturerData(manufacturerId, manufacturerData);
+                    int manufacturerSize = in.readInt();
+                    for (int i = 0; i < manufacturerSize; ++i) {
+                        int manufacturerId = in.readInt();
+                        if (in.readInt() == 1) {
+                            int manufacturerDataLength = in.readInt();
+                            byte[] manufacturerData = new byte[manufacturerDataLength];
+                            in.readByteArray(manufacturerData);
+                            builder.addManufacturerData(manufacturerId, manufacturerData);
+                        }
                     }
-                    ParcelUuid serviceDataUuid = in.readParcelable(
-                            ParcelUuid.class.getClassLoader());
-                    if (in.readInt() == 1) {
-                        int serviceDataLength = in.readInt();
-                        byte[] serviceData = new byte[serviceDataLength];
-                        in.readByteArray(serviceData);
-                        builder.setServiceData(serviceDataUuid, serviceData);
+                    int serviceDataSize = in.readInt();
+                    for (int i = 0; i < serviceDataSize; ++i) {
+                        ParcelUuid serviceDataUuid = in.readParcelable(
+                                ParcelUuid.class.getClassLoader());
+                        if (in.readInt() == 1) {
+                            int serviceDataLength = in.readInt();
+                            byte[] serviceData = new byte[serviceDataLength];
+                            in.readByteArray(serviceData);
+                            builder.addServiceData(serviceDataUuid, serviceData);
+                        }
                     }
                     builder.setIncludeTxPowerLevel(in.readByte() == 1);
                     builder.setIncludeDeviceName(in.readByte() == 1);
@@ -236,13 +225,8 @@
     public static final class Builder {
         @Nullable
         private List<ParcelUuid> mServiceUuids = new ArrayList<ParcelUuid>();
-        private int mManufacturerId = -1;
-        @Nullable
-        private byte[] mManufacturerSpecificData;
-        @Nullable
-        private ParcelUuid mServiceDataUuid;
-        @Nullable
-        private byte[] mServiceData;
+        private SparseArray<byte[]> mManufacturerSpecificData = new SparseArray<byte[]>();
+        private Map<ParcelUuid, byte[]> mServiceData = new ArrayMap<ParcelUuid, byte[]>();
         private boolean mIncludeTxPowerLevel;
         private boolean mIncludeDeviceName;
 
@@ -268,18 +252,17 @@
          * @throws IllegalArgumentException If the {@code serviceDataUuid} or {@code serviceData} is
          *             empty.
          */
-        public Builder setServiceData(ParcelUuid serviceDataUuid, byte[] serviceData) {
+        public Builder addServiceData(ParcelUuid serviceDataUuid, byte[] serviceData) {
             if (serviceDataUuid == null || serviceData == null) {
                 throw new IllegalArgumentException(
                         "serviceDataUuid or serviceDataUuid is null");
             }
-            mServiceDataUuid = serviceDataUuid;
-            mServiceData = serviceData;
+            mServiceData.put(serviceDataUuid, serviceData);
             return this;
         }
 
         /**
-         * Set manufacturer specific data.
+         * Add manufacturer specific data.
          * <p>
          * Please refer to the Bluetooth Assigned Numbers document provided by the <a
          * href="https://www.bluetooth.org">Bluetooth SIG</a> for a list of existing company
@@ -290,7 +273,7 @@
          * @throws IllegalArgumentException If the {@code manufacturerId} is negative or
          *             {@code manufacturerSpecificData} is null.
          */
-        public Builder setManufacturerData(int manufacturerId, byte[] manufacturerSpecificData) {
+        public Builder addManufacturerData(int manufacturerId, byte[] manufacturerSpecificData) {
             if (manufacturerId < 0) {
                 throw new IllegalArgumentException(
                         "invalid manufacturerId - " + manufacturerId);
@@ -298,8 +281,7 @@
             if (manufacturerSpecificData == null) {
                 throw new IllegalArgumentException("manufacturerSpecificData is null");
             }
-            mManufacturerId = manufacturerId;
-            mManufacturerSpecificData = manufacturerSpecificData;
+            mManufacturerSpecificData.put(manufacturerId, manufacturerSpecificData);
             return this;
         }
 
@@ -324,9 +306,7 @@
          * Build the {@link AdvertiseData}.
          */
         public AdvertiseData build() {
-            return new AdvertiseData(mServiceUuids,
-                    mServiceDataUuid,
-                    mServiceData, mManufacturerId, mManufacturerSpecificData,
+            return new AdvertiseData(mServiceUuids, mManufacturerSpecificData, mServiceData,
                     mIncludeTxPowerLevel, mIncludeDeviceName);
         }
     }
diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
index 4d128e7..8879da7 100644
--- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
+++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
@@ -209,13 +209,13 @@
                         num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT;
             }
         }
-        if (data.getServiceDataUuid() != null) {
+        for (ParcelUuid uuid : data.getServiceData().keySet()) {
             size += OVERHEAD_BYTES_PER_FIELD + SERVICE_DATA_UUID_LENGTH
-                    + byteLength(data.getServiceData());
+                    + byteLength(data.getServiceData().get(uuid));
         }
-        if (data.getManufacturerId() > 0) {
+        for (int i = 0; i < data.getManufacturerSpecificData().size(); ++i) {
             size += OVERHEAD_BYTES_PER_FIELD + MANUFACTURER_SPECIFIC_DATA_LENGTH +
-                    byteLength(data.getManufacturerSpecificData());
+                    byteLength(data.getManufacturerSpecificData().valueAt(i));
         }
         if (data.getIncludeTxPowerLevel()) {
             size += OVERHEAD_BYTES_PER_FIELD + 1; // tx power level value is one byte.
diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java
index 30aaf2e..d1b93d2 100644
--- a/core/java/android/bluetooth/le/ScanFilter.java
+++ b/core/java/android/bluetooth/le/ScanFilter.java
@@ -181,7 +181,7 @@
                                 byte[] serviceDataMask = new byte[serviceDataMaskLength];
                                 in.readByteArray(serviceDataMask);
                                 builder.setServiceData(
-                                            servcieDataUuid, serviceData, serviceDataMask);
+                                        servcieDataUuid, serviceData, serviceDataMask);
                             }
                         }
                     }
@@ -242,9 +242,6 @@
         return mServiceDataMask;
     }
 
-    /**
-     * @hide
-     */
     @Nullable
     public ParcelUuid getServiceDataUuid() {
         return mServiceDataUuid;
@@ -303,19 +300,17 @@
         }
 
         // Service data match
-        if (mServiceData != null) {
-            if (!Objects.equals(mServiceDataUuid, scanRecord.getServiceDataUuid()) ||
-                    !matchesPartialData(mServiceData, mServiceDataMask,
-                            scanRecord.getServiceData())) {
+        if (mServiceDataUuid != null) {
+            if (!matchesPartialData(mServiceData, mServiceDataMask,
+                    scanRecord.getServiceData(mServiceDataUuid))) {
                 return false;
             }
         }
 
         // Manufacturer data match.
-        if (mManufacturerData != null) {
-            if (mManufacturerId != scanRecord.getManufacturerId() ||
-                    !matchesPartialData(mManufacturerData,
-                            mManufacturerDataMask, scanRecord.getManufacturerSpecificData())) {
+        if (mManufacturerId >= 0) {
+            if (!matchesPartialData(mManufacturerData, mManufacturerDataMask,
+                    scanRecord.getManufacturerSpecificData(mManufacturerId))) {
                 return false;
             }
         }
diff --git a/core/java/android/bluetooth/le/ScanRecord.java b/core/java/android/bluetooth/le/ScanRecord.java
index e564c7d..e7f33ff1 100644
--- a/core/java/android/bluetooth/le/ScanRecord.java
+++ b/core/java/android/bluetooth/le/ScanRecord.java
@@ -19,11 +19,14 @@
 import android.annotation.Nullable;
 import android.bluetooth.BluetoothUuid;
 import android.os.ParcelUuid;
+import android.util.ArrayMap;
 import android.util.Log;
+import android.util.SparseArray;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Represents a scan record from Bluetooth LE scan.
@@ -53,14 +56,9 @@
     @Nullable
     private final List<ParcelUuid> mServiceUuids;
 
-    private final int mManufacturerId;
-    @Nullable
-    private final byte[] mManufacturerSpecificData;
+    private final SparseArray<byte[]> mManufacturerSpecificData;
 
-    @Nullable
-    private final ParcelUuid mServiceDataUuid;
-    @Nullable
-    private final byte[] mServiceData;
+    private final Map<ParcelUuid, byte[]> mServiceData;
 
     // Transmission power level(in dB).
     private final int mTxPowerLevel;
@@ -81,43 +79,49 @@
 
     /**
      * Returns a list of service UUIDs within the advertisement that are used to identify the
-     * bluetooth gatt services.
+     * bluetooth GATT services.
      */
     public List<ParcelUuid> getServiceUuids() {
         return mServiceUuids;
     }
 
     /**
-     * Returns the manufacturer identifier, which is a non-negative number assigned by Bluetooth
-     * SIG.
+     * Returns a sparse array of manufacturer identifier and its corresponding manufacturer specific
+     * data.
      */
-    public int getManufacturerId() {
-        return mManufacturerId;
-    }
-
-    /**
-     * Returns the manufacturer specific data which is the content of manufacturer specific data
-     * field.
-     */
-    public byte[] getManufacturerSpecificData() {
+    public SparseArray<byte[]> getManufacturerSpecificData() {
         return mManufacturerSpecificData;
     }
 
     /**
-     * Returns a 16-bit UUID of the service that the service data is associated with.
+     * Returns the manufacturer specific data associated with the manufacturer id. Returns
+     * {@code null} if the {@code manufacturerId} is not found.
      */
-    public ParcelUuid getServiceDataUuid() {
-        return mServiceDataUuid;
+    @Nullable
+    public byte[] getManufacturerSpecificData(int manufacturerId) {
+        return mManufacturerSpecificData.get(manufacturerId);
     }
 
     /**
-     * Returns service data.
+     * Returns a map of service UUID and its corresponding service data.
      */
-    public byte[] getServiceData() {
+    public Map<ParcelUuid, byte[]> getServiceData() {
         return mServiceData;
     }
 
     /**
+     * Returns the service data byte array associated with the {@code serviceUuid}. Returns
+     * {@code null} if the {@code serviceDataUuid} is not found.
+     */
+    @Nullable
+    public byte[] getServiceData(ParcelUuid serviceDataUuid) {
+        if (serviceDataUuid == null) {
+            return null;
+        }
+        return mServiceData.get(serviceDataUuid);
+    }
+
+    /**
      * Returns the transmission power level of the packet in dBm. Returns {@link Integer#MIN_VALUE}
      * if the field is not set. This value can be used to calculate the path loss of a received
      * packet using the following equation:
@@ -144,14 +148,12 @@
     }
 
     private ScanRecord(List<ParcelUuid> serviceUuids,
-            ParcelUuid serviceDataUuid, byte[] serviceData,
-            int manufacturerId,
-            byte[] manufacturerSpecificData, int advertiseFlags, int txPowerLevel,
+            SparseArray<byte[]> manufacturerData,
+            Map<ParcelUuid, byte[]> serviceData,
+            int advertiseFlags, int txPowerLevel,
             String localName, byte[] bytes) {
         mServiceUuids = serviceUuids;
-        mManufacturerId = manufacturerId;
-        mManufacturerSpecificData = manufacturerSpecificData;
-        mServiceDataUuid = serviceDataUuid;
+        mManufacturerSpecificData = manufacturerData;
         mServiceData = serviceData;
         mDeviceName = localName;
         mAdvertiseFlags = advertiseFlags;
@@ -168,7 +170,6 @@
      * order.
      *
      * @param scanRecord The scan record of Bluetooth LE advertisement and/or scan response.
-     *
      * @hide
      */
     public static ScanRecord parseFromBytes(byte[] scanRecord) {
@@ -181,10 +182,9 @@
         List<ParcelUuid> serviceUuids = new ArrayList<ParcelUuid>();
         String localName = null;
         int txPowerLevel = Integer.MIN_VALUE;
-        ParcelUuid serviceDataUuid = null;
-        byte[] serviceData = null;
-        int manufacturerId = -1;
-        byte[] manufacturerSpecificData = null;
+
+        SparseArray<byte[]> manufacturerData = new SparseArray<byte[]>();
+        Map<ParcelUuid, byte[]> serviceData = new ArrayMap<ParcelUuid, byte[]>();
 
         try {
             while (currentPos < scanRecord.length) {
@@ -230,16 +230,20 @@
                         int serviceUuidLength = BluetoothUuid.UUID_BYTES_16_BIT;
                         byte[] serviceDataUuidBytes = extractBytes(scanRecord, currentPos,
                                 serviceUuidLength);
-                        serviceDataUuid = BluetoothUuid.parseUuidFrom(serviceDataUuidBytes);
-                        serviceData = extractBytes(scanRecord, currentPos + 2, dataLength - 2);
+                        ParcelUuid serviceDataUuid = BluetoothUuid.parseUuidFrom(
+                                serviceDataUuidBytes);
+                        byte[] serviceDataArray = extractBytes(scanRecord,
+                                currentPos + serviceUuidLength, dataLength - serviceUuidLength);
+                        serviceData.put(serviceDataUuid, serviceDataArray);
                         break;
                     case DATA_TYPE_MANUFACTURER_SPECIFIC_DATA:
                         // The first two bytes of the manufacturer specific data are
                         // manufacturer ids in little endian.
-                        manufacturerId = ((scanRecord[currentPos + 1] & 0xFF) << 8) +
+                        int manufacturerId = ((scanRecord[currentPos + 1] & 0xFF) << 8) +
                                 (scanRecord[currentPos] & 0xFF);
-                        manufacturerSpecificData = extractBytes(scanRecord, currentPos + 2,
+                        byte[] manufacturerDataBytes = extractBytes(scanRecord, currentPos + 2,
                                 dataLength - 2);
+                        manufacturerData.put(manufacturerId, manufacturerDataBytes);
                         break;
                     default:
                         // Just ignore, we don't handle such data type.
@@ -251,9 +255,8 @@
             if (serviceUuids.isEmpty()) {
                 serviceUuids = null;
             }
-            return new ScanRecord(serviceUuids, serviceDataUuid, serviceData,
-                    manufacturerId, manufacturerSpecificData, advertiseFlag, txPowerLevel,
-                    localName, scanRecord);
+            return new ScanRecord(serviceUuids, manufacturerData, serviceData,
+                    advertiseFlag, txPowerLevel, localName, scanRecord);
         } catch (IndexOutOfBoundsException e) {
             Log.e(TAG, "unable to parse scan record: " + Arrays.toString(scanRecord));
             return null;
@@ -263,13 +266,11 @@
     @Override
     public String toString() {
         return "ScanRecord [mAdvertiseFlags=" + mAdvertiseFlags + ", mServiceUuids=" + mServiceUuids
-                + ", mManufacturerId=" + mManufacturerId + ", mManufacturerSpecificData="
-                + Arrays.toString(mManufacturerSpecificData) + ", mServiceDataUuid="
-                + mServiceDataUuid + ", mServiceData=" + Arrays.toString(mServiceData)
+                + ", mManufacturerSpecificData=" + Utils.toString(mManufacturerSpecificData)
+                + ", mServiceData=" + Utils.toString(mServiceData)
                 + ", mTxPowerLevel=" + mTxPowerLevel + ", mDeviceName=" + mDeviceName + "]";
     }
 
-
     // Parse service UUIDs.
     private static int parseServiceUuid(byte[] scanRecord, int currentPos, int dataLength,
             int uuidLength, List<ParcelUuid> serviceUuids) {
diff --git a/core/java/android/bluetooth/le/Utils.java b/core/java/android/bluetooth/le/Utils.java
new file mode 100644
index 0000000..8598dd7
--- /dev/null
+++ b/core/java/android/bluetooth/le/Utils.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2014 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.bluetooth.le;
+
+import android.util.SparseArray;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Helper class for Bluetooth LE utils.
+ *
+ * @hide
+ */
+public class Utils {
+
+    /**
+     * Returns a string composed from a {@link SparseArray}.
+     */
+    static String toString(SparseArray<byte[]> array) {
+        if (array == null) {
+            return "null";
+        }
+        if (array.size() == 0) {
+            return "{}";
+        }
+        StringBuilder buffer = new StringBuilder();
+        buffer.append('{');
+        for (int i = 0; i < array.size(); ++i) {
+            buffer.append(array.keyAt(i)).append("=").append(array.valueAt(i));
+        }
+        buffer.append('}');
+        return buffer.toString();
+    }
+
+    /**
+     * Returns a string composed from a {@link Map}.
+     */
+    static <T> String toString(Map<T, byte[]> map) {
+        if (map == null) {
+            return "null";
+        }
+        if (map.isEmpty()) {
+            return "{}";
+        }
+        StringBuilder buffer = new StringBuilder();
+        buffer.append('{');
+        Iterator<Map.Entry<T, byte[]>> it = map.entrySet().iterator();
+        while (it.hasNext()) {
+            Map.Entry<T, byte[]> entry = it.next();
+            Object key = entry.getKey();
+            buffer.append(key).append("=").append(Arrays.toString(map.get(key)));
+            if (it.hasNext()) {
+                buffer.append(", ");
+            }
+        }
+        buffer.append('}');
+        return buffer.toString();
+    }
+
+    /**
+     * Check whether two {@link SparseArray} equal.
+     */
+    static boolean equals(SparseArray<byte[]> array, SparseArray<byte[]> otherArray) {
+        if (array == otherArray) {
+            return true;
+        }
+        if (array == null || otherArray == null) {
+            return false;
+        }
+        if (array.size() != otherArray.size()) {
+            return false;
+        }
+
+        // Keys are guaranteed in ascending order when indices are in ascending order.
+        for (int i = 0; i < array.size(); ++i) {
+            if (array.keyAt(i) != otherArray.keyAt(i) ||
+                    !Arrays.equals(array.valueAt(i), otherArray.valueAt(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Check whether two {@link Map} equal.
+     */
+    static <T> boolean equals(Map<T, byte[]> map, Map<T, byte[]> otherMap) {
+        if (map == otherMap) {
+            return true;
+        }
+        if (map == null || otherMap == null) {
+            return false;
+        }
+        if (map.size() != otherMap.size()) {
+            return false;
+        }
+        Set<T> keys = map.keySet();
+        if (!keys.equals(otherMap.keySet())) {
+            return false;
+        }
+        for (T key : keys) {
+            if (!Objects.deepEquals(map.get(key), otherMap.get(key))) {
+                return false;
+            }
+        }
+        return true;
+    }
+}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/le/AdvertiseDataTest.java b/core/tests/bluetoothtests/src/android/bluetooth/le/AdvertiseDataTest.java
index 5e451ca..e58d905 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/le/AdvertiseDataTest.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/le/AdvertiseDataTest.java
@@ -66,7 +66,7 @@
         byte[] manufacturerData = new byte[0];
         AdvertiseData data =
                 mAdvertiseDataBuilder.setIncludeDeviceName(true)
-                        .setManufacturerData(manufacturerId, manufacturerData).build();
+                        .addManufacturerData(manufacturerId, manufacturerData).build();
         data.writeToParcel(parcel, 0);
         parcel.setDataPosition(0);
         AdvertiseData dataFromParcel =
@@ -81,7 +81,7 @@
         byte[] serviceData = new byte[0];
         AdvertiseData data =
                 mAdvertiseDataBuilder.setIncludeDeviceName(true)
-                        .setServiceData(uuid, serviceData).build();
+                        .addServiceData(uuid, serviceData).build();
         data.writeToParcel(parcel, 0);
         parcel.setDataPosition(0);
         AdvertiseData dataFromParcel =
@@ -117,7 +117,7 @@
         AdvertiseData data =
                 mAdvertiseDataBuilder.setIncludeDeviceName(true)
                         .addServiceUuid(uuid).addServiceUuid(uuid2)
-                        .setManufacturerData(manufacturerId, manufacturerData).build();
+                        .addManufacturerData(manufacturerId, manufacturerData).build();
 
         data.writeToParcel(parcel, 0);
         parcel.setDataPosition(0);
@@ -134,7 +134,7 @@
                 (byte) 0xF0, 0x00, 0x02, 0x15 };
         AdvertiseData data =
                 mAdvertiseDataBuilder.setIncludeDeviceName(true)
-                        .setServiceData(uuid, serviceData).build();
+                        .addServiceData(uuid, serviceData).build();
         data.writeToParcel(parcel, 0);
         parcel.setDataPosition(0);
         AdvertiseData dataFromParcel =
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java
index ccdd90a..8b3db7e 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java
@@ -53,13 +53,13 @@
         assertEquals("Ped", data.getDeviceName());
         assertEquals(-20, data.getTxPowerLevel());
 
-        assertEquals(0x00e0, data.getManufacturerId());
+        assertTrue(data.getManufacturerSpecificData().get(0x00E0) != null);
         assertArrayEquals(new byte[] {
-                0x02, 0x15 }, data.getManufacturerSpecificData());
+                0x02, 0x15 }, data.getManufacturerSpecificData().get(0x00E0));
 
-        assertEquals(uuid2, data.getServiceDataUuid());
+        assertTrue(data.getServiceData().containsKey(uuid2));
         assertArrayEquals(new byte[] {
-                0x50, 0x64 }, data.getServiceData());
+                0x50, 0x64 }, data.getServiceData().get(uuid2));
     }
 
     // Assert two byte arrays are equal.