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

Commit 2265b5b2 authored by Chienyuan's avatar Chienyuan Committed by Automerger Merge Worker
Browse files

LE Scanner: Add AD TYPE filter APIs am: 599bf26c

Original change: https://android-review.googlesource.com/c/platform/packages/modules/Bluetooth/+/1956001

Change-Id: Ie85ee761dfc2a6e42a962a6b47fa2f49e9bbe617
parents c04d3364 599bf26c
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -1230,6 +1230,9 @@ package android.bluetooth.le {

  public final class ScanFilter implements android.os.Parcelable {
    method public int describeContents();
    method @Nullable public byte[] getAdvertisingData();
    method @Nullable public byte[] getAdvertisingDataMask();
    method public int getAdvertisingDataType();
    method @Nullable public String getDeviceAddress();
    method @Nullable public String getDeviceName();
    method @Nullable public byte[] getManufacturerData();
@@ -1250,6 +1253,7 @@ package android.bluetooth.le {
  public static final class ScanFilter.Builder {
    ctor public ScanFilter.Builder();
    method public android.bluetooth.le.ScanFilter build();
    method @NonNull public android.bluetooth.le.ScanFilter.Builder setAdvertisingDataWithType(int, @Nullable byte[], @Nullable byte[]);
    method public android.bluetooth.le.ScanFilter.Builder setDeviceAddress(String);
    method public android.bluetooth.le.ScanFilter.Builder setDeviceName(String);
    method public android.bluetooth.le.ScanFilter.Builder setManufacturerData(int, byte[]);
@@ -1264,6 +1268,7 @@ package android.bluetooth.le {

  public final class ScanRecord {
    method public int getAdvertiseFlags();
    method @NonNull public java.util.Map<java.lang.Integer,byte[]> getAdvertisingDataMap();
    method public byte[] getBytes();
    method @Nullable public String getDeviceName();
    method public android.util.SparseArray<byte[]> getManufacturerSpecificData();
+133 −19
Original line number Diff line number Diff line
@@ -83,15 +83,21 @@ public final class ScanFilter implements Parcelable {
    @Nullable
    private final byte[] mManufacturerDataMask;

    private int mAdvertisingDataType = -1;
    @Nullable
    private final byte[] mAdvertisingData;
    @Nullable
    private final byte[] mAdvertisingDataMask;

    /** @hide */
    public static final ScanFilter EMPTY = new ScanFilter.Builder().build();

    private ScanFilter(String name, String deviceAddress, ParcelUuid uuid,
            ParcelUuid uuidMask, ParcelUuid solicitationUuid,
            ParcelUuid solicitationUuidMask, ParcelUuid serviceDataUuid,
            byte[] serviceData, byte[] serviceDataMask,
    private ScanFilter(String name, String deviceAddress, ParcelUuid uuid, ParcelUuid uuidMask,
            ParcelUuid solicitationUuid, ParcelUuid solicitationUuidMask,
            ParcelUuid serviceDataUuid, byte[] serviceData, byte[] serviceDataMask,
            int manufacturerId, byte[] manufacturerData, byte[] manufacturerDataMask,
            @AddressType int addressType, @Nullable byte[] irk) {
            @AddressType int addressType, @Nullable byte[] irk, int advertisingDataType,
            @Nullable byte[] advertisingData, @Nullable byte[] advertisingDataMask) {
        mDeviceName = name;
        mServiceUuid = uuid;
        mServiceUuidMask = uuidMask;
@@ -106,6 +112,9 @@ public final class ScanFilter implements Parcelable {
        mManufacturerDataMask = manufacturerDataMask;
        mAddressType = addressType;
        mIrk = irk;
        mAdvertisingDataType = advertisingDataType;
        mAdvertisingData = advertisingData;
        mAdvertisingDataMask = advertisingDataMask;
    }

    @Override
@@ -175,6 +184,20 @@ public final class ScanFilter implements Parcelable {
                dest.writeByteArray(mIrk);
            }
        }

        // Advertising data type filter
        dest.writeInt(mAdvertisingDataType);
        dest.writeInt(mAdvertisingData == null ? 0 : 1);
        if (mAdvertisingData != null) {
            dest.writeInt(mAdvertisingData.length);
            dest.writeByteArray(mAdvertisingData);

            dest.writeInt(mAdvertisingDataMask == null ? 0 : 1);
            if (mAdvertisingDataMask != null) {
                dest.writeInt(mAdvertisingDataMask.length);
                dest.writeByteArray(mAdvertisingDataMask);
            }
        }
    }

    /**
@@ -265,6 +288,26 @@ public final class ScanFilter implements Parcelable {
                    builder.setDeviceAddress(address, addressType);
                }
            }

            // Advertising data type
            int advertisingDataType = in.readInt();
            if (advertisingDataType != -1) {
                byte[] advertisingData = null;
                byte[] advertisingDataMask = null;
                if (in.readInt() == 1) {
                    int advertisingDataLength = in.readInt();
                    advertisingData = new byte[advertisingDataLength];
                    in.readByteArray(advertisingData);
                    if (in.readInt() == 1) {
                        int advertisingDataMaskLength = in.readInt();
                        advertisingDataMask = new byte[advertisingDataMaskLength];
                        in.readByteArray(advertisingDataMask);
                    }
                }
                builder.setAdvertisingDataWithType(advertisingDataType, advertisingData,
                        advertisingDataMask);
            }

            return builder.build();
        }
    };
@@ -360,6 +403,21 @@ public final class ScanFilter implements Parcelable {
        return mManufacturerDataMask;
    }

    /**
     * Returns the advertising data type. -1 if the type is not set.
     */
    public int getAdvertisingDataType() {
        return mAdvertisingDataType;
    }

    public @Nullable byte[] getAdvertisingData() {
        return mAdvertisingData;
    }

    public @Nullable byte[] getAdvertisingDataMask() {
        return mAdvertisingDataMask;
    }

    /**
     * Check if the scan filter matches a {@code scanResult}. A scan result is considered as a match
     * if it matches all the field filters.
@@ -380,7 +438,8 @@ public final class ScanFilter implements Parcelable {
        // Scan record is null but there exist filters on it.
        if (scanRecord == null
                && (mDeviceName != null || mServiceUuid != null || mManufacturerData != null
                || mServiceData != null || mServiceSolicitationUuid != null)) {
                || mServiceData != null || mServiceSolicitationUuid != null
                || mAdvertisingData != null)) {
            return false;
        }

@@ -417,6 +476,16 @@ public final class ScanFilter implements Parcelable {
                return false;
            }
        }

        // Advertising data type match
        if (mAdvertisingDataType >= 0) {
            byte[] advertisingData = scanRecord.getAdvertisingDataMap().get(mAdvertisingDataType);
            if (advertisingData == null || !matchesPartialData(mAdvertisingData,
                    mAdvertisingDataMask, advertisingData)) {
                return false;
            }
        }

        // All filters match.
        return true;
    }
@@ -503,15 +572,17 @@ public final class ScanFilter implements Parcelable {
    @Override
    public String toString() {
        return "BluetoothLeScanFilter [mDeviceName=" + mDeviceName + ", mDeviceAddress="
                + mDeviceAddress
                + ", mUuid=" + mServiceUuid + ", mUuidMask=" + mServiceUuidMask
                + mDeviceAddress + ", mUuid=" + mServiceUuid + ", mUuidMask=" + mServiceUuidMask
                + ", mServiceSolicitationUuid=" + mServiceSolicitationUuid
                + ", mServiceSolicitationUuidMask=" + mServiceSolicitationUuidMask
                + ", mServiceDataUuid=" + Objects.toString(mServiceDataUuid) + ", mServiceData="
                + Arrays.toString(mServiceData) + ", mServiceDataMask="
                + ", mServiceDataUuid=" + Objects.toString(mServiceDataUuid)
                + ", mServiceData=" + Arrays.toString(mServiceData) + ", mServiceDataMask="
                + Arrays.toString(mServiceDataMask) + ", mManufacturerId=" + mManufacturerId
                + ", mManufacturerData=" + Arrays.toString(mManufacturerData)
                + ", mManufacturerDataMask=" + Arrays.toString(mManufacturerDataMask) + "]";
                + ", mManufacturerDataMask=" + Arrays.toString(mManufacturerDataMask)
                + ", mAdvertisingDataType=" + mAdvertisingDataType + ", mAdvertisingData="
                + Arrays.toString(mAdvertisingData) + ", mAdvertisingDataMask="
                + Arrays.toString(mAdvertisingDataMask) + "]";
    }

    @Override
@@ -523,7 +594,10 @@ public final class ScanFilter implements Parcelable {
                Arrays.hashCode(mServiceData),
                Arrays.hashCode(mServiceDataMask),
                mServiceUuid, mServiceUuidMask,
                mServiceSolicitationUuid, mServiceSolicitationUuidMask);
                mServiceSolicitationUuid, mServiceSolicitationUuidMask,
                mAdvertisingDataType,
                Arrays.hashCode(mAdvertisingData),
                Arrays.hashCode(mAdvertisingDataMask));
    }

    @Override
@@ -547,7 +621,10 @@ public final class ScanFilter implements Parcelable {
                && Objects.equals(mServiceUuidMask, other.mServiceUuidMask)
                && Objects.equals(mServiceSolicitationUuid, other.mServiceSolicitationUuid)
                && Objects.equals(mServiceSolicitationUuidMask,
                        other.mServiceSolicitationUuidMask);
                        other.mServiceSolicitationUuidMask)
                && mAdvertisingDataType == other.mAdvertisingDataType
                && Objects.deepEquals(mAdvertisingData, other.mAdvertisingData)
                && Objects.deepEquals(mAdvertisingDataMask, other.mAdvertisingDataMask);
    }

    /**
@@ -589,6 +666,10 @@ public final class ScanFilter implements Parcelable {
        private byte[] mManufacturerData;
        private byte[] mManufacturerDataMask;

        private int mAdvertisingDataType = -1;
        private byte[] mAdvertisingData;
        private byte[] mAdvertisingDataMask;

        /**
         * Set filter on device name.
         */
@@ -893,18 +974,51 @@ public final class ScanFilter implements Parcelable {
            return this;
        }

        /**
         * Set filter on advertising data with specific advertising data type.
         * For any bit in the mask, set it the 1 if it needs to match the one in
         * advertising data, otherwise set it to 0.
         * <p>
         * The {@code advertisingDataMask} must have the same length of {@code advertisingData}.
         *
         * @throws IllegalArgumentException If the {@code advertisingDataType} is invalid, {@code
         * advertisingData} is null while {@code advertisingDataMask} is not, or {@code
         * advertisingData} and {@code advertisingDataMask} have different length.
         */
        public @NonNull Builder setAdvertisingDataWithType(int advertisingDataType,
                @Nullable byte[] advertisingData, @Nullable byte[] advertisingDataMask) {
            if (advertisingDataType < -1) {
                throw new IllegalArgumentException("invalid advertising data type");
            }
            if (mAdvertisingDataMask != null) {
                if (mAdvertisingData == null) {
                    throw new IllegalArgumentException(
                            "mAdvertisingData is null while mAdvertisingDataMask is not null");
                }
                // Since the mAdvertisingDataMask is a bit mask for mAdvertisingData, the lengths
                // of the two byte array need to be the same.
                if (mAdvertisingData.length != mAdvertisingDataMask.length) {
                    throw new IllegalArgumentException(
                            "size mismatch for mAdvertisingData and mAdvertisingDataMask");
                }
            }
            mAdvertisingDataType = advertisingDataType;
            mAdvertisingData = advertisingData;
            mAdvertisingDataMask = advertisingDataMask;
            return this;
        }

        /**
         * Build {@link ScanFilter}.
         *
         * @throws IllegalArgumentException If the filter cannot be built.
         */
        public ScanFilter build() {
            return new ScanFilter(mDeviceName, mDeviceAddress,
                    mServiceUuid, mUuidMask, mServiceSolicitationUuid,
                    mServiceSolicitationUuidMask,
                    mServiceDataUuid, mServiceData, mServiceDataMask,
                    mManufacturerId, mManufacturerData, mManufacturerDataMask,
                    mAddressType, mIrk);
            return new ScanFilter(mDeviceName, mDeviceAddress, mServiceUuid, mUuidMask,
                    mServiceSolicitationUuid, mServiceSolicitationUuidMask, mServiceDataUuid,
                    mServiceData, mServiceDataMask, mManufacturerId, mManufacturerData,
                    mManufacturerDataMask, mAddressType, mIrk, mAdvertisingDataType,
                    mAdvertisingData, mAdvertisingDataMask);
        }
    }
}
+20 −3
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import android.util.SparseArray;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
@@ -81,6 +82,8 @@ public final class ScanRecord {
    // Raw bytes of scan record.
    private final byte[] mBytes;

    private final HashMap<Integer, byte[]> mAdvertisingDataMap;

    /**
     * Returns the advertising flags indicating the discoverable mode and capability of the device.
     * Returns -1 if the flag field is not set.
@@ -164,6 +167,14 @@ public final class ScanRecord {
        return mDeviceName;
    }


    /**
     * Returns a map of advertising data type and its corresponding advertising data.
     */
    public @NonNull Map<Integer, byte[]> getAdvertisingDataMap() {
        return mAdvertisingDataMap;
    }

    /**
     * Returns raw bytes of scan record.
     */
@@ -197,7 +208,7 @@ public final class ScanRecord {
            SparseArray<byte[]> manufacturerData,
            Map<ParcelUuid, byte[]> serviceData,
            int advertiseFlags, int txPowerLevel,
            String localName, byte[] bytes) {
            String localName, HashMap<Integer, byte[]> advertisingDataMap, byte[] bytes) {
        mServiceSolicitationUuids = serviceSolicitationUuids;
        mServiceUuids = serviceUuids;
        mManufacturerSpecificData = manufacturerData;
@@ -205,6 +216,7 @@ public final class ScanRecord {
        mDeviceName = localName;
        mAdvertiseFlags = advertiseFlags;
        mTxPowerLevel = txPowerLevel;
        mAdvertisingDataMap = advertisingDataMap;
        mBytes = bytes;
    }

@@ -234,6 +246,7 @@ public final class ScanRecord {

        SparseArray<byte[]> manufacturerData = new SparseArray<byte[]>();
        Map<ParcelUuid, byte[]> serviceData = new ArrayMap<ParcelUuid, byte[]>();
        HashMap<Integer, byte[]> advertisingDataMap = new HashMap<Integer, byte[]>();

        try {
            while (currentPos < scanRecord.length) {
@@ -246,6 +259,8 @@ public final class ScanRecord {
                int dataLength = length - 1;
                // fieldType is unsigned int.
                int fieldType = scanRecord[currentPos++] & 0xFF;
                byte[] advertisingData = extractBytes(scanRecord, currentPos, dataLength);
                advertisingDataMap.put(fieldType, advertisingData);
                switch (fieldType) {
                    case DATA_TYPE_FLAGS:
                        advertiseFlag = scanRecord[currentPos] & 0xFF;
@@ -323,12 +338,14 @@ public final class ScanRecord {
                serviceUuids = null;
            }
            return new ScanRecord(serviceUuids, serviceSolicitationUuids, manufacturerData,
                    serviceData, advertiseFlag, txPowerLevel, localName, scanRecord);
                    serviceData, advertiseFlag, txPowerLevel, localName, advertisingDataMap,
                    scanRecord);
        } catch (Exception e) {
            Log.e(TAG, "unable to parse scan record: " + Arrays.toString(scanRecord));
            // As the record is invalid, ignore all the parsed results for this packet
            // and return an empty record with raw scanRecord bytes in results
            return new ScanRecord(null, null, null, null, -1, Integer.MIN_VALUE, null, scanRecord);
            return new ScanRecord(null, null, null, null, -1, Integer.MIN_VALUE, null,
                    advertisingDataMap, scanRecord);
        }
    }