From e1708c8c44783ed5b3882a0e5ffa59e37e5b77e1 Mon Sep 17 00:00:00 2001 From: Myles Watson Date: Wed, 18 Jan 2023 17:07:58 -0800 Subject: [PATCH] VtsHalBluetoothTargetTest: test loopback Add tests for loopback testing mode. Bug: 205758693 Test: atest VtsHalBluetoothTargetTest Change-Id: Ie4d23136f0cff901435ec33f8668dd1e261d9870 --- .../aidl/vts/VtsHalBluetoothTargetTest.cpp | 421 ++++++++++++++++-- 1 file changed, 394 insertions(+), 27 deletions(-) diff --git a/bluetooth/aidl/vts/VtsHalBluetoothTargetTest.cpp b/bluetooth/aidl/vts/VtsHalBluetoothTargetTest.cpp index 27e6ebfab3..57a33616c5 100644 --- a/bluetooth/aidl/vts/VtsHalBluetoothTargetTest.cpp +++ b/bluetooth/aidl/vts/VtsHalBluetoothTargetTest.cpp @@ -46,8 +46,13 @@ static constexpr uint8_t kHciMinimumHciVersion = 5; // Bluetooth Core Specification 3.0 + HS static constexpr uint8_t kHciMinimumLmpVersion = 5; +static constexpr size_t kNumHciCommandsBandwidth = 100; +static constexpr size_t kNumScoPacketsBandwidth = 100; +static constexpr size_t kNumAclPacketsBandwidth = 100; static constexpr std::chrono::milliseconds kWaitForInitTimeout(2000); static constexpr std::chrono::milliseconds kWaitForHciEventTimeout(2000); +static constexpr std::chrono::milliseconds kWaitForScoDataTimeout(1000); +static constexpr std::chrono::milliseconds kWaitForAclDataTimeout(1000); static constexpr std::chrono::milliseconds kInterfaceCloseDelayMs(200); static constexpr uint8_t kCommandHciShouldBeUnknown[] = { @@ -55,15 +60,24 @@ static constexpr uint8_t kCommandHciShouldBeUnknown[] = { static constexpr uint8_t kCommandHciReadLocalVersionInformation[] = {0x01, 0x10, 0x00}; static constexpr uint8_t kCommandHciReadBufferSize[] = {0x05, 0x10, 0x00}; +static constexpr uint8_t kCommandHciWriteLoopbackModeLocal[] = {0x02, 0x18, + 0x01, 0x01}; static constexpr uint8_t kCommandHciReset[] = {0x03, 0x0c, 0x00}; +static constexpr uint8_t kCommandHciSynchronousFlowControlEnable[] = { + 0x2f, 0x0c, 0x01, 0x01}; +static constexpr uint8_t kCommandHciWriteLocalName[] = {0x13, 0x0c, 0xf8}; static constexpr uint8_t kHciStatusSuccess = 0x00; static constexpr uint8_t kHciStatusUnknownHciCommand = 0x01; +static constexpr uint8_t kEventConnectionComplete = 0x03; static constexpr uint8_t kEventCommandComplete = 0x0e; static constexpr uint8_t kEventCommandStatus = 0x0f; static constexpr uint8_t kEventNumberOfCompletedPackets = 0x13; +static constexpr uint8_t kEventLoopbackCommand = 0x19; static constexpr size_t kEventCodeByte = 0; +static constexpr size_t kEventLengthByte = 1; +static constexpr size_t kEventFirstPayloadByte = 2; static constexpr size_t kEventCommandStatusStatusByte = 2; static constexpr size_t kEventCommandStatusOpcodeLsByte = 4; // Bytes 4 and 5 static constexpr size_t kEventCommandCompleteOpcodeLsByte = 3; // Bytes 3 and 4 @@ -74,15 +88,33 @@ static constexpr size_t kEventLocalHciVersionByte = static constexpr size_t kEventLocalLmpVersionByte = kEventLocalHciVersionByte + 3; +static constexpr size_t kEventConnectionCompleteParamLength = 11; +static constexpr size_t kEventConnectionCompleteType = 11; +static constexpr size_t kEventConnectionCompleteTypeSco = 0; +static constexpr size_t kEventConnectionCompleteTypeAcl = 1; +static constexpr size_t kEventConnectionCompleteHandleLsByte = 3; + static constexpr size_t kEventNumberOfCompletedPacketsNumHandles = 2; +static constexpr size_t kAclBroadcastFlagOffset = 6; +static constexpr uint8_t kAclBroadcastFlagPointToPoint = 0x0; +static constexpr uint8_t kAclBroadcastPointToPoint = + (kAclBroadcastFlagPointToPoint << kAclBroadcastFlagOffset); + +static constexpr uint8_t kAclPacketBoundaryFlagOffset = 4; +static constexpr uint8_t kAclPacketBoundaryFlagFirstAutoFlushable = 0x2; +static constexpr uint8_t kAclPacketBoundaryFirstAutoFlushable = + kAclPacketBoundaryFlagFirstAutoFlushable << kAclPacketBoundaryFlagOffset; + // To discard Qualcomm ACL debugging static constexpr uint16_t kAclHandleQcaDebugMessage = 0xedc; class ThroughputLogger { public: ThroughputLogger(std::string task) - : task_(task), start_time_(std::chrono::steady_clock::now()) {} + : total_bytes_(0), + task_(task), + start_time_(std::chrono::steady_clock::now()) {} ~ThroughputLogger() { if (total_bytes_ == 0) { @@ -153,6 +185,7 @@ class BluetoothAidlTest : public ::testing::TestWithParam { ASSERT_TRUE(hci->close().isOk()); std::this_thread::sleep_for(kInterfaceCloseDelayMs); handle_no_ops(); + discard_qca_debugging(); EXPECT_EQ(static_cast(0), event_queue.size()); EXPECT_EQ(static_cast(0), sco_queue.size()); EXPECT_EQ(static_cast(0), acl_queue.size()); @@ -160,9 +193,18 @@ class BluetoothAidlTest : public ::testing::TestWithParam { } void setBufferSizes(); + void setSynchronousFlowControlEnable(); + + // Functions called from within tests in loopback mode + void sendAndCheckHci(int num_packets); + void sendAndCheckSco(int num_packets, size_t size, uint16_t handle); + void sendAndCheckAcl(int num_packets, size_t size, uint16_t handle); // Helper functions to try to get a handle on verbosity + void reset(); + void enterLoopbackMode(); void handle_no_ops(); + void discard_qca_debugging(); void wait_for_event(bool timeout_is_error); void wait_for_command_complete_event(std::vector cmd); int wait_for_completed_packets_event(uint16_t handle); @@ -304,6 +346,9 @@ class BluetoothAidlTest : public ::testing::TestWithParam { int max_sco_data_packet_length; int max_acl_data_packets; int max_sco_data_packets; + + std::vector sco_connection_handles; + std::vector acl_connection_handles; }; // Discard NO-OPs from the event queue. @@ -326,7 +371,10 @@ void BluetoothAidlTest::handle_no_ops() { break; } } - // Discard Qualcomm ACL debugging +} + +// Discard Qualcomm ACL debugging +void BluetoothAidlTest::discard_qca_debugging() { while (!acl_queue.empty()) { std::vector acl_packet; acl_queue.front(acl_packet); @@ -344,24 +392,28 @@ void BluetoothAidlTest::handle_no_ops() { // Receive an event, discarding NO-OPs. void BluetoothAidlTest::wait_for_event(bool timeout_is_error = true) { - if (timeout_is_error) { - ASSERT_TRUE(event_queue.waitWithTimeout(kWaitForHciEventTimeout)); - } else { - event_queue.wait(); + // Wait until we get something that's not a no-op. + while (true) { + bool event_ready = event_queue.waitWithTimeout(kWaitForHciEventTimeout); + ASSERT_TRUE(event_ready || !timeout_is_error); + if (event_queue.empty()) { + // waitWithTimeout timed out + return; + } + handle_no_ops(); + if (!event_queue.empty()) { + // There's an event in the queue that's not a no-op. + return; + } } - ASSERT_LT(static_cast(0), event_queue.size()); - if (event_queue.empty()) { - // waitWithTimeout timed out - return; - } - handle_no_ops(); } // Wait until a command complete is received. void BluetoothAidlTest::wait_for_command_complete_event( std::vector cmd) { - wait_for_event(); + ASSERT_NO_FATAL_FAILURE(wait_for_event()); std::vector event; + ASSERT_FALSE(event_queue.empty()); ASSERT_TRUE(event_queue.pop(event)); ASSERT_GT(event.size(), static_cast(kEventCommandCompleteStatusByte)); @@ -378,7 +430,7 @@ void BluetoothAidlTest::setBufferSizes() { kCommandHciReadBufferSize + sizeof(kCommandHciReadBufferSize)}; hci->sendHciCommand(cmd); - wait_for_event(); + ASSERT_NO_FATAL_FAILURE(wait_for_event()); if (event_queue.empty()) { return; } @@ -406,6 +458,155 @@ void BluetoothAidlTest::setBufferSizes() { static_cast(max_sco_data_packets)); } +// Enable flow control packets for SCO +void BluetoothAidlTest::setSynchronousFlowControlEnable() { + std::vector cmd{kCommandHciSynchronousFlowControlEnable, + kCommandHciSynchronousFlowControlEnable + + sizeof(kCommandHciSynchronousFlowControlEnable)}; + hci->sendHciCommand(cmd); + + wait_for_command_complete_event(cmd); +} + +// Send an HCI command (in Loopback mode) and check the response. +void BluetoothAidlTest::sendAndCheckHci(int num_packets) { + ThroughputLogger logger = {__func__}; + int command_size = 0; + for (int n = 0; n < num_packets; n++) { + // Send an HCI packet + std::vector write_name{ + kCommandHciWriteLocalName, + kCommandHciWriteLocalName + sizeof(kCommandHciWriteLocalName)}; + // With a name + char new_name[] = "John Jacob Jingleheimer Schmidt ___________________0"; + size_t new_name_length = strlen(new_name); + for (size_t i = 0; i < new_name_length; i++) { + write_name.push_back(static_cast(new_name[i])); + } + // And the packet number + size_t i = new_name_length - 1; + for (int digits = n; digits > 0; digits = digits / 10, i--) { + write_name[i] = static_cast('0' + digits % 10); + } + // And padding + for (size_t i = 0; i < 248 - new_name_length; i++) { + write_name.push_back(static_cast(0)); + } + + hci->sendHciCommand(write_name); + + // Check the loopback of the HCI packet + ASSERT_NO_FATAL_FAILURE(wait_for_event()); + + std::vector event; + ASSERT_TRUE(event_queue.pop(event)); + + size_t compare_length = (write_name.size() > static_cast(0xff) + ? static_cast(0xff) + : write_name.size()); + ASSERT_GT(event.size(), compare_length + kEventFirstPayloadByte - 1); + + ASSERT_EQ(kEventLoopbackCommand, event[kEventCodeByte]); + ASSERT_EQ(compare_length, event[kEventLengthByte]); + + // Don't compare past the end of the event. + if (compare_length + kEventFirstPayloadByte > event.size()) { + compare_length = event.size() - kEventFirstPayloadByte; + ALOGE("Only comparing %d bytes", static_cast(compare_length)); + } + + if (n == num_packets - 1) { + command_size = write_name.size(); + } + + for (size_t i = 0; i < compare_length; i++) { + ASSERT_EQ(write_name[i], event[kEventFirstPayloadByte + i]); + } + } + logger.setTotalBytes(command_size * num_packets * 2); +} + +// Send a SCO data packet (in Loopback mode) and check the response. +void BluetoothAidlTest::sendAndCheckSco(int num_packets, size_t size, + uint16_t handle) { + ThroughputLogger logger = {__func__}; + for (int n = 0; n < num_packets; n++) { + // Send a SCO packet + std::vector sco_packet; + sco_packet.push_back(static_cast(handle & 0xff)); + sco_packet.push_back(static_cast((handle & 0x0f00) >> 8)); + sco_packet.push_back(static_cast(size & 0xff)); + for (size_t i = 0; i < size; i++) { + sco_packet.push_back(static_cast(i + n)); + } + hci->sendScoData(sco_packet); + + // Check the loopback of the SCO packet + std::vector sco_loopback; + ASSERT_TRUE( + sco_queue.tryPopWithTimeout(sco_loopback, kWaitForScoDataTimeout)); + + ASSERT_EQ(sco_packet.size(), sco_loopback.size()); + size_t successful_bytes = 0; + + for (size_t i = 0; i < sco_packet.size(); i++) { + if (sco_packet[i] == sco_loopback[i]) { + successful_bytes = i; + } else { + ALOGE("Miscompare at %d (expected %x, got %x)", static_cast(i), + sco_packet[i], sco_loopback[i]); + ALOGE("At %d (expected %x, got %x)", static_cast(i + 1), + sco_packet[i + 1], sco_loopback[i + 1]); + break; + } + } + ASSERT_EQ(sco_packet.size(), successful_bytes + 1); + } + logger.setTotalBytes(num_packets * size * 2); +} + +// Send an ACL data packet (in Loopback mode) and check the response. +void BluetoothAidlTest::sendAndCheckAcl(int num_packets, size_t size, + uint16_t handle) { + ThroughputLogger logger = {__func__}; + for (int n = 0; n < num_packets; n++) { + // Send an ACL packet + std::vector acl_packet; + acl_packet.push_back(static_cast(handle & 0xff)); + acl_packet.push_back(static_cast((handle & 0x0f00) >> 8) | + kAclBroadcastPointToPoint | + kAclPacketBoundaryFirstAutoFlushable); + acl_packet.push_back(static_cast(size & 0xff)); + acl_packet.push_back(static_cast((size & 0xff00) >> 8)); + for (size_t i = 0; i < size; i++) { + acl_packet.push_back(static_cast(i + n)); + } + hci->sendAclData(acl_packet); + + std::vector acl_loopback; + // Check the loopback of the ACL packet + ASSERT_TRUE( + acl_queue.tryPopWithTimeout(acl_loopback, kWaitForAclDataTimeout)); + + ASSERT_EQ(acl_packet.size(), acl_loopback.size()); + size_t successful_bytes = 0; + + for (size_t i = 0; i < acl_packet.size(); i++) { + if (acl_packet[i] == acl_loopback[i]) { + successful_bytes = i; + } else { + ALOGE("Miscompare at %d (expected %x, got %x)", static_cast(i), + acl_packet[i], acl_loopback[i]); + ALOGE("At %d (expected %x, got %x)", static_cast(i + 1), + acl_packet[i + 1], acl_loopback[i + 1]); + break; + } + } + ASSERT_EQ(acl_packet.size(), successful_bytes + 1); + } + logger.setTotalBytes(num_packets * size * 2); +} + // Return the number of completed packets reported by the controller. int BluetoothAidlTest::wait_for_completed_packets_event(uint16_t handle) { int packets_processed = 0; @@ -429,11 +630,8 @@ int BluetoothAidlTest::wait_for_completed_packets_event(uint16_t handle) { return packets_processed; } -// Empty test: Initialize()/Close() are called in SetUp()/TearDown(). -TEST_P(BluetoothAidlTest, InitializeAndClose) {} - -// Send an HCI Reset with sendHciCommand and wait for a command complete event. -TEST_P(BluetoothAidlTest, HciReset) { +// Send the reset command and wait for a response. +void BluetoothAidlTest::reset() { std::vector reset{kCommandHciReset, kCommandHciReset + sizeof(kCommandHciReset)}; hci->sendHciCommand(reset); @@ -441,17 +639,74 @@ TEST_P(BluetoothAidlTest, HciReset) { wait_for_command_complete_event(reset); } +// Send local loopback command and initialize SCO and ACL handles. +void BluetoothAidlTest::enterLoopbackMode() { + std::vector cmd{kCommandHciWriteLoopbackModeLocal, + kCommandHciWriteLoopbackModeLocal + + sizeof(kCommandHciWriteLoopbackModeLocal)}; + hci->sendHciCommand(cmd); + + // Receive connection complete events with data channels + int connection_event_count = 0; + bool command_complete_received = false; + while (true) { + wait_for_event(false); + if (event_queue.empty()) { + // Fail if there was no event received or no connections completed. + ASSERT_TRUE(command_complete_received); + ASSERT_LT(0, connection_event_count); + return; + } + std::vector event; + ASSERT_TRUE(event_queue.pop(event)); + ASSERT_GT(event.size(), + static_cast(kEventCommandCompleteStatusByte)); + if (event[kEventCodeByte] == kEventConnectionComplete) { + ASSERT_GT(event.size(), + static_cast(kEventConnectionCompleteType)); + ASSERT_EQ(event[kEventLengthByte], kEventConnectionCompleteParamLength); + uint8_t connection_type = event[kEventConnectionCompleteType]; + + ASSERT_TRUE(connection_type == kEventConnectionCompleteTypeSco || + connection_type == kEventConnectionCompleteTypeAcl); + + // Save handles + uint16_t handle = event[kEventConnectionCompleteHandleLsByte] | + event[kEventConnectionCompleteHandleLsByte + 1] << 8; + if (connection_type == kEventConnectionCompleteTypeSco) { + sco_connection_handles.push_back(handle); + } else { + acl_connection_handles.push_back(handle); + } + + ALOGD("Connect complete type = %d handle = %d", + event[kEventConnectionCompleteType], handle); + connection_event_count++; + } else { + ASSERT_EQ(kEventCommandComplete, event[kEventCodeByte]); + ASSERT_EQ(cmd[0], event[kEventCommandCompleteOpcodeLsByte]); + ASSERT_EQ(cmd[1], event[kEventCommandCompleteOpcodeLsByte + 1]); + ASSERT_EQ(kHciStatusSuccess, event[kEventCommandCompleteStatusByte]); + command_complete_received = true; + } + } +} + +// Empty test: Initialize()/Close() are called in SetUp()/TearDown(). +TEST_P(BluetoothAidlTest, InitializeAndClose) {} + +// Send an HCI Reset with sendHciCommand and wait for a command complete event. +TEST_P(BluetoothAidlTest, HciReset) { reset(); } + // Read and check the HCI version of the controller. TEST_P(BluetoothAidlTest, HciVersionTest) { + reset(); std::vector cmd{kCommandHciReadLocalVersionInformation, kCommandHciReadLocalVersionInformation + sizeof(kCommandHciReadLocalVersionInformation)}; hci->sendHciCommand(cmd); - wait_for_event(); - if (event_queue.empty()) { - return; - } + ASSERT_NO_FATAL_FAILURE(wait_for_event()); std::vector event; ASSERT_TRUE(event_queue.pop(event)); @@ -468,15 +723,13 @@ TEST_P(BluetoothAidlTest, HciVersionTest) { // Send an unknown HCI command and wait for the error message. TEST_P(BluetoothAidlTest, HciUnknownCommand) { + reset(); std::vector cmd{ kCommandHciShouldBeUnknown, kCommandHciShouldBeUnknown + sizeof(kCommandHciShouldBeUnknown)}; hci->sendHciCommand(cmd); - wait_for_event(); - if (event_queue.empty()) { - return; - } + ASSERT_NO_FATAL_FAILURE(wait_for_event()); std::vector event; ASSERT_TRUE(event_queue.pop(event)); @@ -496,8 +749,121 @@ TEST_P(BluetoothAidlTest, HciUnknownCommand) { } } +// Enter loopback mode, but don't send any packets. +TEST_P(BluetoothAidlTest, WriteLoopbackMode) { + reset(); + enterLoopbackMode(); +} + +// Enter loopback mode and send a single command. +TEST_P(BluetoothAidlTest, LoopbackModeSingleCommand) { + reset(); + setBufferSizes(); + + enterLoopbackMode(); + + sendAndCheckHci(1); +} + +// Enter loopback mode and send a single SCO packet. +TEST_P(BluetoothAidlTest, LoopbackModeSingleSco) { + reset(); + setBufferSizes(); + setSynchronousFlowControlEnable(); + + enterLoopbackMode(); + + if (!sco_connection_handles.empty()) { + ASSERT_LT(0, max_sco_data_packet_length); + sendAndCheckSco(1, max_sco_data_packet_length, sco_connection_handles[0]); + int sco_packets_sent = 1; + int completed_packets = + wait_for_completed_packets_event(sco_connection_handles[0]); + if (sco_packets_sent != completed_packets) { + ALOGW("%s: packets_sent (%d) != completed_packets (%d)", __func__, + sco_packets_sent, completed_packets); + } + } +} + +// Enter loopback mode and send a single ACL packet. +TEST_P(BluetoothAidlTest, LoopbackModeSingleAcl) { + reset(); + setBufferSizes(); + + enterLoopbackMode(); + + if (!acl_connection_handles.empty()) { + ASSERT_LT(0, max_acl_data_packet_length); + sendAndCheckAcl(1, max_acl_data_packet_length - 1, + acl_connection_handles[0]); + int acl_packets_sent = 1; + int completed_packets = + wait_for_completed_packets_event(acl_connection_handles[0]); + if (acl_packets_sent != completed_packets) { + ALOGW("%s: packets_sent (%d) != completed_packets (%d)", __func__, + acl_packets_sent, completed_packets); + } + } + ASSERT_GE(acl_cb_count, 1); +} + +// Enter loopback mode and send command packets for bandwidth measurements. +TEST_P(BluetoothAidlTest, LoopbackModeCommandBandwidth) { + reset(); + setBufferSizes(); + + enterLoopbackMode(); + + sendAndCheckHci(kNumHciCommandsBandwidth); +} + +// Enter loopback mode and send SCO packets for bandwidth measurements. +TEST_P(BluetoothAidlTest, LoopbackModeScoBandwidth) { + reset(); + setBufferSizes(); + setSynchronousFlowControlEnable(); + + enterLoopbackMode(); + + if (!sco_connection_handles.empty()) { + ASSERT_LT(0, max_sco_data_packet_length); + sendAndCheckSco(kNumScoPacketsBandwidth, max_sco_data_packet_length, + sco_connection_handles[0]); + int sco_packets_sent = kNumScoPacketsBandwidth; + int completed_packets = + wait_for_completed_packets_event(sco_connection_handles[0]); + if (sco_packets_sent != completed_packets) { + ALOGW("%s: packets_sent (%d) != completed_packets (%d)", __func__, + sco_packets_sent, completed_packets); + } + } +} + +// Enter loopback mode and send packets for ACL bandwidth measurements. +TEST_P(BluetoothAidlTest, LoopbackModeAclBandwidth) { + reset(); + setBufferSizes(); + + enterLoopbackMode(); + + if (!acl_connection_handles.empty()) { + ASSERT_LT(0, max_acl_data_packet_length); + sendAndCheckAcl(kNumAclPacketsBandwidth, max_acl_data_packet_length - 1, + acl_connection_handles[0]); + int acl_packets_sent = kNumAclPacketsBandwidth; + int completed_packets = + wait_for_completed_packets_event(acl_connection_handles[0]); + if (acl_packets_sent != completed_packets) { + ALOGW("%s: packets_sent (%d) != completed_packets (%d)", __func__, + acl_packets_sent, completed_packets); + } + } +} + // Set all bits in the event mask TEST_P(BluetoothAidlTest, SetEventMask) { + reset(); std::vector set_event_mask{ 0x01, 0x0c, 0x08 /*parameter bytes*/, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; @@ -507,6 +873,7 @@ TEST_P(BluetoothAidlTest, SetEventMask) { // Set all bits in the LE event mask TEST_P(BluetoothAidlTest, SetLeEventMask) { + reset(); std::vector set_event_mask{ 0x20, 0x0c, 0x08 /*parameter bytes*/, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};