diff options
author | Jakub Pawlowski <jpawlowski@google.com> | 2018-08-29 11:26:51 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2018-08-29 11:26:51 +0000 |
commit | 13d6540956bc0aff2e08b785d90ad3c91d81663d (patch) | |
tree | e608442280a4415504b10da7b2d5c7eed3fe5a2c | |
parent | 17115bdb02c8b590f3317225ae8adcbc24e3da1f (diff) | |
parent | 1555eae7fae2456dad565db3bcb7e68758f253e2 (diff) | |
download | frameworks_base-13d6540956bc0aff2e08b785d90ad3c91d81663d.tar.gz frameworks_base-13d6540956bc0aff2e08b785d90ad3c91d81663d.tar.bz2 frameworks_base-13d6540956bc0aff2e08b785d90ad3c91d81663d.zip |
Merge "BLE: Add service solicitation uuid feature in scan filter"
-rw-r--r-- | api/current.txt | 5 | ||||
-rw-r--r-- | core/java/android/bluetooth/le/ScanFilter.java | 130 | ||||
-rw-r--r-- | core/java/android/bluetooth/le/ScanRecord.java | 53 |
3 files changed, 180 insertions, 8 deletions
diff --git a/api/current.txt b/api/current.txt index 3c7dcc8bdd3..0ef9d30bc6e 100644 --- a/api/current.txt +++ b/api/current.txt @@ -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(); } diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java index c3fae7d470a..c5d435b7613 100644 --- a/core/java/android/bluetooth/le/ScanFilter.java +++ b/core/java/android/bluetooth/le/ScanFilter.java @@ -58,6 +58,11 @@ public final class ScanFilter implements Parcelable { private final ParcelUuid mServiceUuidMask; @Nullable + private final ParcelUuid mServiceSolicitationUuid; + @Nullable + private final ParcelUuid mServiceSolicitationUuidMask; + + @Nullable private final ParcelUuid mServiceDataUuid; @Nullable private final byte[] mServiceData; @@ -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); @@ -172,6 +188,17 @@ public final class ScanFilter implements Parcelable { } } 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()); if (in.readInt() == 1) { @@ -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); } diff --git a/core/java/android/bluetooth/le/ScanRecord.java b/core/java/android/bluetooth/le/ScanRecord.java index 07ed18d90ee..7988008f03c 100644 --- a/core/java/android/bluetooth/le/ScanRecord.java +++ b/core/java/android/bluetooth/le/ScanRecord.java @@ -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; @@ -89,6 +94,15 @@ public final class ScanRecord { } /** + * 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]; |