Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3f7a3906 authored by Wei Wang's avatar Wei Wang
Browse files

Service data and manufacturer data can be repeated fields.

b/16635899

Change-Id: I73f1f4effd3f0e38cd427297eb9d22f3ba285d61
parent 8e554924
Loading
Loading
Loading
Loading
+9 −10
Original line number Diff line number Diff line
@@ -6580,22 +6580,20 @@ package android.bluetooth.le {
    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 @@ package android.bluetooth.le {
    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 @@ package android.bluetooth.le {
    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();
  }
+71 −91
Original line number Diff line number Diff line
@@ -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 @@ public final class AdvertiseData implements Parcelable {
    @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 @@ public final class AdvertiseData implements Parcelable {
    }

    /**
     * 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 @@ public final class AdvertiseData implements Parcelable {
     */
    @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 @@ public final class AdvertiseData implements Parcelable {
        }
        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 final class AdvertiseData implements Parcelable {
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeList(mServiceUuids);

        dest.writeInt(mManufacturerId);
        if (mManufacturerSpecificData == null) {
        // 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(mManufacturerSpecificData.length);
            dest.writeByteArray(mManufacturerSpecificData);
                dest.writeInt(data.length);
                dest.writeByteArray(data);
            }
        }
        dest.writeParcelable(mServiceDataUuid, flags);
        if (mServiceData == null) {
        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(mServiceData.length);
            dest.writeByteArray(mServiceData);
                dest.writeInt(data.length);
                dest.writeByteArray(data);
            }
        }
        dest.writeByte((byte) (getIncludeTxPowerLevel() ? 1 : 0));
        dest.writeByte((byte) (getIncludeDeviceName() ? 1 : 0));
@@ -209,20 +192,26 @@ public final class AdvertiseData implements Parcelable {
                            builder.addServiceUuid(uuid);
                        }
                    }
                    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.setManufacturerData(manufacturerId, manufacturerData);
                            builder.addManufacturerData(manufacturerId, manufacturerData);
                        }
                    }
                    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.setServiceData(serviceDataUuid, serviceData);
                            builder.addServiceData(serviceDataUuid, serviceData);
                        }
                    }
                    builder.setIncludeTxPowerLevel(in.readByte() == 1);
                    builder.setIncludeDeviceName(in.readByte() == 1);
@@ -236,13 +225,8 @@ public final class AdvertiseData implements Parcelable {
    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 @@ public final class AdvertiseData implements Parcelable {
         * @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 @@ public final class AdvertiseData implements Parcelable {
         * @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 @@ public final class AdvertiseData implements Parcelable {
            if (manufacturerSpecificData == null) {
                throw new IllegalArgumentException("manufacturerSpecificData is null");
            }
            mManufacturerId = manufacturerId;
            mManufacturerSpecificData = manufacturerSpecificData;
            mManufacturerSpecificData.put(manufacturerId, manufacturerSpecificData);
            return this;
        }

@@ -324,9 +306,7 @@ public final class AdvertiseData implements Parcelable {
         * Build the {@link AdvertiseData}.
         */
        public AdvertiseData build() {
            return new AdvertiseData(mServiceUuids,
                    mServiceDataUuid,
                    mServiceData, mManufacturerId, mManufacturerSpecificData,
            return new AdvertiseData(mServiceUuids, mManufacturerSpecificData, mServiceData,
                    mIncludeTxPowerLevel, mIncludeDeviceName);
        }
    }
+4 −4
Original line number Diff line number Diff line
@@ -209,13 +209,13 @@ public final class BluetoothLeAdvertiser {
                        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.
+7 −12
Original line number Diff line number Diff line
@@ -242,9 +242,6 @@ public final class ScanFilter implements Parcelable {
        return mServiceDataMask;
    }

    /**
     * @hide
     */
    @Nullable
    public ParcelUuid getServiceDataUuid() {
        return mServiceDataUuid;
@@ -303,19 +300,17 @@ public final class ScanFilter implements Parcelable {
        }

        // 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;
            }
        }
+45 −44
Original line number Diff line number Diff line
@@ -19,11 +19,14 @@ package android.bluetooth.le;
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 @@ public final class ScanRecord {
    @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,40 +79,46 @@ public final class ScanRecord {

    /**
     * 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;
    public SparseArray<byte[]> getManufacturerSpecificData() {
        return mManufacturerSpecificData;
    }

    /**
     * Returns the manufacturer specific data which is the content of manufacturer specific data
     * field.
     * Returns the manufacturer specific data associated with the manufacturer id. Returns
     * {@code null} if the {@code manufacturerId} is not found.
     */
    public byte[] getManufacturerSpecificData() {
        return mManufacturerSpecificData;
    @Nullable
    public byte[] getManufacturerSpecificData(int manufacturerId) {
        return mManufacturerSpecificData.get(manufacturerId);
    }

    /**
     * Returns a 16-bit UUID of the service that the service data is associated with.
     * Returns a map of service UUID and its corresponding service data.
     */
    public ParcelUuid getServiceDataUuid() {
        return mServiceDataUuid;
    public Map<ParcelUuid, byte[]> getServiceData() {
        return mServiceData;
    }

    /**
     * Returns service data.
     * Returns the service data byte array associated with the {@code serviceUuid}. Returns
     * {@code null} if the {@code serviceDataUuid} is not found.
     */
    public byte[] getServiceData() {
        return mServiceData;
    @Nullable
    public byte[] getServiceData(ParcelUuid serviceDataUuid) {
        if (serviceDataUuid == null) {
            return null;
        }
        return mServiceData.get(serviceDataUuid);
    }

    /**
@@ -144,14 +148,12 @@ public final class ScanRecord {
    }

    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 @@ public final class ScanRecord {
     * 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 @@ public final class ScanRecord {
        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 @@ public final class ScanRecord {
                        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 @@ public final class ScanRecord {
            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 @@ public final class ScanRecord {
    @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) {
Loading