BLE: Add service solicitation uuid feature in scan filter
Adding service solicitation uuid feature in scan filter, So BLE Scanner can set scan filter for advertising packets that includes the Service Solicitation uuid, which can be one of the below types. - List of 16 bit Service UUIDs - List of 32 bit Service UUIDs - List of 128 bit Service UUIDs Test: BLE Scanner can do filter scan for advertising packets that includes the Service Solicitation uuid. Bug: 78483310 Change-Id: I3d83c50e446fca06a76db002dad716759c145d6e
This commit is contained in:
committed by
Jakub Pawlowski
parent
ef7432f245
commit
1555eae7fa
@@ -8689,6 +8689,8 @@ package android.bluetooth.le {
|
||||
method public byte[] getServiceData();
|
||||
method public byte[] getServiceDataMask();
|
||||
method public android.os.ParcelUuid getServiceDataUuid();
|
||||
method public android.os.ParcelUuid getServiceSolicitationUuid();
|
||||
method public android.os.ParcelUuid getServiceSolicitationUuidMask();
|
||||
method public android.os.ParcelUuid getServiceUuid();
|
||||
method public android.os.ParcelUuid getServiceUuidMask();
|
||||
method public boolean matches(android.bluetooth.le.ScanResult);
|
||||
@@ -8705,6 +8707,8 @@ package android.bluetooth.le {
|
||||
method public android.bluetooth.le.ScanFilter.Builder setManufacturerData(int, byte[], byte[]);
|
||||
method public android.bluetooth.le.ScanFilter.Builder setServiceData(android.os.ParcelUuid, byte[]);
|
||||
method public android.bluetooth.le.ScanFilter.Builder setServiceData(android.os.ParcelUuid, byte[], byte[]);
|
||||
method public android.bluetooth.le.ScanFilter.Builder setServiceSolicitationUuid(android.os.ParcelUuid);
|
||||
method public android.bluetooth.le.ScanFilter.Builder setServiceSolicitationUuid(android.os.ParcelUuid, android.os.ParcelUuid);
|
||||
method public android.bluetooth.le.ScanFilter.Builder setServiceUuid(android.os.ParcelUuid);
|
||||
method public android.bluetooth.le.ScanFilter.Builder setServiceUuid(android.os.ParcelUuid, android.os.ParcelUuid);
|
||||
}
|
||||
@@ -8717,6 +8721,7 @@ package android.bluetooth.le {
|
||||
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> getServiceSolicitationUuids();
|
||||
method public java.util.List<android.os.ParcelUuid> getServiceUuids();
|
||||
method public int getTxPowerLevel();
|
||||
}
|
||||
|
||||
@@ -57,6 +57,11 @@ public final class ScanFilter implements Parcelable {
|
||||
@Nullable
|
||||
private final ParcelUuid mServiceUuidMask;
|
||||
|
||||
@Nullable
|
||||
private final ParcelUuid mServiceSolicitationUuid;
|
||||
@Nullable
|
||||
private final ParcelUuid mServiceSolicitationUuidMask;
|
||||
|
||||
@Nullable
|
||||
private final ParcelUuid mServiceDataUuid;
|
||||
@Nullable
|
||||
@@ -75,12 +80,15 @@ public final class ScanFilter implements Parcelable {
|
||||
|
||||
|
||||
private ScanFilter(String name, String deviceAddress, ParcelUuid uuid,
|
||||
ParcelUuid uuidMask, ParcelUuid serviceDataUuid,
|
||||
ParcelUuid uuidMask, ParcelUuid solicitationUuid,
|
||||
ParcelUuid solicitationUuidMask, ParcelUuid serviceDataUuid,
|
||||
byte[] serviceData, byte[] serviceDataMask,
|
||||
int manufacturerId, byte[] manufacturerData, byte[] manufacturerDataMask) {
|
||||
mDeviceName = name;
|
||||
mServiceUuid = uuid;
|
||||
mServiceUuidMask = uuidMask;
|
||||
mServiceSolicitationUuid = solicitationUuid;
|
||||
mServiceSolicitationUuidMask = solicitationUuidMask;
|
||||
mDeviceAddress = deviceAddress;
|
||||
mServiceDataUuid = serviceDataUuid;
|
||||
mServiceData = serviceData;
|
||||
@@ -113,6 +121,14 @@ public final class ScanFilter implements Parcelable {
|
||||
dest.writeParcelable(mServiceUuidMask, flags);
|
||||
}
|
||||
}
|
||||
dest.writeInt(mServiceSolicitationUuid == null ? 0 : 1);
|
||||
if (mServiceSolicitationUuid != null) {
|
||||
dest.writeParcelable(mServiceSolicitationUuid, flags);
|
||||
dest.writeInt(mServiceSolicitationUuidMask == null ? 0 : 1);
|
||||
if (mServiceSolicitationUuidMask != null) {
|
||||
dest.writeParcelable(mServiceSolicitationUuidMask, flags);
|
||||
}
|
||||
}
|
||||
dest.writeInt(mServiceDataUuid == null ? 0 : 1);
|
||||
if (mServiceDataUuid != null) {
|
||||
dest.writeParcelable(mServiceDataUuid, flags);
|
||||
@@ -171,6 +187,17 @@ public final class ScanFilter implements Parcelable {
|
||||
builder.setServiceUuid(uuid, uuidMask);
|
||||
}
|
||||
}
|
||||
if (in.readInt() == 1) {
|
||||
ParcelUuid solicitationUuid = in.readParcelable(
|
||||
ParcelUuid.class.getClassLoader());
|
||||
builder.setServiceSolicitationUuid(solicitationUuid);
|
||||
if (in.readInt() == 1) {
|
||||
ParcelUuid solicitationUuidMask = in.readParcelable(
|
||||
ParcelUuid.class.getClassLoader());
|
||||
builder.setServiceSolicitationUuid(solicitationUuid,
|
||||
solicitationUuidMask);
|
||||
}
|
||||
}
|
||||
if (in.readInt() == 1) {
|
||||
ParcelUuid servcieDataUuid =
|
||||
in.readParcelable(ParcelUuid.class.getClassLoader());
|
||||
@@ -231,6 +258,22 @@ public final class ScanFilter implements Parcelable {
|
||||
return mServiceUuidMask;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the filter set on the service Solicitation uuid.
|
||||
*/
|
||||
@Nullable
|
||||
public ParcelUuid getServiceSolicitationUuid() {
|
||||
return mServiceSolicitationUuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the filter set on the service Solicitation uuid mask.
|
||||
*/
|
||||
@Nullable
|
||||
public ParcelUuid getServiceSolicitationUuidMask() {
|
||||
return mServiceSolicitationUuidMask;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getDeviceAddress() {
|
||||
return mDeviceAddress;
|
||||
@@ -288,7 +331,7 @@ 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)) {
|
||||
|| mServiceData != null || mServiceSolicitationUuid != null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -303,6 +346,13 @@ public final class ScanFilter implements Parcelable {
|
||||
return false;
|
||||
}
|
||||
|
||||
// solicitation UUID match.
|
||||
if (mServiceSolicitationUuid != null && !matchesServiceSolicitationUuids(
|
||||
mServiceSolicitationUuid, mServiceSolicitationUuidMask,
|
||||
scanRecord.getServiceSolicitationUuids())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Service data match
|
||||
if (mServiceDataUuid != null) {
|
||||
if (!matchesPartialData(mServiceData, mServiceDataMask,
|
||||
@@ -350,6 +400,36 @@ public final class ScanFilter implements Parcelable {
|
||||
return BitUtils.maskedEquals(data, uuid, mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the solicitation uuid pattern is contained in a list of parcel uuids.
|
||||
*
|
||||
*/
|
||||
private static boolean matchesServiceSolicitationUuids(ParcelUuid solicitationUuid,
|
||||
ParcelUuid parcelSolicitationUuidMask, List<ParcelUuid> solicitationUuids) {
|
||||
if (solicitationUuid == null) {
|
||||
return true;
|
||||
}
|
||||
if (solicitationUuids == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (ParcelUuid parcelSolicitationUuid : solicitationUuids) {
|
||||
UUID solicitationUuidMask = parcelSolicitationUuidMask == null
|
||||
? null : parcelSolicitationUuidMask.getUuid();
|
||||
if (matchesServiceUuid(solicitationUuid.getUuid(), solicitationUuidMask,
|
||||
parcelSolicitationUuid.getUuid())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the solicitation uuid pattern matches the particular service solicitation uuid.
|
||||
private static boolean matchesServiceSolicitationUuid(UUID solicitationUuid,
|
||||
UUID solicitationUuidMask, UUID data) {
|
||||
return BitUtils.maskedEquals(data, solicitationUuid, solicitationUuidMask);
|
||||
}
|
||||
|
||||
// Check whether the data pattern matches the parsed data.
|
||||
private boolean matchesPartialData(byte[] data, byte[] dataMask, byte[] parsedData) {
|
||||
if (parsedData == null || parsedData.length < data.length) {
|
||||
@@ -376,6 +456,8 @@ public final class ScanFilter implements Parcelable {
|
||||
return "BluetoothLeScanFilter [mDeviceName=" + mDeviceName + ", mDeviceAddress="
|
||||
+ mDeviceAddress
|
||||
+ ", mUuid=" + mServiceUuid + ", mUuidMask=" + mServiceUuidMask
|
||||
+ ", mServiceSolicitationUuid=" + mServiceSolicitationUuid
|
||||
+ ", mServiceSolicitationUuidMask=" + mServiceSolicitationUuidMask
|
||||
+ ", mServiceDataUuid=" + Objects.toString(mServiceDataUuid) + ", mServiceData="
|
||||
+ Arrays.toString(mServiceData) + ", mServiceDataMask="
|
||||
+ Arrays.toString(mServiceDataMask) + ", mManufacturerId=" + mManufacturerId
|
||||
@@ -391,7 +473,8 @@ public final class ScanFilter implements Parcelable {
|
||||
mServiceDataUuid,
|
||||
Arrays.hashCode(mServiceData),
|
||||
Arrays.hashCode(mServiceDataMask),
|
||||
mServiceUuid, mServiceUuidMask);
|
||||
mServiceUuid, mServiceUuidMask,
|
||||
mServiceSolicitationUuid, mServiceSolicitationUuidMask);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -412,7 +495,10 @@ public final class ScanFilter implements Parcelable {
|
||||
&& Objects.deepEquals(mServiceData, other.mServiceData)
|
||||
&& Objects.deepEquals(mServiceDataMask, other.mServiceDataMask)
|
||||
&& Objects.equals(mServiceUuid, other.mServiceUuid)
|
||||
&& Objects.equals(mServiceUuidMask, other.mServiceUuidMask);
|
||||
&& Objects.equals(mServiceUuidMask, other.mServiceUuidMask)
|
||||
&& Objects.equals(mServiceSolicitationUuid, other.mServiceSolicitationUuid)
|
||||
&& Objects.equals(mServiceSolicitationUuidMask,
|
||||
other.mServiceSolicitationUuidMask);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -435,6 +521,9 @@ public final class ScanFilter implements Parcelable {
|
||||
private ParcelUuid mServiceUuid;
|
||||
private ParcelUuid mUuidMask;
|
||||
|
||||
private ParcelUuid mServiceSolicitationUuid;
|
||||
private ParcelUuid mServiceSolicitationUuidMask;
|
||||
|
||||
private ParcelUuid mServiceDataUuid;
|
||||
private byte[] mServiceData;
|
||||
private byte[] mServiceDataMask;
|
||||
@@ -493,6 +582,36 @@ public final class ScanFilter implements Parcelable {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set filter on service solicitation uuid.
|
||||
*/
|
||||
public Builder setServiceSolicitationUuid(ParcelUuid serviceSolicitationUuid) {
|
||||
mServiceSolicitationUuid = serviceSolicitationUuid;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set filter on partial service Solicitation uuid. The {@code SolicitationUuidMask} is the
|
||||
* bit mask for the {@code serviceSolicitationUuid}. Set any bit in the mask to 1 to
|
||||
* indicate a match is needed for the bit in {@code serviceSolicitationUuid}, and 0 to
|
||||
* ignore that bit.
|
||||
*
|
||||
* @throws IllegalArgumentException If {@code serviceSolicitationUuid} is {@code null} but
|
||||
* {@code serviceSolicitationUuidMask} is not {@code null}.
|
||||
*/
|
||||
public Builder setServiceSolicitationUuid(ParcelUuid serviceSolicitationUuid,
|
||||
ParcelUuid solicitationUuidMask) {
|
||||
if (mServiceSolicitationUuidMask != null && mServiceSolicitationUuid == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"SolicitationUuid is null while SolicitationUuidMask is not null!");
|
||||
}
|
||||
mServiceSolicitationUuid = serviceSolicitationUuid;
|
||||
mServiceSolicitationUuidMask = solicitationUuidMask;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set filtering on service data.
|
||||
*
|
||||
@@ -598,7 +717,8 @@ public final class ScanFilter implements Parcelable {
|
||||
*/
|
||||
public ScanFilter build() {
|
||||
return new ScanFilter(mDeviceName, mDeviceAddress,
|
||||
mServiceUuid, mUuidMask,
|
||||
mServiceUuid, mUuidMask, mServiceSolicitationUuid,
|
||||
mServiceSolicitationUuidMask,
|
||||
mServiceDataUuid, mServiceData, mServiceDataMask,
|
||||
mManufacturerId, mManufacturerData, mManufacturerDataMask);
|
||||
}
|
||||
|
||||
@@ -51,6 +51,9 @@ public final class ScanRecord {
|
||||
private static final int DATA_TYPE_SERVICE_DATA_16_BIT = 0x16;
|
||||
private static final int DATA_TYPE_SERVICE_DATA_32_BIT = 0x20;
|
||||
private static final int DATA_TYPE_SERVICE_DATA_128_BIT = 0x21;
|
||||
private static final int DATA_TYPE_SERVICE_SOLICITATION_UUIDS_16_BIT = 0x14;
|
||||
private static final int DATA_TYPE_SERVICE_SOLICITATION_UUIDS_32_BIT = 0x1F;
|
||||
private static final int DATA_TYPE_SERVICE_SOLICITATION_UUIDS_128_BIT = 0x15;
|
||||
private static final int DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF;
|
||||
|
||||
// Flags of the advertising data.
|
||||
@@ -58,6 +61,8 @@ public final class ScanRecord {
|
||||
|
||||
@Nullable
|
||||
private final List<ParcelUuid> mServiceUuids;
|
||||
@Nullable
|
||||
private final List<ParcelUuid> mServiceSolicitationUuids;
|
||||
|
||||
private final SparseArray<byte[]> mManufacturerSpecificData;
|
||||
|
||||
@@ -88,6 +93,15 @@ public final class ScanRecord {
|
||||
return mServiceUuids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of service solicitation UUIDs within the advertisement that are used to
|
||||
* identify the Bluetooth GATT services.
|
||||
*/
|
||||
@Nullable
|
||||
public List<ParcelUuid> getServiceSolicitationUuids() {
|
||||
return mServiceSolicitationUuids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a sparse array of manufacturer identifier and its corresponding manufacturer specific
|
||||
* data.
|
||||
@@ -151,10 +165,12 @@ public final class ScanRecord {
|
||||
}
|
||||
|
||||
private ScanRecord(List<ParcelUuid> serviceUuids,
|
||||
List<ParcelUuid> serviceSolicitationUuids,
|
||||
SparseArray<byte[]> manufacturerData,
|
||||
Map<ParcelUuid, byte[]> serviceData,
|
||||
int advertiseFlags, int txPowerLevel,
|
||||
String localName, byte[] bytes) {
|
||||
mServiceSolicitationUuids = serviceSolicitationUuids;
|
||||
mServiceUuids = serviceUuids;
|
||||
mManufacturerSpecificData = manufacturerData;
|
||||
mServiceData = serviceData;
|
||||
@@ -184,6 +200,7 @@ public final class ScanRecord {
|
||||
int currentPos = 0;
|
||||
int advertiseFlag = -1;
|
||||
List<ParcelUuid> serviceUuids = new ArrayList<ParcelUuid>();
|
||||
List<ParcelUuid> serviceSolicitationUuids = new ArrayList<ParcelUuid>();
|
||||
String localName = null;
|
||||
int txPowerLevel = Integer.MIN_VALUE;
|
||||
|
||||
@@ -220,6 +237,18 @@ public final class ScanRecord {
|
||||
parseServiceUuid(scanRecord, currentPos, dataLength,
|
||||
BluetoothUuid.UUID_BYTES_128_BIT, serviceUuids);
|
||||
break;
|
||||
case DATA_TYPE_SERVICE_SOLICITATION_UUIDS_16_BIT:
|
||||
parseServiceSolicitationUuid(scanRecord, currentPos, dataLength,
|
||||
BluetoothUuid.UUID_BYTES_16_BIT, serviceSolicitationUuids);
|
||||
break;
|
||||
case DATA_TYPE_SERVICE_SOLICITATION_UUIDS_32_BIT:
|
||||
parseServiceSolicitationUuid(scanRecord, currentPos, dataLength,
|
||||
BluetoothUuid.UUID_BYTES_32_BIT, serviceSolicitationUuids);
|
||||
break;
|
||||
case DATA_TYPE_SERVICE_SOLICITATION_UUIDS_128_BIT:
|
||||
parseServiceSolicitationUuid(scanRecord, currentPos, dataLength,
|
||||
BluetoothUuid.UUID_BYTES_128_BIT, serviceSolicitationUuids);
|
||||
break;
|
||||
case DATA_TYPE_LOCAL_NAME_SHORT:
|
||||
case DATA_TYPE_LOCAL_NAME_COMPLETE:
|
||||
localName = new String(
|
||||
@@ -265,19 +294,23 @@ public final class ScanRecord {
|
||||
if (serviceUuids.isEmpty()) {
|
||||
serviceUuids = null;
|
||||
}
|
||||
return new ScanRecord(serviceUuids, manufacturerData, serviceData,
|
||||
advertiseFlag, txPowerLevel, localName, scanRecord);
|
||||
if (serviceSolicitationUuids.isEmpty()) {
|
||||
serviceSolicitationUuids = null;
|
||||
}
|
||||
return new ScanRecord(serviceUuids, serviceSolicitationUuids, manufacturerData,
|
||||
serviceData, advertiseFlag, txPowerLevel, localName, 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, -1, Integer.MIN_VALUE, null, scanRecord);
|
||||
return new ScanRecord(null, null, null, null, -1, Integer.MIN_VALUE, null, scanRecord);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ScanRecord [mAdvertiseFlags=" + mAdvertiseFlags + ", mServiceUuids=" + mServiceUuids
|
||||
+ ", mServiceSolicitationUuids=" + mServiceSolicitationUuids
|
||||
+ ", mManufacturerSpecificData=" + BluetoothLeUtils.toString(
|
||||
mManufacturerSpecificData)
|
||||
+ ", mServiceData=" + BluetoothLeUtils.toString(mServiceData)
|
||||
@@ -297,6 +330,20 @@ public final class ScanRecord {
|
||||
return currentPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse service Solicitation UUIDs.
|
||||
*/
|
||||
private static int parseServiceSolicitationUuid(byte[] scanRecord, int currentPos,
|
||||
int dataLength, int uuidLength, List<ParcelUuid> serviceSolicitationUuids) {
|
||||
while (dataLength > 0) {
|
||||
byte[] uuidBytes = extractBytes(scanRecord, currentPos, uuidLength);
|
||||
serviceSolicitationUuids.add(BluetoothUuid.parseUuidFrom(uuidBytes));
|
||||
dataLength -= uuidLength;
|
||||
currentPos += uuidLength;
|
||||
}
|
||||
return currentPos;
|
||||
}
|
||||
|
||||
// Helper method to extract bytes from byte array.
|
||||
private static byte[] extractBytes(byte[] scanRecord, int start, int length) {
|
||||
byte[] bytes = new byte[length];
|
||||
|
||||
Reference in New Issue
Block a user