Merge "BLE: Add service solicitation uuid feature in scan filter"
This commit is contained in:
@@ -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