| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/connectivity/bluetooth/core/bt-host/testing/fake_sdp_server.h" |
| |
| #include <lib/gtest/test_loop_fixture.h> |
| |
| #include "src/connectivity/bluetooth/core/bt-host/common/byte_buffer.h" |
| #include "src/connectivity/bluetooth/core/bt-host/common/test_helpers.h" |
| #include "src/connectivity/bluetooth/core/bt-host/hci-spec/protocol.h" |
| #include "src/connectivity/bluetooth/core/bt-host/l2cap/l2cap_defs.h" |
| #include "src/connectivity/bluetooth/core/bt-host/l2cap/test_packets.h" |
| #include "src/connectivity/bluetooth/core/bt-host/testing/fake_dynamic_channel.h" |
| #include "src/connectivity/bluetooth/core/bt-host/testing/fake_l2cap.h" |
| #include "src/connectivity/bluetooth/core/bt-host/testing/fake_signaling_server.h" |
| |
| namespace bt::testing { |
| namespace { |
| |
| l2cap::ChannelParameters kChannelParams; |
| hci::ConnectionHandle kConnectionHandle = 0x01; |
| l2cap::CommandId kCommandId = 0x02; |
| l2cap::PSM kPsm = l2cap::kSDP; |
| l2cap::ChannelId src_id = l2cap::kFirstDynamicChannelId; |
| |
| auto SdpErrorResponse(uint16_t t_id, sdp::ErrorCode code) { |
| return StaticByteBuffer(0x01, UpperBits(t_id), LowerBits(t_id), 0x00, 0x02, |
| UpperBits(uint16_t(code)), LowerBits(uint16_t(code))); |
| } |
| |
| std::vector<sdp::ServiceRecord> GetSPPServiceRecord() { |
| sdp::ServiceRecord record; |
| record.SetServiceClassUUIDs({sdp::profile::kSerialPort}); |
| record.AddProtocolDescriptor(sdp::ServiceRecord::kPrimaryProtocolList, sdp::protocol::kL2CAP, |
| sdp::DataElement()); |
| record.AddProtocolDescriptor(sdp::ServiceRecord::kPrimaryProtocolList, sdp::protocol::kRFCOMM, |
| sdp::DataElement(uint8_t{0})); |
| record.AddProfile(sdp::profile::kSerialPort, 1, 2); |
| record.AddInfo("en", "FAKE", "", ""); |
| std::vector<sdp::ServiceRecord> records; |
| records.emplace_back(std::move(record)); |
| return records; |
| } |
| |
| std::vector<sdp::ServiceRecord> GetA2DPServiceRecord() { |
| sdp::ServiceRecord record; |
| record.SetServiceClassUUIDs({sdp::profile::kAudioSink}); |
| record.AddProtocolDescriptor(sdp::ServiceRecord::kPrimaryProtocolList, sdp::protocol::kL2CAP, |
| sdp::DataElement(l2cap::kAVDTP)); |
| record.AddProtocolDescriptor(sdp::ServiceRecord::kPrimaryProtocolList, sdp::protocol::kAVDTP, |
| sdp::DataElement(uint16_t{0x0103})); // Version |
| record.AddProfile(sdp::profile::kAdvancedAudioDistribution, 1, 3); |
| record.SetAttribute(sdp::kA2DP_SupportedFeatures, |
| sdp::DataElement(uint16_t{0x0001})); // Headphones |
| std::vector<sdp::ServiceRecord> records; |
| records.emplace_back(std::move(record)); |
| return records; |
| } |
| |
| #define UINT32_AS_BE_BYTES(x) \ |
| UpperBits(x >> 16), LowerBits(x >> 16), UpperBits(x & 0xFFFF), LowerBits(x & 0xFFFF) |
| |
| TEST(TESTING_FakeSdpServerTest, SuccessfulSearch) { |
| std::unique_ptr<ByteBuffer> sent_packet; |
| auto send_cb = [&sent_packet](auto& buffer) { |
| sent_packet = std::make_unique<DynamicByteBuffer>(buffer); |
| }; |
| FakeDynamicChannel channel(kConnectionHandle, kCommandId, src_id, src_id); |
| channel.set_send_packet_callback(send_cb); |
| channel.set_opened(); |
| auto sdp_server = FakeSdpServer(); |
| |
| // Configure the SDP server to provide a response to the search. |
| auto NopConnectCallback = [](auto /*channel*/, const sdp::DataElement&) {}; |
| sdp::Server::RegistrationHandle spp_handle = sdp_server.server()->RegisterService( |
| GetSPPServiceRecord(), kChannelParams, NopConnectCallback); |
| EXPECT_TRUE(spp_handle); |
| sdp::Server::RegistrationHandle a2dp_handle = sdp_server.server()->RegisterService( |
| GetA2DPServiceRecord(), kChannelParams, NopConnectCallback); |
| EXPECT_TRUE(a2dp_handle); |
| const StaticByteBuffer kL2capSearch = |
| CreateStaticByteBuffer(0x02, // SDP_ServiceSearchRequest |
| 0x10, 0x01, // Transaction ID (0x1001) |
| 0x00, 0x08, // Parameter length (8 bytes) |
| // ServiceSearchPattern |
| 0x35, 0x03, // Sequence uint8 3 bytes |
| 0x19, 0x01, 0x00, // UUID: Protocol: L2CAP |
| 0xFF, 0xFF, // MaximumServiceRecordCount: (none) |
| 0x00 // Contunuation State: none |
| ); |
| const StaticByteBuffer kL2capSearchResponse = CreateStaticByteBuffer( |
| 0x03, // SDP_ServicesearchResponse |
| 0x10, 0x01, // Transaction ID (0x1001) |
| 0x00, 0x0D, // Parameter length (13 bytes) |
| 0x00, 0x02, // Total service record count: 2 |
| 0x00, 0x02, // Current service record count: 2 |
| UINT32_AS_BE_BYTES(a2dp_handle), // This list isn't specifically ordered |
| UINT32_AS_BE_BYTES(spp_handle), |
| 0x00 // No continuation state |
| ); |
| sdp_server.HandleSdu(channel.AsWeakPtr(), kL2capSearch); |
| EXPECT_TRUE(ContainersEqual(kL2capSearchResponse, *sent_packet)); |
| } |
| |
| TEST(TESTING_FakeSdpServerTest, ErrorIfTooSmall) { |
| std::unique_ptr<ByteBuffer> sent_packet; |
| auto send_cb = [&sent_packet](auto& buffer) { |
| sent_packet = std::make_unique<DynamicByteBuffer>(buffer); |
| }; |
| FakeDynamicChannel channel(kConnectionHandle, kCommandId, src_id, src_id); |
| channel.set_send_packet_callback(send_cb); |
| channel.set_opened(); |
| auto sdp_server = FakeSdpServer(); |
| |
| // Expect an error response if the packet is too small |
| const auto kTooSmall = CreateStaticByteBuffer(0x01, // SDP_ServiceSearchRequest |
| 0x10, 0x01, // Transaction ID (0x1001) |
| 0x00, 0x09 // Parameter length (9 bytes) |
| ); |
| const auto kRspTooSmall = SdpErrorResponse(0x1001, sdp::ErrorCode::kInvalidSize); |
| sdp_server.HandleSdu(channel.AsWeakPtr(), kTooSmall); |
| EXPECT_TRUE(ContainersEqual(kRspTooSmall, *sent_packet)); |
| } |
| |
| TEST(TESTING_FakeSdpServerTest, RegisterWithL2cap) { |
| std::unique_ptr<ByteBuffer> received_packet; |
| auto send_cb = [&received_packet](auto conn, auto cid, auto& buffer) { |
| received_packet = std::make_unique<DynamicByteBuffer>(buffer); |
| }; |
| auto fake_l2cap = FakeL2cap(send_cb); |
| auto sdp_server = std::make_unique<FakeSdpServer>(); |
| sdp_server->RegisterWithL2cap(&fake_l2cap); |
| EXPECT_TRUE(fake_l2cap.ServiceRegisteredForPsm(kPsm)); |
| } |
| |
| } // namespace |
| } // namespace bt::testing |