diff --git a/services/ble/Gap.cpp b/services/ble/Gap.cpp index 8ff3db9e5..97c4af770 100644 --- a/services/ble/Gap.cpp +++ b/services/ble/Gap.cpp @@ -1,462 +1,462 @@ -#include "services/ble/Gap.hpp" -#include "infra/stream/ByteInputStream.hpp" -#include "infra/util/BoundedString.hpp" -#include "infra/util/LogAndAbort.hpp" -#include "infra/util/MemoryRange.hpp" -#include - -namespace -{ - void AddHeader(infra::BoundedVector& payload, std::size_t length, services::GapAdvertisementDataType type) - { - payload.push_back(static_cast(length + 1)); - payload.push_back(static_cast(type)); - } - - void AddData(infra::BoundedVector& payload, infra::ConstByteRange data) - { - payload.insert(payload.end(), data.begin(), data.end()); - } -} - -namespace services -{ - void GapPairingDecorator::AuthenticationRequired(bool isNumericComparison, uint32_t passkey) - { - GapPairing::NotifyObservers([&passkey, &isNumericComparison](auto& obs) - { - obs.AuthenticationRequired(isNumericComparison, passkey); - }); - } - - void GapPairingDecorator::PairingResult(bool pairedSuccessfully, PairingFailedReason pairingFailedReason) - { - GapPairing::NotifyObservers([&pairedSuccessfully, &pairingFailedReason](auto& obs) - { - obs.PairingResult(pairedSuccessfully, pairingFailedReason); - }); - } - - void GapPairingDecorator::OutOfBandDataGenerated(const GapOutOfBandData& outOfBandData) - { - GapPairing::NotifyObservers([outOfBandData](auto& obs) - { - obs.OutOfBandDataGenerated(outOfBandData); - }); - } - - void GapPairingDecorator::PairAndBond() - { - GapPairingObserver::Subject().PairAndBond(); - } - - void GapPairingDecorator::AllowPairing(bool allow) - { - GapPairingObserver::Subject().AllowPairing(allow); - } - - void GapPairingDecorator::SetSecurityMode(SecurityMode mode, SecurityLevel level) - { - GapPairingObserver::Subject().SetSecurityMode(mode, level); - } - - void GapPairingDecorator::SetIoCapabilities(IoCapabilities caps) - { - GapPairingObserver::Subject().SetIoCapabilities(caps); - } - - void GapPairingDecorator::GenerateOutOfBandData() - { - GapPairingObserver::Subject().GenerateOutOfBandData(); - } - - void GapPairingDecorator::SetOutOfBandData(const GapOutOfBandData& outOfBandData) - { - GapPairingObserver::Subject().SetOutOfBandData(outOfBandData); - } - - void GapPairingDecorator::AuthenticateWithPasskey(uint32_t passkey) - { - GapPairingObserver::Subject().AuthenticateWithPasskey(passkey); - } - - void GapPairingDecorator::NumericComparisonConfirm(bool accept) - { - GapPairingObserver::Subject().NumericComparisonConfirm(accept); - } - - void GapBondingDecorator::NumberOfBondsChanged(std::size_t nrBonds) - { - GapBonding::NotifyObservers([&nrBonds](auto& obs) - { - obs.NumberOfBondsChanged(nrBonds); - }); - } - - void GapBondingDecorator::RemoveAllBonds() - { - GapBondingObserver::Subject().RemoveAllBonds(); - } - - void GapBondingDecorator::RemoveOldestBond() - { - GapBondingObserver::Subject().RemoveOldestBond(); - } - - void GapBondingDecorator::RemoveBondWithAddress(GapAddress gapAddress) - { - GapBondingObserver::Subject().RemoveBondWithAddress(gapAddress); - } - - std::size_t GapBondingDecorator::GetMaxNumberOfBonds() const - { - return GapBondingObserver::Subject().GetMaxNumberOfBonds(); - } - - std::size_t GapBondingDecorator::GetNumberOfBonds() const - { - return GapBondingObserver::Subject().GetNumberOfBonds(); - } - - bool GapBondingDecorator::IsDeviceBonded(hal::MacAddress address, GapDeviceAddressType addressType) const - { - return GapBondingObserver::Subject().IsDeviceBonded(address, addressType); - } - - infra::MemoryRange GapBondingDecorator::GetBondList() const - { - return GapBondingObserver::Subject().GetBondList(); - } - - void GapPeripheralDecorator::StateChanged(GapState state) - { - GapPeripheral::NotifyObservers([&state](auto& obs) - { - obs.StateChanged(state); - }); - } - - GapAddress GapPeripheralDecorator::GetAddress() const - { - return GapPeripheralObserver::Subject().GetAddress(); - } - - GapAddress GapPeripheralDecorator::GetIdentityAddress() const - { - return GapPeripheralObserver::Subject().GetIdentityAddress(); - } - - void GapPeripheralDecorator::SetAdvertisementData(infra::ConstByteRange data) - { - GapPeripheralObserver::Subject().SetAdvertisementData(data); - } - - infra::ConstByteRange GapPeripheralDecorator::GetAdvertisementData() const - { - return GapPeripheralObserver::Subject().GetAdvertisementData(); - } - - void GapPeripheralDecorator::SetScanResponseData(infra::ConstByteRange data) - { - GapPeripheralObserver::Subject().SetScanResponseData(data); - } - - infra::ConstByteRange GapPeripheralDecorator::GetScanResponseData() const - { - return GapPeripheralObserver::Subject().GetScanResponseData(); - } - - void GapPeripheralDecorator::Advertise(GapAdvertisementType type, AdvertisementIntervalMultiplier multiplier) - { - GapPeripheralObserver::Subject().Advertise(type, multiplier); - } - - void GapPeripheralDecorator::Standby() - { - GapPeripheralObserver::Subject().Standby(); - } - - void GapPeripheralDecorator::SetConnectionParameters(const services::GapConnectionParameters& connParam) - { - GapPeripheralObserver::Subject().SetConnectionParameters(connParam); - } - - void GapCentralDecorator::DeviceDiscovered(const GapAdvertisingReport& deviceDiscovered) - { - GapCentralObserver::SubjectType::NotifyObservers([&deviceDiscovered](auto& obs) - { - obs.DeviceDiscovered(deviceDiscovered); - }); - } - - void GapCentralDecorator::StateChanged(GapState state) - { - GapCentralObserver::SubjectType::NotifyObservers([&state](auto& obs) - { - obs.StateChanged(state); - }); - } - - void GapCentralDecorator::Connect(hal::MacAddress macAddress, GapDeviceAddressType addressType, infra::Duration initiatingTimeout) - { - GapCentralObserver::Subject().Connect(macAddress, addressType, initiatingTimeout); - } - - void GapCentralDecorator::Standby() - { - GapCentralObserver::Subject().Standby(); - } - - void GapCentralDecorator::SetIdentityAddress(hal::MacAddress macAddress, GapDeviceAddressType addressType) - { - GapCentralObserver::Subject().SetIdentityAddress(macAddress, addressType); - } - - void GapCentralDecorator::StartDeviceDiscovery() - { - GapCentralObserver::Subject().StartDeviceDiscovery(); - } - - std::optional GapCentralDecorator::ResolvePrivateAddress(hal::MacAddress address) const - { - return GapCentralObserver::Subject().ResolvePrivateAddress(address); - } - - void GapCentralDecorator::SetPrivacyMode(bool enabled) - { - GapCentralObserver::Subject().SetPrivacyMode(enabled); - } - - GapAdvertisingDataParser::GapAdvertisingDataParser(infra::ConstByteRange data) - : data(data) - {} - - infra::ConstByteRange GapAdvertisingDataParser::LocalName() const - { - auto localName = ParserAdvertisingData(GapAdvertisementDataType::completeLocalName); - - if (localName.empty()) - return ParserAdvertisingData(GapAdvertisementDataType::shortenedLocalName); - else - return localName; - } - - std::optional> GapAdvertisingDataParser::ManufacturerSpecificData() const - { - infra::ByteInputStream stream(ParserAdvertisingData(GapAdvertisementDataType::manufacturerSpecificData), infra::softFail); - auto manufacturerCode = stream.Extract(); - auto manufacturerData = stream.Reader().Remaining(); - - if (stream.Failed()) - return std::nullopt; - - return std::make_optional(std::make_pair(manufacturerCode, manufacturerData)); - } - - std::optional GapAdvertisingDataParser::Flags() const - { - auto flagsData = ParserAdvertisingData(GapAdvertisementDataType::flags); - - if (flagsData.empty()) - return std::nullopt; - - if (flagsData.size() != 1) - return std::nullopt; - - return std::make_optional(static_cast(flagsData[0])); - } - - infra::MemoryRange GapAdvertisingDataParser::CompleteListOf16BitUuids() const - { - auto uuidData = ParserAdvertisingData(GapAdvertisementDataType::completeListOf16BitUuids); - - if (uuidData.size() % sizeof(AttAttribute::Uuid16) != 0) - return {}; - - return infra::ConstCastMemoryRange(infra::ReinterpretCastMemoryRange(uuidData)); - } - - infra::MemoryRange GapAdvertisingDataParser::CompleteListOf128BitUuids() const - { - auto uuidData = ParserAdvertisingData(GapAdvertisementDataType::completeListOf128BitUuids); - - if (uuidData.size() % sizeof(AttAttribute::Uuid128) != 0) - return {}; - - return infra::ConstCastMemoryRange(infra::ReinterpretCastMemoryRange(uuidData)); - } - - std::optional GapAdvertisingDataParser::Appearance() const - { - auto appearanceData = ParserAdvertisingData(GapAdvertisementDataType::appearance); - - infra::ByteInputStream stream(appearanceData, infra::softFail); - auto appearance = stream.Extract(); - - if (stream.Failed()) - return std::nullopt; - - return std::make_optional(appearance); - } - - infra::ConstByteRange GapAdvertisingDataParser::ParserAdvertisingData(GapAdvertisementDataType type) const - { - const uint8_t lengthOffset = 0; - const uint8_t advertisingTypeOffset = 1; - const uint8_t headerSize = 2; - - infra::ConstByteRange advData = data; - - while (!advData.empty()) - { - size_t elementSize = std::min(advData[lengthOffset] + 1, advData.size()); - - auto element = infra::Head(advData, elementSize); - - if (element.size() == 1 || (element.size() != element[lengthOffset] + 1)) - return infra::ConstByteRange(); - - if (element[advertisingTypeOffset] == static_cast(type)) - return infra::DiscardHead(element, headerSize); - - advData = infra::DiscardHead(advData, element.size()); - } - - return infra::ConstByteRange(); - } - - GapAdvertisementFormatter::GapAdvertisementFormatter(infra::BoundedVector& payload) - : payload(payload) - {} - - void GapAdvertisementFormatter::AppendFlags(GapPeripheral::AdvertisementFlags flags) - { - really_assert(headerSize + sizeof(flags) <= RemainingSpaceAvailable()); - - auto flagsByte = static_cast(flags); - AddHeader(payload, sizeof(flags), GapAdvertisementDataType::flags); - AddData(payload, infra::MakeRangeFromSingleObject(flagsByte)); - } - - void GapAdvertisementFormatter::AppendCompleteLocalName(const infra::BoundedConstString& name) - { - really_assert(name.size() + headerSize <= RemainingSpaceAvailable() && name.size() > 0); - - AddHeader(payload, name.size(), GapAdvertisementDataType::completeLocalName); - AddData(payload, infra::StringAsByteRange(name)); - } - - void GapAdvertisementFormatter::AppendShortenedLocalName(const infra::BoundedConstString& name) - { - really_assert(name.size() + headerSize <= RemainingSpaceAvailable() && name.size() > 0); - - AddHeader(payload, name.size(), GapAdvertisementDataType::shortenedLocalName); - AddData(payload, infra::StringAsByteRange(name)); - } - - void GapAdvertisementFormatter::AppendManufacturerData(uint16_t manufacturerCode, infra::ConstByteRange data) - { - really_assert(data.size() + headerSize + sizeof(manufacturerCode) <= RemainingSpaceAvailable()); - - AddHeader(payload, data.size() + sizeof(manufacturerCode), GapAdvertisementDataType::manufacturerSpecificData); - AddData(payload, infra::ReinterpretCastMemoryRange(infra::MakeRangeFromSingleObject(manufacturerCode))); - AddData(payload, data); - } - - void GapAdvertisementFormatter::AppendListOfServicesUuid(infra::MemoryRange services) - { - really_assert(services.size() * sizeof(AttAttribute::Uuid16) + headerSize <= RemainingSpaceAvailable() && !services.empty()); - - AddHeader(payload, services.size() * sizeof(AttAttribute::Uuid16), GapAdvertisementDataType::completeListOf16BitUuids); - - for (const auto& service : services) - AddData(payload, infra::ReinterpretCastMemoryRange(infra::MakeRangeFromSingleObject(service))); - } - - void GapAdvertisementFormatter::AppendListOfServicesUuid(infra::MemoryRange services) - { - really_assert(services.size() * sizeof(AttAttribute::Uuid128) + headerSize <= RemainingSpaceAvailable() && !services.empty()); - - AddHeader(payload, services.size() * sizeof(AttAttribute::Uuid128), GapAdvertisementDataType::completeListOf128BitUuids); - - for (auto& service : services) - AddData(payload, infra::ReinterpretCastMemoryRange(infra::MakeRangeFromSingleObject(service))); - } - - void GapAdvertisementFormatter::AppendPublicTargetAddress(hal::MacAddress address) - { - really_assert(sizeof(address) + headerSize <= RemainingSpaceAvailable()); - - AddHeader(payload, sizeof(address), GapAdvertisementDataType::publicTargetAddress); - AddData(payload, infra::ReinterpretCastMemoryRange(infra::MakeRangeFromSingleObject(address))); - } - - void GapAdvertisementFormatter::AppendAppearance(uint16_t appearance) - { - really_assert(sizeof(appearance) + headerSize <= RemainingSpaceAvailable()); - - AddHeader(payload, sizeof(appearance), GapAdvertisementDataType::appearance); - AddData(payload, infra::ReinterpretCastMemoryRange(infra::MakeRangeFromSingleObject(appearance))); - } - - infra::ConstByteRange GapAdvertisementFormatter::FormattedAdvertisementData() const - { - return infra::MakeRange(payload); - } - - std::size_t GapAdvertisementFormatter::RemainingSpaceAvailable() const - { - return payload.max_size() - payload.size(); - } -} - -namespace infra -{ - TextOutputStream& operator<<(TextOutputStream& stream, const services::AdvertisingReportType& reportType) - { - if (reportType == services::AdvertisingReportType::advInd) - stream << "ADV_IND"; - else if (reportType == services::AdvertisingReportType::advDirectInd) - stream << "ADV_DIRECT_IND"; - else if (reportType == services::AdvertisingReportType::advScanInd) - stream << "ADV_SCAN_IND"; - else if (reportType == services::AdvertisingReportType::scanResponse) - stream << "SCAN_RESPONSE"; - else if (reportType == services::AdvertisingReportType::advNonconnInd) - stream << "ADV_NONCONN_IND"; - else - LOG_AND_ABORT_ENUM(reportType); - - return stream; - } - - TextOutputStream& operator<<(TextOutputStream& stream, const services::GapDeviceAddressType& addressType) - { - if (addressType == services::GapDeviceAddressType::publicAddress) - stream << "Public"; - else if (addressType == services::GapDeviceAddressType::randomAddress) - stream << "Random"; - else - LOG_AND_ABORT_ENUM(addressType); - - return stream; - } - - TextOutputStream& operator<<(TextOutputStream& stream, const services::GapState& state) - { - if (state == services::GapState::standby) - stream << "Standby"; - else if (state == services::GapState::scanning) - stream << "Scanning"; - else if (state == services::GapState::advertising) - stream << "Advertising"; - else if (state == services::GapState::initiating) - stream << "Initiating"; - else if (state == services::GapState::connected) - stream << "Connected"; - else - LOG_AND_ABORT_ENUM(state); - - return stream; - } -} +#include "services/ble/Gap.hpp" +#include "infra/stream/ByteInputStream.hpp" +#include "infra/util/BoundedString.hpp" +#include "infra/util/LogAndAbort.hpp" +#include "infra/util/MemoryRange.hpp" +#include + +namespace +{ + void AddHeader(infra::BoundedVector& payload, std::size_t length, services::GapAdvertisementDataType type) + { + payload.push_back(static_cast(length + 1)); + payload.push_back(static_cast(type)); + } + + void AddData(infra::BoundedVector& payload, infra::ConstByteRange data) + { + payload.insert(payload.end(), data.begin(), data.end()); + } +} + +namespace services +{ + void GapPairingDecorator::AuthenticationRequired(bool isNumericComparison, uint32_t passkey) + { + GapPairing::NotifyObservers([&passkey, &isNumericComparison](auto& obs) + { + obs.AuthenticationRequired(isNumericComparison, passkey); + }); + } + + void GapPairingDecorator::PairingResult(bool pairedSuccessfully, PairingFailedReason pairingFailedReason) + { + GapPairing::NotifyObservers([&pairedSuccessfully, &pairingFailedReason](auto& obs) + { + obs.PairingResult(pairedSuccessfully, pairingFailedReason); + }); + } + + void GapPairingDecorator::OutOfBandDataGenerated(const GapOutOfBandData& outOfBandData) + { + GapPairing::NotifyObservers([outOfBandData](auto& obs) + { + obs.OutOfBandDataGenerated(outOfBandData); + }); + } + + void GapPairingDecorator::PairAndBond() + { + GapPairingObserver::Subject().PairAndBond(); + } + + void GapPairingDecorator::AllowPairing(bool allow) + { + GapPairingObserver::Subject().AllowPairing(allow); + } + + void GapPairingDecorator::SetSecurityMode(SecurityMode mode, SecurityLevel level) + { + GapPairingObserver::Subject().SetSecurityMode(mode, level); + } + + void GapPairingDecorator::SetIoCapabilities(IoCapabilities caps) + { + GapPairingObserver::Subject().SetIoCapabilities(caps); + } + + void GapPairingDecorator::GenerateOutOfBandData() + { + GapPairingObserver::Subject().GenerateOutOfBandData(); + } + + void GapPairingDecorator::SetOutOfBandData(const GapOutOfBandData& outOfBandData) + { + GapPairingObserver::Subject().SetOutOfBandData(outOfBandData); + } + + void GapPairingDecorator::AuthenticateWithPasskey(uint32_t passkey) + { + GapPairingObserver::Subject().AuthenticateWithPasskey(passkey); + } + + void GapPairingDecorator::NumericComparisonConfirm(bool accept) + { + GapPairingObserver::Subject().NumericComparisonConfirm(accept); + } + + void GapBondingDecorator::NumberOfBondsChanged(std::size_t nrBonds) + { + GapBonding::NotifyObservers([&nrBonds](auto& obs) + { + obs.NumberOfBondsChanged(nrBonds); + }); + } + + void GapBondingDecorator::RemoveAllBonds() + { + GapBondingObserver::Subject().RemoveAllBonds(); + } + + void GapBondingDecorator::RemoveOldestBond() + { + GapBondingObserver::Subject().RemoveOldestBond(); + } + + void GapBondingDecorator::RemoveBondWithAddress(GapAddress gapAddress) + { + GapBondingObserver::Subject().RemoveBondWithAddress(gapAddress); + } + + std::size_t GapBondingDecorator::GetMaxNumberOfBonds() const + { + return GapBondingObserver::Subject().GetMaxNumberOfBonds(); + } + + std::size_t GapBondingDecorator::GetNumberOfBonds() const + { + return GapBondingObserver::Subject().GetNumberOfBonds(); + } + + bool GapBondingDecorator::IsDeviceBonded(hal::MacAddress address, GapDeviceAddressType addressType) const + { + return GapBondingObserver::Subject().IsDeviceBonded(address, addressType); + } + + infra::MemoryRange GapBondingDecorator::GetBondList() const + { + return GapBondingObserver::Subject().GetBondList(); + } + + void GapPeripheralDecorator::StateChanged(GapState state) + { + GapPeripheral::NotifyObservers([&state](auto& obs) + { + obs.StateChanged(state); + }); + } + + GapAddress GapPeripheralDecorator::GetAddress() const + { + return GapPeripheralObserver::Subject().GetAddress(); + } + + GapAddress GapPeripheralDecorator::GetIdentityAddress() const + { + return GapPeripheralObserver::Subject().GetIdentityAddress(); + } + + void GapPeripheralDecorator::SetAdvertisementData(infra::ConstByteRange data) + { + GapPeripheralObserver::Subject().SetAdvertisementData(data); + } + + infra::ConstByteRange GapPeripheralDecorator::GetAdvertisementData() const + { + return GapPeripheralObserver::Subject().GetAdvertisementData(); + } + + void GapPeripheralDecorator::SetScanResponseData(infra::ConstByteRange data) + { + GapPeripheralObserver::Subject().SetScanResponseData(data); + } + + infra::ConstByteRange GapPeripheralDecorator::GetScanResponseData() const + { + return GapPeripheralObserver::Subject().GetScanResponseData(); + } + + void GapPeripheralDecorator::Advertise(GapAdvertisementType type, AdvertisementIntervalMultiplier multiplier) + { + GapPeripheralObserver::Subject().Advertise(type, multiplier); + } + + void GapPeripheralDecorator::Standby() + { + GapPeripheralObserver::Subject().Standby(); + } + + void GapPeripheralDecorator::SetConnectionParameters(const services::GapConnectionParameters& connParam) + { + GapPeripheralObserver::Subject().SetConnectionParameters(connParam); + } + + void GapCentralDecorator::DeviceDiscovered(const GapAdvertisingReport& deviceDiscovered) + { + GapCentralObserver::SubjectType::NotifyObservers([&deviceDiscovered](auto& obs) + { + obs.DeviceDiscovered(deviceDiscovered); + }); + } + + void GapCentralDecorator::StateChanged(GapState state) + { + GapCentralObserver::SubjectType::NotifyObservers([&state](auto& obs) + { + obs.StateChanged(state); + }); + } + + void GapCentralDecorator::Connect(GapAddress address, const GapConnectionParameters& connectionParameters, infra::Duration initiatingTimeout) + { + GapCentralObserver::Subject().Connect(address, connectionParameters, initiatingTimeout); + } + + void GapCentralDecorator::Standby() + { + GapCentralObserver::Subject().Standby(); + } + + void GapCentralDecorator::SetIdentityAddress(hal::MacAddress macAddress, GapDeviceAddressType addressType) + { + GapCentralObserver::Subject().SetIdentityAddress(macAddress, addressType); + } + + void GapCentralDecorator::StartDeviceDiscovery() + { + GapCentralObserver::Subject().StartDeviceDiscovery(); + } + + std::optional GapCentralDecorator::ResolvePrivateAddress(hal::MacAddress address) const + { + return GapCentralObserver::Subject().ResolvePrivateAddress(address); + } + + void GapCentralDecorator::SetPrivacyMode(bool enabled) + { + GapCentralObserver::Subject().SetPrivacyMode(enabled); + } + + GapAdvertisingDataParser::GapAdvertisingDataParser(infra::ConstByteRange data) + : data(data) + {} + + infra::ConstByteRange GapAdvertisingDataParser::LocalName() const + { + auto localName = ParserAdvertisingData(GapAdvertisementDataType::completeLocalName); + + if (localName.empty()) + return ParserAdvertisingData(GapAdvertisementDataType::shortenedLocalName); + else + return localName; + } + + std::optional> GapAdvertisingDataParser::ManufacturerSpecificData() const + { + infra::ByteInputStream stream(ParserAdvertisingData(GapAdvertisementDataType::manufacturerSpecificData), infra::softFail); + auto manufacturerCode = stream.Extract(); + auto manufacturerData = stream.Reader().Remaining(); + + if (stream.Failed()) + return std::nullopt; + + return std::make_optional(std::make_pair(manufacturerCode, manufacturerData)); + } + + std::optional GapAdvertisingDataParser::Flags() const + { + auto flagsData = ParserAdvertisingData(GapAdvertisementDataType::flags); + + if (flagsData.empty()) + return std::nullopt; + + if (flagsData.size() != 1) + return std::nullopt; + + return std::make_optional(static_cast(flagsData[0])); + } + + infra::MemoryRange GapAdvertisingDataParser::CompleteListOf16BitUuids() const + { + auto uuidData = ParserAdvertisingData(GapAdvertisementDataType::completeListOf16BitUuids); + + if (uuidData.size() % sizeof(AttAttribute::Uuid16) != 0) + return {}; + + return infra::ConstCastMemoryRange(infra::ReinterpretCastMemoryRange(uuidData)); + } + + infra::MemoryRange GapAdvertisingDataParser::CompleteListOf128BitUuids() const + { + auto uuidData = ParserAdvertisingData(GapAdvertisementDataType::completeListOf128BitUuids); + + if (uuidData.size() % sizeof(AttAttribute::Uuid128) != 0) + return {}; + + return infra::ConstCastMemoryRange(infra::ReinterpretCastMemoryRange(uuidData)); + } + + std::optional GapAdvertisingDataParser::Appearance() const + { + auto appearanceData = ParserAdvertisingData(GapAdvertisementDataType::appearance); + + infra::ByteInputStream stream(appearanceData, infra::softFail); + auto appearance = stream.Extract(); + + if (stream.Failed()) + return std::nullopt; + + return std::make_optional(appearance); + } + + infra::ConstByteRange GapAdvertisingDataParser::ParserAdvertisingData(GapAdvertisementDataType type) const + { + const uint8_t lengthOffset = 0; + const uint8_t advertisingTypeOffset = 1; + const uint8_t headerSize = 2; + + infra::ConstByteRange advData = data; + + while (!advData.empty()) + { + size_t elementSize = std::min(advData[lengthOffset] + 1, advData.size()); + + auto element = infra::Head(advData, elementSize); + + if (element.size() == 1 || (element.size() != element[lengthOffset] + 1)) + return infra::ConstByteRange(); + + if (element[advertisingTypeOffset] == static_cast(type)) + return infra::DiscardHead(element, headerSize); + + advData = infra::DiscardHead(advData, element.size()); + } + + return infra::ConstByteRange(); + } + + GapAdvertisementFormatter::GapAdvertisementFormatter(infra::BoundedVector& payload) + : payload(payload) + {} + + void GapAdvertisementFormatter::AppendFlags(GapPeripheral::AdvertisementFlags flags) + { + really_assert(headerSize + sizeof(flags) <= RemainingSpaceAvailable()); + + auto flagsByte = static_cast(flags); + AddHeader(payload, sizeof(flags), GapAdvertisementDataType::flags); + AddData(payload, infra::MakeRangeFromSingleObject(flagsByte)); + } + + void GapAdvertisementFormatter::AppendCompleteLocalName(const infra::BoundedConstString& name) + { + really_assert(name.size() + headerSize <= RemainingSpaceAvailable() && name.size() > 0); + + AddHeader(payload, name.size(), GapAdvertisementDataType::completeLocalName); + AddData(payload, infra::StringAsByteRange(name)); + } + + void GapAdvertisementFormatter::AppendShortenedLocalName(const infra::BoundedConstString& name) + { + really_assert(name.size() + headerSize <= RemainingSpaceAvailable() && name.size() > 0); + + AddHeader(payload, name.size(), GapAdvertisementDataType::shortenedLocalName); + AddData(payload, infra::StringAsByteRange(name)); + } + + void GapAdvertisementFormatter::AppendManufacturerData(uint16_t manufacturerCode, infra::ConstByteRange data) + { + really_assert(data.size() + headerSize + sizeof(manufacturerCode) <= RemainingSpaceAvailable()); + + AddHeader(payload, data.size() + sizeof(manufacturerCode), GapAdvertisementDataType::manufacturerSpecificData); + AddData(payload, infra::ReinterpretCastMemoryRange(infra::MakeRangeFromSingleObject(manufacturerCode))); + AddData(payload, data); + } + + void GapAdvertisementFormatter::AppendListOfServicesUuid(infra::MemoryRange services) + { + really_assert(services.size() * sizeof(AttAttribute::Uuid16) + headerSize <= RemainingSpaceAvailable() && !services.empty()); + + AddHeader(payload, services.size() * sizeof(AttAttribute::Uuid16), GapAdvertisementDataType::completeListOf16BitUuids); + + for (const auto& service : services) + AddData(payload, infra::ReinterpretCastMemoryRange(infra::MakeRangeFromSingleObject(service))); + } + + void GapAdvertisementFormatter::AppendListOfServicesUuid(infra::MemoryRange services) + { + really_assert(services.size() * sizeof(AttAttribute::Uuid128) + headerSize <= RemainingSpaceAvailable() && !services.empty()); + + AddHeader(payload, services.size() * sizeof(AttAttribute::Uuid128), GapAdvertisementDataType::completeListOf128BitUuids); + + for (auto& service : services) + AddData(payload, infra::ReinterpretCastMemoryRange(infra::MakeRangeFromSingleObject(service))); + } + + void GapAdvertisementFormatter::AppendPublicTargetAddress(hal::MacAddress address) + { + really_assert(sizeof(address) + headerSize <= RemainingSpaceAvailable()); + + AddHeader(payload, sizeof(address), GapAdvertisementDataType::publicTargetAddress); + AddData(payload, infra::ReinterpretCastMemoryRange(infra::MakeRangeFromSingleObject(address))); + } + + void GapAdvertisementFormatter::AppendAppearance(uint16_t appearance) + { + really_assert(sizeof(appearance) + headerSize <= RemainingSpaceAvailable()); + + AddHeader(payload, sizeof(appearance), GapAdvertisementDataType::appearance); + AddData(payload, infra::ReinterpretCastMemoryRange(infra::MakeRangeFromSingleObject(appearance))); + } + + infra::ConstByteRange GapAdvertisementFormatter::FormattedAdvertisementData() const + { + return infra::MakeRange(payload); + } + + std::size_t GapAdvertisementFormatter::RemainingSpaceAvailable() const + { + return payload.max_size() - payload.size(); + } +} + +namespace infra +{ + TextOutputStream& operator<<(TextOutputStream& stream, const services::AdvertisingReportType& reportType) + { + if (reportType == services::AdvertisingReportType::advInd) + stream << "ADV_IND"; + else if (reportType == services::AdvertisingReportType::advDirectInd) + stream << "ADV_DIRECT_IND"; + else if (reportType == services::AdvertisingReportType::advScanInd) + stream << "ADV_SCAN_IND"; + else if (reportType == services::AdvertisingReportType::scanResponse) + stream << "SCAN_RESPONSE"; + else if (reportType == services::AdvertisingReportType::advNonconnInd) + stream << "ADV_NONCONN_IND"; + else + LOG_AND_ABORT_ENUM(reportType); + + return stream; + } + + TextOutputStream& operator<<(TextOutputStream& stream, const services::GapDeviceAddressType& addressType) + { + if (addressType == services::GapDeviceAddressType::publicAddress) + stream << "Public"; + else if (addressType == services::GapDeviceAddressType::randomAddress) + stream << "Random"; + else + LOG_AND_ABORT_ENUM(addressType); + + return stream; + } + + TextOutputStream& operator<<(TextOutputStream& stream, const services::GapState& state) + { + if (state == services::GapState::standby) + stream << "Standby"; + else if (state == services::GapState::scanning) + stream << "Scanning"; + else if (state == services::GapState::advertising) + stream << "Advertising"; + else if (state == services::GapState::initiating) + stream << "Initiating"; + else if (state == services::GapState::connected) + stream << "Connected"; + else + LOG_AND_ABORT_ENUM(state); + + return stream; + } +} diff --git a/services/ble/Gap.hpp b/services/ble/Gap.hpp index 312631cb6..77e52b392 100644 --- a/services/ble/Gap.hpp +++ b/services/ble/Gap.hpp @@ -59,10 +59,10 @@ namespace services struct GapConnectionParameters { - uint16_t minConnIntMultiplier; - uint16_t maxConnIntMultiplier; - uint16_t slaveLatency; - uint16_t supervisorTimeoutMs; + uint16_t minConnectionIntervalMultiplier; + uint16_t maxConnectionIntervalMultiplier; + uint16_t peripheralLatency; + uint16_t supervisionTimeoutMultiplier; static constexpr uint16_t connectionInitialMaxTxOctets = 251; static constexpr uint16_t connectionInitialMaxTxTime = 2120; // (connectionInitialMaxTxOctets + 14) * 8 @@ -399,7 +399,7 @@ namespace services : public infra::Subject { public: - virtual void Connect(hal::MacAddress macAddress, GapDeviceAddressType addressType, infra::Duration initiatingTimeout) = 0; + virtual void Connect(GapAddress address, const GapConnectionParameters& connectionParameters, infra::Duration initiatingTimeout) = 0; virtual void Standby() = 0; virtual void SetIdentityAddress(hal::MacAddress macAddress, GapDeviceAddressType addressType) = 0; virtual void StartDeviceDiscovery() = 0; @@ -419,7 +419,7 @@ namespace services void StateChanged(GapState state) override; // Implementation of GapCentral - void Connect(hal::MacAddress macAddress, GapDeviceAddressType addressType, infra::Duration initiatingTimeout) override; + void Connect(GapAddress address, const GapConnectionParameters& connectionParameters, infra::Duration initiatingTimeout) override; void Standby() override; void SetIdentityAddress(hal::MacAddress macAddress, GapDeviceAddressType addressType) override; void StartDeviceDiscovery() override; diff --git a/services/ble/Gap.proto b/services/ble/Gap.proto index 4c1195aef..a8cad37ea 100644 --- a/services/ble/Gap.proto +++ b/services/ble/Gap.proto @@ -401,17 +401,34 @@ message SecurityModeAndLevel SecurityLevelEnum level = 2; } +// Link layer connection parameters, as defined in Bluetooth Core Specification v6.3, Vol 6, Part B, Section 4.5.1. message ConnectionParameters +{ + // Minimum and maximum connection interval, in units of 1.25 ms. + uint32 minConnectionIntervalMultiplier = 1; + uint32 maxConnectionIntervalMultiplier = 2; + + // Number of consecutive connection events the peripheral is allowed to skip. + uint32 peripheralLatency = 3; + + // Time without a successful connection event after which the link is considered lost, in units of 10 ms. + uint32 supervisionTimeoutMultiplier = 4; +} + +message ConnectRequest { // The address of the peripheral to connect to, as discovered during scanning (e.g. from DiscoveredDevice). AddressWithType addressWithType = 1; + // The link layer parameters requested for this connection. + ConnectionParameters connectionParameters = 2; + // How long the central will wait for the peripheral to respond to the connection request, in milliseconds. // After the central sends the connection request, the peripheral may take time to process and confirm it. // If the peripheral does not respond within this timeout, the connection attempt is aborted. // A value of zero waits indefinitely. // Recommended: set to at least several multiples of the peripheral's advertising interval. - uint32 initiatingTimeoutInMs = 2; + uint32 initiatingTimeoutInMs = 3; } message AdvertisingReportType @@ -431,7 +448,7 @@ message AdvertisingReportType message DiscoveredDevice { - // The address as received in the advertising packet. Use this address directly when connecting via ConnectionParameters. + // The address as received in the advertising packet. Use this address directly when connecting via ConnectRequest. // If the peripheral uses a resolvable private address (RPA), the controller will resolve it automatically // after a bond has been established and the peer's IRK is stored. AddressWithType addressWithType = 1; @@ -531,7 +548,7 @@ service GapCentral // Response Sequences: // On successful connection establishment: standby -> initiating -> connected // On failed connection establishment: standby -> initiating -> standby - rpc Connect(ConnectionParameters) returns (Nothing) { option (method_id) = 4; } + rpc Connect(ConnectRequest) returns (Nothing) { option (method_id) = 4; } // Allowed states: standby, scanning, connected, initiating // Expected response: GapCentralResponse.CurrentState with state standby diff --git a/services/ble/test/TestGapCentral.cpp b/services/ble/test/TestGapCentral.cpp index 1b218ed14..0a4cf1a0e 100644 --- a/services/ble/test/TestGapCentral.cpp +++ b/services/ble/test/TestGapCentral.cpp @@ -30,6 +30,11 @@ namespace services return x.reportType == arg.reportType && x.gapAddress == arg.gapAddress && x.rssi == arg.rssi; } + MATCHER_P(ConnectionParametersEqual, x, negation ? "Contents not equal" : "Contents are equal") + { + return x.minConnectionIntervalMultiplier == arg.minConnectionIntervalMultiplier && x.maxConnectionIntervalMultiplier == arg.maxConnectionIntervalMultiplier && x.peripheralLatency == arg.peripheralLatency && x.supervisionTimeoutMultiplier == arg.supervisionTimeoutMultiplier; + } + TEST_F(GapCentralDecoratorTest, forward_all_state_changed_events_to_observers) { EXPECT_CALL(gapObserver, StateChanged(GapState::connected)); @@ -66,9 +71,11 @@ namespace services TEST_F(GapCentralDecoratorTest, forward_all_calls_to_subject) { hal::MacAddress macAddress{ 0, 1, 2, 3, 4, 5 }; + const GapAddress address{ macAddress, GapDeviceAddressType::publicAddress }; + const GapConnectionParameters connectionParameters{ 6, 6, 0, 500 }; - EXPECT_CALL(gap, Connect(MacAddressContentsEqual(macAddress), services::GapDeviceAddressType::publicAddress, infra::Duration{ 0 })); - decorator.Connect(macAddress, services::GapDeviceAddressType::publicAddress, std::chrono::seconds(0)); + EXPECT_CALL(gap, Connect(address, ConnectionParametersEqual(connectionParameters), infra::Duration{ 0 })); + decorator.Connect(address, connectionParameters, std::chrono::seconds(0)); EXPECT_CALL(gap, Standby()); decorator.Standby(); diff --git a/services/ble/test/TestGapPeripheral.cpp b/services/ble/test/TestGapPeripheral.cpp index 95bb12bb9..47fa5afe8 100644 --- a/services/ble/test/TestGapPeripheral.cpp +++ b/services/ble/test/TestGapPeripheral.cpp @@ -1,74 +1,74 @@ -#include "infra/util/test_helper/MemoryRangeMatcher.hpp" -#include "services/ble/Gap.hpp" -#include "services/ble/test_doubles/GapCentralMock.hpp" -#include "services/ble/test_doubles/GapCentralObserverMock.hpp" -#include "services/ble/test_doubles/GapPeripheralMock.hpp" -#include "services/ble/test_doubles/GapPeripheralObserverMock.hpp" -#include "gmock/gmock.h" - -namespace services -{ - namespace - { - class GapPeripheralDecoratorTest - : public testing::Test - { - public: - GapPeripheralMock gap; - GapPeripheralDecorator decorator{ gap }; - GapPeripheralObserverMock gapObserver{ decorator }; - }; - } - - TEST_F(GapPeripheralDecoratorTest, forward_all_events_to_observers) - { - EXPECT_CALL(gapObserver, StateChanged(GapState::connected)); - EXPECT_CALL(gapObserver, StateChanged(GapState::advertising)); - - gap.NotifyObservers([](GapPeripheralObserver& obs) - { - obs.StateChanged(GapState::connected); - obs.StateChanged(GapState::advertising); - }); - } - - TEST_F(GapPeripheralDecoratorTest, forward_all_calls_to_subject) - { - services::GapAddress address = { hal::MacAddress({ 5, 4, 3, 2, 1, 0 }), services::GapDeviceAddressType::publicAddress }; - EXPECT_CALL(gap, GetAddress()).WillOnce(testing::Return(address)); - EXPECT_THAT(decorator.GetAddress(), testing::Eq(address)); - - services::GapAddress identityAddress = { hal::MacAddress({ 0, 1, 2, 3, 4, 5 }), services::GapDeviceAddressType::publicAddress }; - EXPECT_CALL(gap, GetIdentityAddress()).WillOnce(testing::Return(identityAddress)); - EXPECT_THAT(decorator.GetIdentityAddress(), testing::Eq(identityAddress)); - - std::array data{ 0, 1, 2, 3, 4, 5 }; - EXPECT_CALL(gap, SetAdvertisementData(infra::ContentsEqual(data))); - decorator.SetAdvertisementData(data); - - EXPECT_CALL(gap, GetAdvertisementData()); - decorator.GetAdvertisementData(); - - EXPECT_CALL(gap, SetScanResponseData(infra::ContentsEqual(data))); - decorator.SetScanResponseData(data); - - EXPECT_CALL(gap, GetScanResponseData()); - decorator.GetScanResponseData(); - - EXPECT_CALL(gap, Advertise(services::GapAdvertisementType::advNonconnInd, 32)); - decorator.Advertise(services::GapAdvertisementType::advNonconnInd, 32); - - EXPECT_CALL(gap, Standby()); - decorator.Standby(); - - services::GapConnectionParameters connParam{ 10, 20, 30, 40 }; - EXPECT_CALL(gap, SetConnectionParameters(testing::_)).WillOnce(testing::Invoke([connParam](const services::GapConnectionParameters& param) - { - EXPECT_EQ(param.maxConnIntMultiplier, connParam.maxConnIntMultiplier); - EXPECT_EQ(param.minConnIntMultiplier, connParam.minConnIntMultiplier); - EXPECT_EQ(param.slaveLatency, connParam.slaveLatency); - EXPECT_EQ(param.supervisorTimeoutMs, connParam.supervisorTimeoutMs); - })); - decorator.SetConnectionParameters(connParam); - } -} +#include "infra/util/test_helper/MemoryRangeMatcher.hpp" +#include "services/ble/Gap.hpp" +#include "services/ble/test_doubles/GapCentralMock.hpp" +#include "services/ble/test_doubles/GapCentralObserverMock.hpp" +#include "services/ble/test_doubles/GapPeripheralMock.hpp" +#include "services/ble/test_doubles/GapPeripheralObserverMock.hpp" +#include "gmock/gmock.h" + +namespace services +{ + namespace + { + class GapPeripheralDecoratorTest + : public testing::Test + { + public: + GapPeripheralMock gap; + GapPeripheralDecorator decorator{ gap }; + GapPeripheralObserverMock gapObserver{ decorator }; + }; + } + + TEST_F(GapPeripheralDecoratorTest, forward_all_events_to_observers) + { + EXPECT_CALL(gapObserver, StateChanged(GapState::connected)); + EXPECT_CALL(gapObserver, StateChanged(GapState::advertising)); + + gap.NotifyObservers([](GapPeripheralObserver& obs) + { + obs.StateChanged(GapState::connected); + obs.StateChanged(GapState::advertising); + }); + } + + TEST_F(GapPeripheralDecoratorTest, forward_all_calls_to_subject) + { + services::GapAddress address = { hal::MacAddress({ 5, 4, 3, 2, 1, 0 }), services::GapDeviceAddressType::publicAddress }; + EXPECT_CALL(gap, GetAddress()).WillOnce(testing::Return(address)); + EXPECT_THAT(decorator.GetAddress(), testing::Eq(address)); + + services::GapAddress identityAddress = { hal::MacAddress({ 0, 1, 2, 3, 4, 5 }), services::GapDeviceAddressType::publicAddress }; + EXPECT_CALL(gap, GetIdentityAddress()).WillOnce(testing::Return(identityAddress)); + EXPECT_THAT(decorator.GetIdentityAddress(), testing::Eq(identityAddress)); + + std::array data{ 0, 1, 2, 3, 4, 5 }; + EXPECT_CALL(gap, SetAdvertisementData(infra::ContentsEqual(data))); + decorator.SetAdvertisementData(data); + + EXPECT_CALL(gap, GetAdvertisementData()); + decorator.GetAdvertisementData(); + + EXPECT_CALL(gap, SetScanResponseData(infra::ContentsEqual(data))); + decorator.SetScanResponseData(data); + + EXPECT_CALL(gap, GetScanResponseData()); + decorator.GetScanResponseData(); + + EXPECT_CALL(gap, Advertise(services::GapAdvertisementType::advNonconnInd, 32)); + decorator.Advertise(services::GapAdvertisementType::advNonconnInd, 32); + + EXPECT_CALL(gap, Standby()); + decorator.Standby(); + + services::GapConnectionParameters connParam{ 10, 20, 30, 40 }; + EXPECT_CALL(gap, SetConnectionParameters(testing::_)).WillOnce(testing::Invoke([connParam](const services::GapConnectionParameters& param) + { + EXPECT_EQ(param.maxConnectionIntervalMultiplier, connParam.maxConnectionIntervalMultiplier); + EXPECT_EQ(param.minConnectionIntervalMultiplier, connParam.minConnectionIntervalMultiplier); + EXPECT_EQ(param.peripheralLatency, connParam.peripheralLatency); + EXPECT_EQ(param.supervisionTimeoutMultiplier, connParam.supervisionTimeoutMultiplier); + })); + decorator.SetConnectionParameters(connParam); + } +} diff --git a/services/ble/test/TestGapPeripheralIntervalDecorator.cpp b/services/ble/test/TestGapPeripheralIntervalDecorator.cpp index d48d361b4..b7b3948c4 100644 --- a/services/ble/test/TestGapPeripheralIntervalDecorator.cpp +++ b/services/ble/test/TestGapPeripheralIntervalDecorator.cpp @@ -71,10 +71,10 @@ TEST_F(GapPeripheralIntervalDecoratorTest, use_default_connection_parameter_when EXPECT_CALL(gapObserver, StateChanged(services::GapState::connected)); EXPECT_CALL(gap, SetConnectionParameters(testing::_)).WillOnce(testing::Invoke([](const services::GapConnectionParameters& connParam) { - EXPECT_EQ(connParam.minConnIntMultiplier, 6); - EXPECT_EQ(connParam.maxConnIntMultiplier, 6); - EXPECT_EQ(connParam.slaveLatency, 0); - EXPECT_EQ(connParam.supervisorTimeoutMs, 500); + EXPECT_EQ(connParam.minConnectionIntervalMultiplier, 6); + EXPECT_EQ(connParam.maxConnectionIntervalMultiplier, 6); + EXPECT_EQ(connParam.peripheralLatency, 0); + EXPECT_EQ(connParam.supervisionTimeoutMultiplier, 500); })); gapPeripheralIntervalDecorator.StateChanged(services::GapState::connected); } @@ -87,10 +87,10 @@ TEST_F(GapPeripheralIntervalDecoratorTest, use_user_connection_parameter_when_co EXPECT_CALL(gapObserver, StateChanged(services::GapState::connected)); EXPECT_CALL(gap, SetConnectionParameters(testing::_)).WillOnce(testing::Invoke([](const services::GapConnectionParameters& connParam) { - EXPECT_EQ(connParam.minConnIntMultiplier, 10); - EXPECT_EQ(connParam.maxConnIntMultiplier, 10); - EXPECT_EQ(connParam.slaveLatency, 0); - EXPECT_EQ(connParam.supervisorTimeoutMs, 500); + EXPECT_EQ(connParam.minConnectionIntervalMultiplier, 10); + EXPECT_EQ(connParam.maxConnectionIntervalMultiplier, 10); + EXPECT_EQ(connParam.peripheralLatency, 0); + EXPECT_EQ(connParam.supervisionTimeoutMultiplier, 500); })); gapPeripheralIntervalDecorator.StateChanged(services::GapState::connected); } @@ -102,10 +102,10 @@ TEST_F(GapPeripheralIntervalDecoratorTest, use_long_connection_parameter_when_co EXPECT_CALL(gapObserver, StateChanged(services::GapState::connected)); EXPECT_CALL(gap, SetConnectionParameters(testing::_)).WillOnce(testing::Invoke([](const services::GapConnectionParameters& connParam) { - EXPECT_EQ(connParam.minConnIntMultiplier, 50); - EXPECT_EQ(connParam.maxConnIntMultiplier, 50); - EXPECT_EQ(connParam.slaveLatency, 0); - EXPECT_EQ(connParam.supervisorTimeoutMs, 500); + EXPECT_EQ(connParam.minConnectionIntervalMultiplier, 50); + EXPECT_EQ(connParam.maxConnectionIntervalMultiplier, 50); + EXPECT_EQ(connParam.peripheralLatency, 0); + EXPECT_EQ(connParam.supervisionTimeoutMultiplier, 500); })); gapPeripheralIntervalDecorator.StateChanged(services::GapState::connected); } @@ -115,28 +115,28 @@ TEST_F(GapPeripheralIntervalDecoratorTest, switch_to_long_connection_interval_an EXPECT_CALL(gapObserver, StateChanged(services::GapState::connected)); EXPECT_CALL(gap, SetConnectionParameters(testing::_)).WillOnce(testing::Invoke([](const services::GapConnectionParameters& connParam) { - EXPECT_EQ(connParam.minConnIntMultiplier, 6); - EXPECT_EQ(connParam.maxConnIntMultiplier, 6); - EXPECT_EQ(connParam.slaveLatency, 0); - EXPECT_EQ(connParam.supervisorTimeoutMs, 500); + EXPECT_EQ(connParam.minConnectionIntervalMultiplier, 6); + EXPECT_EQ(connParam.maxConnectionIntervalMultiplier, 6); + EXPECT_EQ(connParam.peripheralLatency, 0); + EXPECT_EQ(connParam.supervisionTimeoutMultiplier, 500); })); gapPeripheralIntervalDecorator.StateChanged(services::GapState::connected); EXPECT_CALL(gap, SetConnectionParameters(testing::_)).WillOnce(testing::Invoke([](const services::GapConnectionParameters& connParam) { - EXPECT_EQ(connParam.minConnIntMultiplier, 50); - EXPECT_EQ(connParam.maxConnIntMultiplier, 50); - EXPECT_EQ(connParam.slaveLatency, 0); - EXPECT_EQ(connParam.supervisorTimeoutMs, 500); + EXPECT_EQ(connParam.minConnectionIntervalMultiplier, 50); + EXPECT_EQ(connParam.maxConnectionIntervalMultiplier, 50); + EXPECT_EQ(connParam.peripheralLatency, 0); + EXPECT_EQ(connParam.supervisionTimeoutMultiplier, 500); })); gapPeripheralIntervalDecorator.SwitchToLongInterval(); EXPECT_CALL(gap, SetConnectionParameters(testing::_)).WillOnce(testing::Invoke([](const services::GapConnectionParameters& connParam) { - EXPECT_EQ(connParam.minConnIntMultiplier, 6); - EXPECT_EQ(connParam.maxConnIntMultiplier, 6); - EXPECT_EQ(connParam.slaveLatency, 0); - EXPECT_EQ(connParam.supervisorTimeoutMs, 500); + EXPECT_EQ(connParam.minConnectionIntervalMultiplier, 6); + EXPECT_EQ(connParam.maxConnectionIntervalMultiplier, 6); + EXPECT_EQ(connParam.peripheralLatency, 0); + EXPECT_EQ(connParam.supervisionTimeoutMultiplier, 500); })); gapPeripheralIntervalDecorator.SwitchToUserInterval(); } diff --git a/services/ble/test_doubles/GapCentralMock.hpp b/services/ble/test_doubles/GapCentralMock.hpp index 10a3e6007..bd7daa620 100644 --- a/services/ble/test_doubles/GapCentralMock.hpp +++ b/services/ble/test_doubles/GapCentralMock.hpp @@ -10,7 +10,7 @@ namespace services : public GapCentral { public: - MOCK_METHOD(void, Connect, (hal::MacAddress macAddress, GapDeviceAddressType addressType, infra::Duration initiatingTimeout)); + MOCK_METHOD(void, Connect, (GapAddress address, const GapConnectionParameters& connectionParameters, infra::Duration initiatingTimeout)); MOCK_METHOD(void, Standby, ()); MOCK_METHOD(void, SetIdentityAddress, (hal::MacAddress macAddress, GapDeviceAddressType addressType)); MOCK_METHOD(void, StartDeviceDiscovery, ());