diff --git a/android/src/main/kotlin/com/navideck/universal_ble/UniversalBle.g.kt b/android/src/main/kotlin/com/navideck/universal_ble/UniversalBle.g.kt index bf74181d..14007452 100644 --- a/android/src/main/kotlin/com/navideck/universal_ble/UniversalBle.g.kt +++ b/android/src/main/kotlin/com/navideck/universal_ble/UniversalBle.g.kt @@ -970,18 +970,29 @@ data class UniversalManufacturerData ( /** Generated class from Pigeon that represents data sent in messages. */ data class PeripheralAndroidOptions ( - val addManufacturerDataInScanResponse: Boolean? = null + val addManufacturerDataInScanResponse: Boolean? = null, + /** + * Put advertised service UUIDs in the scan response instead of the primary + * advertisement. The Android primary advertisement is capped at 31 bytes; + * a 128-bit service UUID (18 bytes) plus a device name can overflow it + * ("ADVERTISE_FAILED_DATA_TOO_LARGE"). Moving the UUIDs to the scan + * response keeps them discoverable to active scanners while freeing the + * primary packet. + */ + val addServicesInScanResponse: Boolean? = null ) { companion object { fun fromList(pigeonVar_list: List): PeripheralAndroidOptions { val addManufacturerDataInScanResponse = pigeonVar_list[0] as Boolean? - return PeripheralAndroidOptions(addManufacturerDataInScanResponse) + val addServicesInScanResponse = pigeonVar_list[1] as Boolean? + return PeripheralAndroidOptions(addManufacturerDataInScanResponse, addServicesInScanResponse) } } fun toList(): List { return listOf( addManufacturerDataInScanResponse, + addServicesInScanResponse, ) } override fun equals(other: Any?): Boolean { @@ -992,12 +1003,13 @@ data class PeripheralAndroidOptions ( return true } val other = other as PeripheralAndroidOptions - return UniversalBlePigeonUtils.deepEquals(this.addManufacturerDataInScanResponse, other.addManufacturerDataInScanResponse) + return UniversalBlePigeonUtils.deepEquals(this.addManufacturerDataInScanResponse, other.addManufacturerDataInScanResponse) && UniversalBlePigeonUtils.deepEquals(this.addServicesInScanResponse, other.addServicesInScanResponse) } override fun hashCode(): Int { var result = javaClass.hashCode() result = 31 * result + UniversalBlePigeonUtils.deepHash(this.addManufacturerDataInScanResponse) + result = 31 * result + UniversalBlePigeonUtils.deepHash(this.addServicesInScanResponse) return result } } diff --git a/android/src/main/kotlin/com/navideck/universal_ble/UniversalBlePeripheralPlugin.kt b/android/src/main/kotlin/com/navideck/universal_ble/UniversalBlePeripheralPlugin.kt index c222c96d..7a4ec148 100644 --- a/android/src/main/kotlin/com/navideck/universal_ble/UniversalBlePeripheralPlugin.kt +++ b/android/src/main/kotlin/com/navideck/universal_ble/UniversalBlePeripheralPlugin.kt @@ -144,12 +144,16 @@ class UniversalBlePeripheralPlugin( .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY) .build() + val addServicesInScanResponse = + platformConfig?.android?.addServicesInScanResponse == true val advertiseDataBuilder = AdvertiseData.Builder() .setIncludeTxPowerLevel(false) .setIncludeDeviceName(localName != null) val scanResponseBuilder = AdvertiseData.Builder() .setIncludeTxPowerLevel(false) - .setIncludeDeviceName(localName != null) +// Device name is already included in the primary advertisement when localName != null. +// Keeping it out of the scan response saves space for manufacturer data / service UUIDs. +.setIncludeDeviceName(false) val addManufacturerDataInScanResponse = platformConfig?.android?.addManufacturerDataInScanResponse == true @@ -166,7 +170,8 @@ class UniversalBlePeripheralPlugin( ) } } - services.forEach { advertiseDataBuilder.addServiceUuid(ParcelUuid.fromString(it)) } + val servicesBuilder = if (addServicesInScanResponse) scanResponseBuilder else advertiseDataBuilder + services.forEach { servicesBuilder.addServiceUuid(ParcelUuid.fromString(it)) } bluetoothLeAdvertiser?.startAdvertising( advertiseSettings, diff --git a/darwin/universal_ble/Sources/universal_ble/UniversalBle.g.swift b/darwin/universal_ble/Sources/universal_ble/UniversalBle.g.swift index 88409a42..18d4efb6 100644 --- a/darwin/universal_ble/Sources/universal_ble/UniversalBle.g.swift +++ b/darwin/universal_ble/Sources/universal_ble/UniversalBle.g.swift @@ -845,31 +845,42 @@ struct UniversalManufacturerData: Hashable { /// Generated class from Pigeon that represents data sent in messages. struct PeripheralAndroidOptions: Hashable { var addManufacturerDataInScanResponse: Bool? = nil + /// Put advertised service UUIDs in the scan response instead of the primary + /// advertisement. The Android primary advertisement is capped at 31 bytes; + /// a 128-bit service UUID (18 bytes) plus a device name can overflow it + /// ("ADVERTISE_FAILED_DATA_TOO_LARGE"). Moving the UUIDs to the scan + /// response keeps them discoverable to active scanners while freeing the + /// primary packet. + var addServicesInScanResponse: Bool? = nil // swift-format-ignore: AlwaysUseLowerCamelCase static func fromList(_ pigeonVar_list: [Any?]) -> PeripheralAndroidOptions? { let addManufacturerDataInScanResponse: Bool? = nilOrValue(pigeonVar_list[0]) + let addServicesInScanResponse: Bool? = nilOrValue(pigeonVar_list[1]) return PeripheralAndroidOptions( - addManufacturerDataInScanResponse: addManufacturerDataInScanResponse + addManufacturerDataInScanResponse: addManufacturerDataInScanResponse, + addServicesInScanResponse: addServicesInScanResponse ) } func toList() -> [Any?] { return [ - addManufacturerDataInScanResponse + addManufacturerDataInScanResponse, + addServicesInScanResponse, ] } static func == (lhs: PeripheralAndroidOptions, rhs: PeripheralAndroidOptions) -> Bool { if Swift.type(of: lhs) != Swift.type(of: rhs) { return false } - return deepEqualsUniversalBle(lhs.addManufacturerDataInScanResponse, rhs.addManufacturerDataInScanResponse) + return deepEqualsUniversalBle(lhs.addManufacturerDataInScanResponse, rhs.addManufacturerDataInScanResponse) && deepEqualsUniversalBle(lhs.addServicesInScanResponse, rhs.addServicesInScanResponse) } func hash(into hasher: inout Hasher) { hasher.combine("PeripheralAndroidOptions") deepHashUniversalBle(value: addManufacturerDataInScanResponse, hasher: &hasher) + deepHashUniversalBle(value: addServicesInScanResponse, hasher: &hasher) } } diff --git a/lib/src/universal_ble.g.dart b/lib/src/universal_ble.g.dart index 09632f17..ec822729 100644 --- a/lib/src/universal_ble.g.dart +++ b/lib/src/universal_ble.g.dart @@ -834,12 +834,26 @@ class UniversalManufacturerData { } class PeripheralAndroidOptions { - PeripheralAndroidOptions({this.addManufacturerDataInScanResponse}); + PeripheralAndroidOptions({ + this.addManufacturerDataInScanResponse, + this.addServicesInScanResponse, + }); bool? addManufacturerDataInScanResponse; + /// Put advertised service UUIDs in the scan response instead of the primary + /// advertisement. The Android primary advertisement is capped at 31 bytes; + /// a 128-bit service UUID (18 bytes) plus a device name can overflow it + /// ("ADVERTISE_FAILED_DATA_TOO_LARGE"). Moving the UUIDs to the scan + /// response keeps them discoverable to active scanners while freeing the + /// primary packet. + bool? addServicesInScanResponse; + List _toList() { - return [addManufacturerDataInScanResponse]; + return [ + addManufacturerDataInScanResponse, + addServicesInScanResponse, + ]; } Object encode() { @@ -850,6 +864,7 @@ class PeripheralAndroidOptions { result as List; return PeripheralAndroidOptions( addManufacturerDataInScanResponse: result[0] as bool?, + addServicesInScanResponse: result[1] as bool?, ); } @@ -864,9 +879,10 @@ class PeripheralAndroidOptions { return true; } return _deepEquals( - addManufacturerDataInScanResponse, - other.addManufacturerDataInScanResponse, - ); + addManufacturerDataInScanResponse, + other.addManufacturerDataInScanResponse, + ) && + _deepEquals(addServicesInScanResponse, other.addServicesInScanResponse); } @override diff --git a/pigeon/universal_ble.dart b/pigeon/universal_ble.dart index 4e02845a..c3056a39 100644 --- a/pigeon/universal_ble.dart +++ b/pigeon/universal_ble.dart @@ -261,7 +261,18 @@ class UniversalManufacturerData { class PeripheralAndroidOptions { bool? addManufacturerDataInScanResponse; - PeripheralAndroidOptions({this.addManufacturerDataInScanResponse}); + + /// Put advertised service UUIDs in the scan response instead of the primary + /// advertisement. The Android primary advertisement and scan response are + /// both capped at 31 bytes. A 128-bit service UUID (18 bytes) plus a + /// device name can overflow the primary packet. + /// Note: If this is enabled with `addManufacturerDataInScanResponse`, ensure + /// the combined data fits within the scan response's 31-byte limit. + bool? addServicesInScanResponse; + PeripheralAndroidOptions({ + this.addManufacturerDataInScanResponse, + this.addServicesInScanResponse, + }); } class PeripheralPlatformConfig { diff --git a/windows/src/generated/universal_ble.g.cpp b/windows/src/generated/universal_ble.g.cpp index 03e4caab..981a6c83 100644 --- a/windows/src/generated/universal_ble.g.cpp +++ b/windows/src/generated/universal_ble.g.cpp @@ -1184,8 +1184,11 @@ size_t PigeonInternalDeepHash(const UniversalManufacturerData& v) { PeripheralAndroidOptions::PeripheralAndroidOptions() {} -PeripheralAndroidOptions::PeripheralAndroidOptions(const bool* add_manufacturer_data_in_scan_response) - : add_manufacturer_data_in_scan_response_(add_manufacturer_data_in_scan_response ? std::optional(*add_manufacturer_data_in_scan_response) : std::nullopt) {} +PeripheralAndroidOptions::PeripheralAndroidOptions( + const bool* add_manufacturer_data_in_scan_response, + const bool* add_services_in_scan_response) + : add_manufacturer_data_in_scan_response_(add_manufacturer_data_in_scan_response ? std::optional(*add_manufacturer_data_in_scan_response) : std::nullopt), + add_services_in_scan_response_(add_services_in_scan_response ? std::optional(*add_services_in_scan_response) : std::nullopt) {} const bool* PeripheralAndroidOptions::add_manufacturer_data_in_scan_response() const { return add_manufacturer_data_in_scan_response_ ? &(*add_manufacturer_data_in_scan_response_) : nullptr; @@ -1200,10 +1203,24 @@ void PeripheralAndroidOptions::set_add_manufacturer_data_in_scan_response(bool v } +const bool* PeripheralAndroidOptions::add_services_in_scan_response() const { + return add_services_in_scan_response_ ? &(*add_services_in_scan_response_) : nullptr; +} + +void PeripheralAndroidOptions::set_add_services_in_scan_response(const bool* value_arg) { + add_services_in_scan_response_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void PeripheralAndroidOptions::set_add_services_in_scan_response(bool value_arg) { + add_services_in_scan_response_ = value_arg; +} + + EncodableList PeripheralAndroidOptions::ToEncodableList() const { EncodableList list; - list.reserve(1); + list.reserve(2); list.push_back(add_manufacturer_data_in_scan_response_ ? EncodableValue(*add_manufacturer_data_in_scan_response_) : EncodableValue()); + list.push_back(add_services_in_scan_response_ ? EncodableValue(*add_services_in_scan_response_) : EncodableValue()); return list; } @@ -1213,11 +1230,15 @@ PeripheralAndroidOptions PeripheralAndroidOptions::FromEncodableList(const Encod if (!encodable_add_manufacturer_data_in_scan_response.IsNull()) { decoded.set_add_manufacturer_data_in_scan_response(std::get(encodable_add_manufacturer_data_in_scan_response)); } + auto& encodable_add_services_in_scan_response = list[1]; + if (!encodable_add_services_in_scan_response.IsNull()) { + decoded.set_add_services_in_scan_response(std::get(encodable_add_services_in_scan_response)); + } return decoded; } bool PeripheralAndroidOptions::operator==(const PeripheralAndroidOptions& other) const { - return PigeonInternalDeepEquals(add_manufacturer_data_in_scan_response_, other.add_manufacturer_data_in_scan_response_); + return PigeonInternalDeepEquals(add_manufacturer_data_in_scan_response_, other.add_manufacturer_data_in_scan_response_) && PigeonInternalDeepEquals(add_services_in_scan_response_, other.add_services_in_scan_response_); } bool PeripheralAndroidOptions::operator!=(const PeripheralAndroidOptions& other) const { @@ -1227,6 +1248,7 @@ bool PeripheralAndroidOptions::operator!=(const PeripheralAndroidOptions& other) size_t PeripheralAndroidOptions::Hash() const { size_t result = 1; result = result * 31 + PigeonInternalDeepHash(add_manufacturer_data_in_scan_response_); + result = result * 31 + PigeonInternalDeepHash(add_services_in_scan_response_); return result; } diff --git a/windows/src/generated/universal_ble.g.h b/windows/src/generated/universal_ble.g.h index dc43c599..d34fb1a7 100644 --- a/windows/src/generated/universal_ble.g.h +++ b/windows/src/generated/universal_ble.g.h @@ -729,12 +729,24 @@ class PeripheralAndroidOptions { PeripheralAndroidOptions(); // Constructs an object setting all fields. - explicit PeripheralAndroidOptions(const bool* add_manufacturer_data_in_scan_response); + explicit PeripheralAndroidOptions( + const bool* add_manufacturer_data_in_scan_response, + const bool* add_services_in_scan_response); const bool* add_manufacturer_data_in_scan_response() const; void set_add_manufacturer_data_in_scan_response(const bool* value_arg); void set_add_manufacturer_data_in_scan_response(bool value_arg); + // Put advertised service UUIDs in the scan response instead of the primary + // advertisement. The Android primary advertisement is capped at 31 bytes; + // a 128-bit service UUID (18 bytes) plus a device name can overflow it + // ("ADVERTISE_FAILED_DATA_TOO_LARGE"). Moving the UUIDs to the scan + // response keeps them discoverable to active scanners while freeing the + // primary packet. + const bool* add_services_in_scan_response() const; + void set_add_services_in_scan_response(const bool* value_arg); + void set_add_services_in_scan_response(bool value_arg); + bool operator==(const PeripheralAndroidOptions& other) const; bool operator!=(const PeripheralAndroidOptions& other) const; /// Returns a hash code value for the object. This method is supported for the benefit of hash tables. @@ -750,6 +762,7 @@ class PeripheralAndroidOptions { friend class UniversalBlePeripheralCallback; friend class PigeonInternalCodecSerializer; std::optional add_manufacturer_data_in_scan_response_; + std::optional add_services_in_scan_response_; };