From b1e8fbd3107b5a6e23d0094705ff3dd0221c4431 Mon Sep 17 00:00:00 2001 From: hmz007 Date: Tue, 19 Dec 2023 14:15:21 +0800 Subject: [PATCH] bt: refresh vendor_libs from aosp main last commit: * b63b23b984 Merge "Add test for attp_build_value_cmd" into main Signed-off-by: hmz007 --- .../bt/vendor_libs/linux/interface/Android.bp | 39 +- .../linux/interface/async_fd_watcher.cc | 5 +- .../linux/interface/bluetooth_hci.cc | 36 +- .../linux/interface/h4_protocol.cc | 86 ++-- .../vendor_libs/linux/interface/h4_protocol.h | 27 +- .../linux/interface/h4_protocol_unittest.cc | 448 ++++++++++++++++++ .../linux/interface/hci_internals.h | 4 + .../linux/interface/hci_packetizer.cc | 114 +++-- .../linux/interface/hci_packetizer.h | 25 +- 9 files changed, 669 insertions(+), 115 deletions(-) create mode 100644 system/bt/vendor_libs/linux/interface/h4_protocol_unittest.cc diff --git a/system/bt/vendor_libs/linux/interface/Android.bp b/system/bt/vendor_libs/linux/interface/Android.bp index 85fde7d4731..f43d9d32ce4 100644 --- a/system/bt/vendor_libs/linux/interface/Android.bp +++ b/system/bt/vendor_libs/linux/interface/Android.bp @@ -27,15 +27,11 @@ cc_binary { proprietary: true, relative_install_path: "hw", srcs: [ - "hci_packetizer.cc", - "h4_protocol.cc", "bluetooth_hci.cc", + "h4_protocol.cc", + "hci_packetizer.cc", "service.cc", ], - cflags: [ - "-Wall", - "-Werror", - ], header_libs: ["libbluetooth_headers"], shared_libs: [ "android.hardware.bluetooth@1.0", @@ -61,14 +57,39 @@ cc_library_static { srcs: [ "async_fd_watcher.cc", ], + shared_libs: [ + "liblog", + ], + export_include_dirs: [ + ".", + ], +} + +cc_test_host { + name: "bluetooth-btlinux-hci-test", + srcs: [ + "async_fd_watcher.cc", + "h4_protocol.cc", + "h4_protocol_unittest.cc", + "hci_packetizer.cc", + ], cflags: [ + "-DNO_THREAD_PRIORITY", "-Wall", "-Werror", ], shared_libs: [ + "libbase", + "libhidlbase", "liblog", + "libutils", ], - export_include_dirs: [ - ".", + static_libs: [ + "libgmock", ], -} \ No newline at end of file + sanitize: { + address: true, + cfi: true, + }, + test_suites: ["general-tests"], +} diff --git a/system/bt/vendor_libs/linux/interface/async_fd_watcher.cc b/system/bt/vendor_libs/linux/interface/async_fd_watcher.cc index 87d2506f146..12cc04483c3 100644 --- a/system/bt/vendor_libs/linux/interface/async_fd_watcher.cc +++ b/system/bt/vendor_libs/linux/interface/async_fd_watcher.cc @@ -29,7 +29,9 @@ #include "unistd.h" static const int INVALID_FD = -1; +#ifndef NO_THREAD_PRIORITY static const int BT_RT_PRIORITY = 1; +#endif namespace android { namespace hardware { @@ -117,7 +119,7 @@ int AsyncFdWatcher::notifyThread() { } void AsyncFdWatcher::ThreadRoutine() { - +#ifndef NO_THREAD_PRIORITY // Make watching thread RT. struct sched_param rt_params; rt_params.sched_priority = BT_RT_PRIORITY; @@ -125,6 +127,7 @@ void AsyncFdWatcher::ThreadRoutine() { ALOGE("%s unable to set SCHED_FIFO for pid %d, tid %d, error %s", __func__, getpid(), gettid(), strerror(errno)); } +#endif while (running_) { fd_set read_fds; diff --git a/system/bt/vendor_libs/linux/interface/bluetooth_hci.cc b/system/bt/vendor_libs/linux/interface/bluetooth_hci.cc index fe5a86a1724..a437b6cbeab 100644 --- a/system/bt/vendor_libs/linux/interface/bluetooth_hci.cc +++ b/system/bt/vendor_libs/linux/interface/bluetooth_hci.cc @@ -15,18 +15,17 @@ // #define LOG_TAG "android.hardware.bluetooth@1.1-btlinux" -#include +#include "bluetooth_hci.h" + #include #include #include #include #include - #include - #include -#include "bluetooth_hci.h" +#include #define BTPROTO_HCI 1 @@ -301,12 +300,31 @@ Return BluetoothHci::initialize_impl( } hci::H4Protocol* h4_hci = new hci::H4Protocol( hci_fd, - [cb](const hidl_vec& packet) { cb->hciEventReceived(packet); }, - [cb](const hidl_vec& packet) { cb->aclDataReceived(packet); }, - [cb](const hidl_vec& packet) { cb->scoDataReceived(packet); }, + [cb](const hidl_vec& packet) { + auto status = cb->hciEventReceived(packet); + if (!status.isOk()) { + ALOGE("VendorInterface -> Unable to call hciEventReceived"); + } + }, + [cb](const hidl_vec& packet) { + auto status = cb->aclDataReceived(packet); + if (!status.isOk()) { + ALOGE("VendorInterface -> Unable to call hciAclReceived"); + } + }, + [cb](const hidl_vec& packet) { + auto status = cb->scoDataReceived(packet); + if (!status.isOk()) { + ALOGE("VendorInterface -> Unable to call hciScoReceived"); + } + }, [cb_1_1](const hidl_vec& packet) { - cb_1_1->isoDataReceived(packet); - }); + auto status = cb_1_1->isoDataReceived(packet); + if (!status.isOk()) { + ALOGE("VendorInterface -> Unable to call hciIsoReceived"); + } + }, + []() { ALOGE("UART disconnected."); }); fd_watcher_.WatchFdForNonBlockingReads( hci_fd, [h4_hci](int fd) { h4_hci->OnDataReady(fd); }); diff --git a/system/bt/vendor_libs/linux/interface/h4_protocol.cc b/system/bt/vendor_libs/linux/interface/h4_protocol.cc index e7c0f6289f3..2ff11fe27f0 100644 --- a/system/bt/vendor_libs/linux/interface/h4_protocol.cc +++ b/system/bt/vendor_libs/linux/interface/h4_protocol.cc @@ -28,6 +28,17 @@ namespace hardware { namespace bluetooth { namespace hci { +H4Protocol::H4Protocol(int fd, PacketReadCallback event_cb, + PacketReadCallback acl_cb, PacketReadCallback sco_cb, + PacketReadCallback iso_cb, + OnDisconnectCallback disconnect_cb) + : uart_fd_(fd), + event_cb_(std::move(event_cb)), + acl_cb_(std::move(acl_cb)), + sco_cb_(std::move(sco_cb)), + iso_cb_(std::move(iso_cb)), + disconnect_cb_(std::move(disconnect_cb)) {} + size_t H4Protocol::Send(uint8_t type, const uint8_t* data, size_t length) { /* For HCI communication over USB dongle, multiple write results in * response timeout as driver expect type + data at once to process @@ -56,51 +67,68 @@ size_t H4Protocol::Send(uint8_t type, const uint8_t* data, size_t length) { return ret; } -void H4Protocol::OnPacketReady() { +size_t H4Protocol::on_packet_ready(const hidl_vec& packet) { switch (hci_packet_type_) { case HCI_PACKET_TYPE_EVENT: - event_cb_(hci_packetizer_.GetPacket()); + event_cb_(packet); break; case HCI_PACKET_TYPE_ACL_DATA: - acl_cb_(hci_packetizer_.GetPacket()); + acl_cb_(packet); break; case HCI_PACKET_TYPE_SCO_DATA: - sco_cb_(hci_packetizer_.GetPacket()); + sco_cb_(packet); break; case HCI_PACKET_TYPE_ISO_DATA: - iso_cb_(hci_packetizer_.GetPacket()); + iso_cb_(packet); break; default: { - bool bad_packet_type = true; - CHECK(!bad_packet_type); + LOG_ALWAYS_FATAL("Unhandled packet of type 0x%x", hci_packet_type_); } } - // Get ready for the next type byte. - hci_packet_type_ = HCI_PACKET_TYPE_UNKNOWN; + return packet.size(); } -void H4Protocol::OnDataReady(int fd) { +void H4Protocol::send_data_to_packetizer(uint8_t* buffer, size_t length) { + std::vector input_buffer{buffer, buffer + length}; + size_t buffer_offset = 0; + while (buffer_offset < input_buffer.size()) { if (hci_packet_type_ == HCI_PACKET_TYPE_UNKNOWN) { - /** - * read full buffer. ACL max length is 2 bytes, and SCO max length is 2 - * byte. so taking 64K as buffer length. - * Question : Why to read in single chunk rather than multiple reads, - * which can give parameter length arriving in response ? - * Answer: The multiple reads does not work with BT USB dongle. At least - * with Bluetooth 2.0 supported USB dongle. After first read, either - * firmware/kernel (do not know who is responsible - inputs ??) driver - * discard the whole message and successive read results in forever - * blocking loop. - Is there any other way to make it work with multiple - * reads, do not know yet (it can eliminate need of this function) ? - * Reading in single shot gives expected response. - */ - const size_t max_plen = 64*1024; - hidl_vec tpkt; - tpkt.resize(max_plen); - size_t bytes_read = TEMP_FAILURE_RETRY(read(fd, tpkt.data(), max_plen)); - hci_packet_type_ = static_cast(tpkt.data()[0]); - hci_packetizer_.CbHciPacket(tpkt.data()+1, bytes_read-1); + hci_packet_type_ = + static_cast(input_buffer.data()[buffer_offset]); + buffer_offset += 1; + } else { + bool packet_ready = hci_packetizer_.OnDataReady( + hci_packet_type_, input_buffer, buffer_offset); + if (packet_ready) { + // Call packet callback and move offset. + buffer_offset += on_packet_ready(hci_packetizer_.GetPacket()); + // Get ready for the next type byte. + hci_packet_type_ = HCI_PACKET_TYPE_UNKNOWN; + } else { + // The data was consumed, but there wasn't a packet. + buffer_offset = input_buffer.size(); + } } + } +} + +void H4Protocol::OnDataReady(int fd) { + if (disconnected_) { + return; + } + uint8_t buffer[kMaxPacketLength]; + ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, kMaxPacketLength)); + if (bytes_read == 0) { + ALOGI("No bytes read, calling the disconnect callback"); + disconnected_ = true; + disconnect_cb_(); + return; + } + if (bytes_read < 0) { + ALOGW("error reading from UART (%s)", strerror(errno)); + return; + } + send_data_to_packetizer(buffer, bytes_read); } } // namespace hci diff --git a/system/bt/vendor_libs/linux/interface/h4_protocol.h b/system/bt/vendor_libs/linux/interface/h4_protocol.h index 92d6698de59..9cf42ffea0c 100644 --- a/system/bt/vendor_libs/linux/interface/h4_protocol.h +++ b/system/bt/vendor_libs/linux/interface/h4_protocol.h @@ -29,34 +29,43 @@ namespace hci { using ::android::hardware::hidl_vec; using PacketReadCallback = std::function&)>; +using OnDisconnectCallback = std::function; class H4Protocol { public: H4Protocol(int fd, PacketReadCallback event_cb, PacketReadCallback acl_cb, - PacketReadCallback sco_cb, PacketReadCallback iso_cb) - : uart_fd_(fd), - event_cb_(event_cb), - acl_cb_(acl_cb), - sco_cb_(sco_cb), - iso_cb_(iso_cb), - hci_packetizer_([this]() { OnPacketReady(); }) {} + PacketReadCallback sco_cb, PacketReadCallback iso_cb, + OnDisconnectCallback disconnect_cb); - size_t Send(uint8_t type, const uint8_t* data, size_t length); + virtual ~H4Protocol() {} - void OnPacketReady(); + size_t Send(uint8_t type, const uint8_t* data, size_t length); void OnDataReady(int fd); private: int uart_fd_; + bool disconnected_{false}; + + size_t on_packet_ready(const hidl_vec& packet); + void send_data_to_packetizer(uint8_t* buffer, size_t length); PacketReadCallback event_cb_; PacketReadCallback acl_cb_; PacketReadCallback sco_cb_; PacketReadCallback iso_cb_; + OnDisconnectCallback disconnect_cb_; HciPacketType hci_packet_type_{HCI_PACKET_TYPE_UNKNOWN}; HciPacketizer hci_packetizer_; + + /** + * Question : Why read in single chunk rather than multiple reads? + * Answer: Using multiple reads does not work with some BT USB dongles. + * Reading in single shot gives expected response. + * ACL max length is 2 bytes, so using 64K as the buffer length. + */ + static constexpr size_t kMaxPacketLength = 64 * 1024; }; } // namespace hci diff --git a/system/bt/vendor_libs/linux/interface/h4_protocol_unittest.cc b/system/bt/vendor_libs/linux/interface/h4_protocol_unittest.cc new file mode 100644 index 00000000000..882b4b57a6f --- /dev/null +++ b/system/bt/vendor_libs/linux/interface/h4_protocol_unittest.cc @@ -0,0 +1,448 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "bt_h4_unittest" + +#include "h4_protocol.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "async_fd_watcher.h" +#include "log/log.h" + +using android::hardware::bluetooth::async::AsyncFdWatcher; +using android::hardware::bluetooth::hci::H4Protocol; +using ::testing::Eq; + +static char sample_data1[100] = "A point is that which has no part."; +static char sample_data2[100] = "A line is breadthless length."; +static char sample_data3[100] = "The ends of a line are points."; +static char sample_data4[100] = + "A plane surface is a surface which lies evenly with the straight ..."; +static char acl_data[100] = + "A straight line is a line which lies evenly with the points on itself."; +static char sco_data[100] = + "A surface is that which has length and breadth only."; +static char event_data[100] = "The edges of a surface are lines."; +static char iso_data[100] = + "A plane angle is the inclination to one another of two lines in a ..."; + +// 5 seconds. Just don't hang. +static constexpr size_t kTimeoutMs = 5000; + +MATCHER_P3(PacketMatches, header_, header_length, payload, + "Match header_length bytes of header and then the payload") { + size_t payload_length = strlen(payload); + if (header_length + payload_length != arg.size()) { + return false; + } + + if (memcmp(header_, arg.data(), header_length) != 0) { + return false; + } + + return memcmp(payload, arg.data() + header_length, payload_length) == 0; +}; + +ACTION_P(Notify, barrier) { + ALOGD("%s", __func__); + barrier->set_value(); +} + +class H4ProtocolTest : public ::testing::Test { + protected: + void SetUp() override { + ALOGD("%s", __func__); + + int sockfd[2]; + socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd); + chip_uart_fd_ = sockfd[1]; + stack_uart_fd_ = sockfd[0]; + h4_hci_ = std::make_shared( + stack_uart_fd_, event_cb_.AsStdFunction(), acl_cb_.AsStdFunction(), + sco_cb_.AsStdFunction(), iso_cb_.AsStdFunction(), + disconnect_cb_.AsStdFunction()); + } + + void TearDown() override { + close(stack_uart_fd_); + close(chip_uart_fd_); + } + + virtual void CallDataReady() { h4_hci_->OnDataReady(stack_uart_fd_); } + + void SendAndReadUartOutbound(HciPacketType type, char* data) { + ALOGD("%s sending", __func__); + int data_length = strlen(data); + h4_hci_->Send(type, (uint8_t*)data, data_length); + + int uart_length = data_length + 1; // + 1 for data type code + int i; + + ALOGD("%s reading", __func__); + for (i = 0; i < uart_length; i++) { + fd_set read_fds; + FD_ZERO(&read_fds); + FD_SET(chip_uart_fd_, &read_fds); + TEMP_FAILURE_RETRY( + select(chip_uart_fd_ + 1, &read_fds, nullptr, nullptr, nullptr)); + + char byte; + TEMP_FAILURE_RETRY(read(chip_uart_fd_, &byte, 1)); + + EXPECT_EQ(i == 0 ? static_cast(type) : data[i - 1], byte); + } + + EXPECT_EQ(i, uart_length); + } + + void ExpectInboundAclData(char* payload, std::promise* promise) { + // h4 type[1] + handle[2] + size[2] + header_[0] = static_cast(HCI_PACKET_TYPE_ACL_DATA); + header_[1] = 19; + header_[2] = 92; + int length = strlen(payload); + header_[3] = length & 0xFF; + header_[4] = (length >> 8) & 0xFF; + ALOGD("(%d bytes) %s", length, payload); + + EXPECT_CALL(acl_cb_, Call(PacketMatches(header_ + 1, HCI_ACL_PREAMBLE_SIZE, + payload))) + .WillOnce(Notify(promise)); + } + + void WaitForTimeout(std::promise* promise) { + auto future = promise->get_future(); + auto status = future.wait_for(std::chrono::milliseconds(kTimeoutMs)); + EXPECT_EQ(status, std::future_status::ready); + } + + void WriteInboundAclData(char* payload) { + // Use the header_ computed in ExpectInboundAclData + TEMP_FAILURE_RETRY( + write(chip_uart_fd_, header_, HCI_ACL_PREAMBLE_SIZE + 1)); + TEMP_FAILURE_RETRY(write(chip_uart_fd_, payload, strlen(payload))); + } + + void ExpectInboundScoData(char* payload, std::promise* promise) { + // h4 type[1] + handle[2] + size[1] + header_[0] = static_cast(HCI_PACKET_TYPE_SCO_DATA); + header_[1] = 20; + header_[2] = 17; + header_[3] = strlen(payload) & 0xFF; + EXPECT_CALL(sco_cb_, Call(PacketMatches(header_ + 1, HCI_SCO_PREAMBLE_SIZE, + payload))) + .WillOnce(Notify(promise)); + } + + void WriteInboundScoData(char* payload) { + // Use the header_ computed in ExpectInboundScoData + ALOGD("%s writing", __func__); + TEMP_FAILURE_RETRY( + write(chip_uart_fd_, header_, HCI_SCO_PREAMBLE_SIZE + 1)); + TEMP_FAILURE_RETRY(write(chip_uart_fd_, payload, strlen(payload))); + } + + void ExpectInboundEvent(char* payload, std::promise* promise) { + // h4 type[1] + event_code[1] + size[1] + header_[0] = static_cast(HCI_PACKET_TYPE_EVENT); + header_[1] = 9; + header_[2] = strlen(payload) & 0xFF; + EXPECT_CALL(event_cb_, Call(PacketMatches( + header_ + 1, HCI_EVENT_PREAMBLE_SIZE, payload))) + .WillOnce(Notify(promise)); + } + + void WriteInboundEvent(char* payload) { + // Use the header_ computed in ExpectInboundEvent + char preamble[3] = {static_cast(HCI_PACKET_TYPE_EVENT), 9, 0}; + preamble[2] = strlen(payload) & 0xFF; + ALOGD("%s writing", __func__); + TEMP_FAILURE_RETRY( + write(chip_uart_fd_, header_, HCI_EVENT_PREAMBLE_SIZE + 1)); + TEMP_FAILURE_RETRY(write(chip_uart_fd_, payload, strlen(payload))); + } + + void ExpectInboundIsoData(char* payload, std::promise* promise) { + // h4 type[1] + handle[2] + size[1] + header_[0] = static_cast(HCI_PACKET_TYPE_ISO_DATA); + header_[1] = 19; + header_[2] = 92; + int length = strlen(payload); + header_[3] = length & 0xFF; + header_[4] = (length >> 8) & 0x3F; + + EXPECT_CALL(iso_cb_, Call(PacketMatches(header_ + 1, HCI_ISO_PREAMBLE_SIZE, + payload))) + .WillOnce(Notify(promise)); + } + + void WriteInboundIsoData(char* payload) { + // Use the header_ computed in ExpectInboundIsoData + ALOGD("%s writing", __func__); + TEMP_FAILURE_RETRY( + write(chip_uart_fd_, header_, HCI_ISO_PREAMBLE_SIZE + 1)); + TEMP_FAILURE_RETRY(write(chip_uart_fd_, payload, strlen(payload))); + } + + void WriteAndExpectManyInboundAclDataPackets(char* payload) { + size_t kNumPackets = 20; + // h4 type[1] + handle[2] + size[2] + char preamble[5] = {static_cast(HCI_PACKET_TYPE_ACL_DATA), 19, 92, + 0, 0}; + int length = strlen(payload); + preamble[3] = length & 0xFF; + preamble[4] = (length >> 8) & 0xFF; + + EXPECT_CALL(acl_cb_, Call(PacketMatches(preamble + 1, sizeof(preamble) - 1, + payload))) + .Times(kNumPackets); + + for (size_t i = 0; i < kNumPackets; i++) { + TEMP_FAILURE_RETRY(write(chip_uart_fd_, preamble, sizeof(preamble))); + TEMP_FAILURE_RETRY(write(chip_uart_fd_, payload, strlen(payload))); + } + + CallDataReady(); + } + + testing::MockFunction&)> cmd_cb_; + testing::MockFunction&)> event_cb_; + testing::MockFunction&)> acl_cb_; + testing::MockFunction&)> sco_cb_; + testing::MockFunction&)> iso_cb_; + testing::MockFunction disconnect_cb_; + std::shared_ptr h4_hci_; + int chip_uart_fd_; + int stack_uart_fd_; + + char header_[5]; +}; + +// Test sending data sends correct data onto the UART +TEST_F(H4ProtocolTest, TestSends) { + SendAndReadUartOutbound(HCI_PACKET_TYPE_COMMAND, sample_data1); + SendAndReadUartOutbound(HCI_PACKET_TYPE_ACL_DATA, sample_data2); + SendAndReadUartOutbound(HCI_PACKET_TYPE_SCO_DATA, sample_data3); + SendAndReadUartOutbound(HCI_PACKET_TYPE_ISO_DATA, sample_data4); +} + +// Ensure we properly parse data coming from the UART +TEST_F(H4ProtocolTest, TestReads) { + std::promise acl_promise; + std::promise sco_promise; + std::promise event_promise; + std::promise iso_promise; + + ExpectInboundAclData(acl_data, &acl_promise); + WriteInboundAclData(acl_data); + CallDataReady(); + ExpectInboundScoData(sco_data, &sco_promise); + WriteInboundScoData(sco_data); + CallDataReady(); + ExpectInboundEvent(event_data, &event_promise); + WriteInboundEvent(event_data); + CallDataReady(); + ExpectInboundIsoData(iso_data, &iso_promise); + WriteInboundIsoData(iso_data); + CallDataReady(); + + WaitForTimeout(&acl_promise); + WaitForTimeout(&sco_promise); + WaitForTimeout(&event_promise); + WaitForTimeout(&iso_promise); +} + +TEST_F(H4ProtocolTest, TestMultiplePackets) { + WriteAndExpectManyInboundAclDataPackets(sco_data); +} + +TEST_F(H4ProtocolTest, TestDisconnect) { + EXPECT_CALL(disconnect_cb_, Call()); + close(chip_uart_fd_); + CallDataReady(); +} + +TEST_F(H4ProtocolTest, TestPartialWrites) { + size_t payload_len = strlen(acl_data); + const size_t kNumIntervals = payload_len + 1; + // h4 type[1] + handle[2] + size[2] + header_[0] = static_cast(HCI_PACKET_TYPE_ACL_DATA); + header_[1] = 19; + header_[2] = 92; + header_[3] = payload_len & 0xFF; + header_[4] = (payload_len >> 8) & 0xFF; + + EXPECT_CALL(acl_cb_, + Call(PacketMatches(header_ + 1, sizeof(header_) - 1, acl_data))) + .Times(kNumIntervals); + + for (size_t interval = 1; interval < kNumIntervals + 1; interval++) { + // Use the header_ data that expect already set up. + if (interval < HCI_ACL_PREAMBLE_SIZE) { + TEMP_FAILURE_RETRY(write(chip_uart_fd_, header_, interval)); + CallDataReady(); + TEMP_FAILURE_RETRY(write(chip_uart_fd_, header_ + interval, + HCI_ACL_PREAMBLE_SIZE + 1 - interval)); + CallDataReady(); + } else { + TEMP_FAILURE_RETRY( + write(chip_uart_fd_, header_, HCI_ACL_PREAMBLE_SIZE + 1)); + CallDataReady(); + } + + for (size_t bytes = 0; bytes + interval <= payload_len; bytes += interval) { + TEMP_FAILURE_RETRY(write(chip_uart_fd_, acl_data + bytes, interval)); + CallDataReady(); + } + size_t extra_bytes = payload_len % interval; + if (extra_bytes) { + TEMP_FAILURE_RETRY(write( + chip_uart_fd_, acl_data + payload_len - extra_bytes, extra_bytes)); + CallDataReady(); + } + } +} + +class H4ProtocolAsyncTest : public H4ProtocolTest { + protected: + void SetUp() override { + H4ProtocolTest::SetUp(); + fd_watcher_.WatchFdForNonBlockingReads( + stack_uart_fd_, [this](int fd) { h4_hci_->OnDataReady(fd); }); + } + + void TearDown() override { fd_watcher_.StopWatchingFileDescriptors(); } + + void CallDataReady() override { + // The Async test can't call data ready. + FAIL(); + } + + void SendAndReadUartOutbound(HciPacketType type, char* data) { + ALOGD("%s sending", __func__); + int data_length = strlen(data); + h4_hci_->Send(type, (uint8_t*)data, data_length); + + int uart_length = data_length + 1; // + 1 for data type code + int i; + + ALOGD("%s reading", __func__); + for (i = 0; i < uart_length; i++) { + fd_set read_fds; + FD_ZERO(&read_fds); + FD_SET(chip_uart_fd_, &read_fds); + TEMP_FAILURE_RETRY( + select(chip_uart_fd_ + 1, &read_fds, nullptr, nullptr, nullptr)); + + char byte; + TEMP_FAILURE_RETRY(read(chip_uart_fd_, &byte, 1)); + + EXPECT_EQ(i == 0 ? static_cast(type) : data[i - 1], byte); + } + + EXPECT_EQ(i, uart_length); + } + + void WriteAndExpectInboundAclData(char* payload) { + std::promise promise; + ExpectInboundAclData(payload, &promise); + WriteInboundAclData(payload); + WaitForTimeout(&promise); + } + + void WriteAndExpectInboundScoData(char* payload) { + std::promise promise; + ExpectInboundScoData(payload, &promise); + WriteInboundScoData(payload); + WaitForTimeout(&promise); + } + + void WriteAndExpectInboundEvent(char* payload) { + std::promise promise; + ExpectInboundEvent(payload, &promise); + WriteInboundEvent(payload); + WaitForTimeout(&promise); + } + + void WriteAndExpectInboundIsoData(char* payload) { + std::promise promise; + ExpectInboundIsoData(payload, &promise); + WriteInboundIsoData(payload); + WaitForTimeout(&promise); + } + + void WriteAndExpectManyInboundAclDataPackets(char* payload) { + const size_t kNumPackets = 20; + // h4 type[1] + handle[2] + size[2] + char preamble[5] = {static_cast(HCI_PACKET_TYPE_ACL_DATA), 19, 92, + 0, 0}; + int length = strlen(payload); + preamble[3] = length & 0xFF; + preamble[4] = (length >> 8) & 0xFF; + + EXPECT_CALL(acl_cb_, Call(PacketMatches(preamble + 1, sizeof(preamble) - 1, + payload))) + .Times(kNumPackets); + + for (size_t i = 0; i < kNumPackets; i++) { + TEMP_FAILURE_RETRY(write(chip_uart_fd_, preamble, sizeof(preamble))); + TEMP_FAILURE_RETRY(write(chip_uart_fd_, payload, strlen(payload))); + } + + WriteAndExpectInboundEvent(event_data); + } + + AsyncFdWatcher fd_watcher_; +}; + +// Test sending data sends correct data onto the UART +TEST_F(H4ProtocolAsyncTest, TestSends) { + SendAndReadUartOutbound(HCI_PACKET_TYPE_COMMAND, sample_data1); + SendAndReadUartOutbound(HCI_PACKET_TYPE_ACL_DATA, sample_data2); + SendAndReadUartOutbound(HCI_PACKET_TYPE_SCO_DATA, sample_data3); + SendAndReadUartOutbound(HCI_PACKET_TYPE_ISO_DATA, sample_data4); +} + +// Ensure we properly parse data coming from the UART +TEST_F(H4ProtocolAsyncTest, TestReads) { + WriteAndExpectInboundAclData(acl_data); + WriteAndExpectInboundScoData(sco_data); + WriteAndExpectInboundEvent(event_data); + WriteAndExpectInboundIsoData(iso_data); +} + +TEST_F(H4ProtocolAsyncTest, TestMultiplePackets) { + WriteAndExpectManyInboundAclDataPackets(sco_data); +} + +TEST_F(H4ProtocolAsyncTest, TestDisconnect) { + std::promise promise; + EXPECT_CALL(disconnect_cb_, Call()).WillOnce(Notify(&promise)); + close(chip_uart_fd_); + + WaitForTimeout(&promise); +} diff --git a/system/bt/vendor_libs/linux/interface/hci_internals.h b/system/bt/vendor_libs/linux/interface/hci_internals.h index 24e944fdecc..5875162464d 100644 --- a/system/bt/vendor_libs/linux/interface/hci_internals.h +++ b/system/bt/vendor_libs/linux/interface/hci_internals.h @@ -44,6 +44,10 @@ const size_t HCI_LENGTH_OFFSET_SCO = 2; const size_t HCI_EVENT_PREAMBLE_SIZE = 2; const size_t HCI_LENGTH_OFFSET_EVT = 1; +// 2 bytes for handle, 2 bytes for data length (Volume 2, Part E, 5.4.5) +const size_t HCI_ISO_PREAMBLE_SIZE = 4; +const size_t HCI_LENGTH_OFFSET_ISO = 2; + const size_t HCI_PREAMBLE_SIZE_MAX = HCI_ACL_PREAMBLE_SIZE; // Event codes (Volume 2, Part E, 7.7.14) diff --git a/system/bt/vendor_libs/linux/interface/hci_packetizer.cc b/system/bt/vendor_libs/linux/interface/hci_packetizer.cc index 0c0a979e725..f3460aa3cf6 100644 --- a/system/bt/vendor_libs/linux/interface/hci_packetizer.cc +++ b/system/bt/vendor_libs/linux/interface/hci_packetizer.cc @@ -17,22 +17,27 @@ #include "hci_packetizer.h" #define LOG_TAG "android.hardware.bluetooth.hci_packetizer" -#include -#include - #include #include +#include namespace { -const size_t preamble_size_for_type[] = { - 0, HCI_COMMAND_PREAMBLE_SIZE, HCI_ACL_PREAMBLE_SIZE, HCI_SCO_PREAMBLE_SIZE, - HCI_EVENT_PREAMBLE_SIZE}; -const size_t packet_length_offset_for_type[] = { - 0, HCI_LENGTH_OFFSET_CMD, HCI_LENGTH_OFFSET_ACL, HCI_LENGTH_OFFSET_SCO, - HCI_LENGTH_OFFSET_EVT}; +const size_t header_size_for_type[] = {0, + HCI_COMMAND_PREAMBLE_SIZE, + HCI_ACL_PREAMBLE_SIZE, + HCI_SCO_PREAMBLE_SIZE, + HCI_EVENT_PREAMBLE_SIZE, + HCI_ISO_PREAMBLE_SIZE}; +const size_t packet_length_offset_for_type[] = {0, + HCI_LENGTH_OFFSET_CMD, + HCI_LENGTH_OFFSET_ACL, + HCI_LENGTH_OFFSET_SCO, + HCI_LENGTH_OFFSET_EVT, + HCI_LENGTH_OFFSET_ISO}; -size_t HciGetPacketLengthForType(HciPacketType type, const uint8_t* preamble) { +size_t HciGetPacketLengthForType(HciPacketType type, + const std::vector& preamble) { size_t offset = packet_length_offset_for_type[type]; if (type != HCI_PACKET_TYPE_ACL_DATA) return preamble[offset]; return (((preamble[offset + 1]) << 8) | preamble[offset]); @@ -47,47 +52,64 @@ namespace hci { const hidl_vec& HciPacketizer::GetPacket() const { return packet_; } -void HciPacketizer::CbHciPacket(uint8_t *data, size_t len) { - packet_.setToExternal(data, len); - packet_ready_cb_(); -} +size_t HciPacketizer::fill_header(HciPacketType packet_type, + const std::vector& buffer, + size_t offset) { + size_t header_size = header_size_for_type[static_cast(packet_type)]; + if (bytes_remaining_ == 0) { + bytes_remaining_ = header_size; + packet_buffer_.clear(); + } + // Add as much of the header as is available to the packet. + size_t bytes_to_copy = std::min(bytes_remaining_, buffer.size() - offset); + packet_buffer_.insert(packet_buffer_.end(), buffer.begin() + offset, + buffer.begin() + offset + bytes_to_copy); + bytes_remaining_ -= bytes_to_copy; -void HciPacketizer::OnDataReady(int fd, HciPacketType packet_type) { - switch (state_) { - case HCI_PREAMBLE: { - size_t bytes_read = TEMP_FAILURE_RETRY( - read(fd, preamble_ + bytes_read_, - preamble_size_for_type[packet_type] - bytes_read_)); - CHECK(bytes_read > 0); - bytes_read_ += bytes_read; - if (bytes_read_ == preamble_size_for_type[packet_type]) { - size_t packet_length = - HciGetPacketLengthForType(packet_type, preamble_); - packet_.resize(preamble_size_for_type[packet_type] + packet_length); - memcpy(packet_.data(), preamble_, preamble_size_for_type[packet_type]); - bytes_remaining_ = packet_length; - state_ = HCI_PAYLOAD; - bytes_read_ = 0; - } - break; + // If the header is complete, find the payload size and transition. + if (bytes_remaining_ == 0) { + bytes_remaining_ = HciGetPacketLengthForType(packet_type, packet_buffer_); + // If there are no bytes remaining, this is a completed packet. + if (bytes_remaining_ > 0) { + state_ = HCI_PAYLOAD; } + } + return bytes_to_copy; +} + +void HciPacketizer::fill_payload(const std::vector& buffer, + size_t offset) { + // Add as much of the payload as is available to the end of the packet. + size_t bytes_to_copy = std::min(bytes_remaining_, buffer.size() - offset); + packet_buffer_.insert(packet_buffer_.end(), buffer.begin() + offset, + buffer.begin() + offset + bytes_to_copy); + bytes_remaining_ -= bytes_to_copy; - case HCI_PAYLOAD: { - size_t bytes_read = TEMP_FAILURE_RETRY(read( - fd, - packet_.data() + preamble_size_for_type[packet_type] + bytes_read_, - bytes_remaining_)); - CHECK(bytes_read > 0); - bytes_remaining_ -= bytes_read; - bytes_read_ += bytes_read; - if (bytes_remaining_ == 0) { - packet_ready_cb_(); - state_ = HCI_PREAMBLE; - bytes_read_ = 0; - } - break; + // If there are no bytes remaining, this is a completed packet. + if (bytes_remaining_ == 0) { + state_ = HCI_HEADER; + } +} + +bool HciPacketizer::OnDataReady(HciPacketType packet_type, + const std::vector& buffer, + size_t offset) { + // Start with the header. + size_t header_bytes = 0; + if (state_ == HCI_HEADER) { + header_bytes = fill_header(packet_type, buffer, offset); + } + // If there are bytes left in this packet, fill the payload. + if (state_ == HCI_PAYLOAD && bytes_remaining_ > 0) { + if (offset + header_bytes < buffer.size()) { + fill_payload(buffer, offset + header_bytes); } } + // If there are no bytes remaining, this is a completed packet. + if (bytes_remaining_ == 0) { + packet_.setToExternal(packet_buffer_.data(), packet_buffer_.size()); + } + return bytes_remaining_ == 0; } } // namespace hci diff --git a/system/bt/vendor_libs/linux/interface/hci_packetizer.h b/system/bt/vendor_libs/linux/interface/hci_packetizer.h index 06de70fea10..998797034c2 100644 --- a/system/bt/vendor_libs/linux/interface/hci_packetizer.h +++ b/system/bt/vendor_libs/linux/interface/hci_packetizer.h @@ -16,10 +16,11 @@ #pragma once -#include - #include +#include +#include + #include "hci_internals.h" namespace android { @@ -32,20 +33,20 @@ using HciPacketReadyCallback = std::function; class HciPacketizer { public: - HciPacketizer(HciPacketReadyCallback packet_cb) - : packet_ready_cb_(packet_cb){}; - void OnDataReady(int fd, HciPacketType packet_type); - void CbHciPacket(uint8_t* data, size_t length); + HciPacketizer() = default; + bool OnDataReady(HciPacketType packet_type, const std::vector& data, + size_t offset); const hidl_vec& GetPacket() const; - protected: - enum State { HCI_PREAMBLE, HCI_PAYLOAD }; - State state_{HCI_PREAMBLE}; - uint8_t preamble_[HCI_PREAMBLE_SIZE_MAX]; + private: + size_t fill_header(HciPacketType packet_type, + const std::vector& data, size_t offset); + void fill_payload(const std::vector& data, size_t offset); + enum State { HCI_HEADER, HCI_PAYLOAD }; + State state_{HCI_HEADER}; hidl_vec packet_; + std::vector packet_buffer_; size_t bytes_remaining_{0}; - size_t bytes_read_{0}; - HciPacketReadyCallback packet_ready_cb_; }; } // namespace hci