| // |
| // Copyright 2017 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 <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| #include <condition_variable> |
| #include <cstdint> |
| #include <cstring> |
| #include <mutex> |
| #include <vector> |
| |
| #include <log/log.h> |
| #include <sys/socket.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| namespace android { |
| namespace hardware { |
| namespace bluetooth { |
| namespace V1_0 { |
| namespace implementation { |
| |
| using ::testing::Eq; |
| using hci::H4Protocol; |
| |
| 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 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."; |
| |
| MATCHER_P3(HidlVecMatches, preamble, preamble_length, payload, "") { |
| size_t length = strlen(payload) + preamble_length; |
| if (length != arg.size()) { |
| return false; |
| } |
| |
| if (memcmp(preamble, arg.data(), preamble_length) != 0) { |
| return false; |
| } |
| |
| return memcmp(payload, arg.data() + preamble_length, |
| length - preamble_length) == 0; |
| }; |
| |
| ACTION_P2(Notify, mutex, condition) { |
| ALOGD("%s", __func__); |
| std::unique_lock<std::mutex> lock(*mutex); |
| condition->notify_one(); |
| } |
| |
| class H4ProtocolTest : public ::testing::Test { |
| protected: |
| void SetUp() override { |
| ALOGD("%s", __func__); |
| |
| int sockfd[2]; |
| socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd); |
| H4Protocol* h4_hci = |
| new H4Protocol(sockfd[0], event_cb_.AsStdFunction(), |
| acl_cb_.AsStdFunction(), sco_cb_.AsStdFunction()); |
| fd_watcher_.WatchFdForNonBlockingReads( |
| sockfd[0], [h4_hci](int fd) { h4_hci->OnDataReady(fd); }); |
| protocol_ = h4_hci; |
| |
| fake_uart_ = sockfd[1]; |
| } |
| |
| void TearDown() override { fd_watcher_.StopWatchingFileDescriptors(); } |
| |
| void SendAndReadUartOutbound(uint8_t type, char* data) { |
| ALOGD("%s sending", __func__); |
| int data_length = strlen(data); |
| protocol_->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(fake_uart_, &read_fds); |
| TEMP_FAILURE_RETRY(select(fake_uart_ + 1, &read_fds, NULL, NULL, NULL)); |
| |
| char byte; |
| TEMP_FAILURE_RETRY(read(fake_uart_, &byte, 1)); |
| |
| EXPECT_EQ(i == 0 ? type : data[i - 1], byte); |
| } |
| |
| EXPECT_EQ(i, uart_length); |
| } |
| |
| void WriteAndExpectInboundAclData(char* payload) { |
| // h4 type[1] + handle[2] + size[2] |
| char preamble[5] = {HCI_PACKET_TYPE_ACL_DATA, 19, 92, 0, 0}; |
| int length = strlen(payload); |
| preamble[3] = length & 0xFF; |
| preamble[4] = (length >> 8) & 0xFF; |
| |
| ALOGD("%s writing", __func__); |
| TEMP_FAILURE_RETRY(write(fake_uart_, preamble, sizeof(preamble))); |
| TEMP_FAILURE_RETRY(write(fake_uart_, payload, strlen(payload))); |
| |
| ALOGD("%s waiting", __func__); |
| std::mutex mutex; |
| std::condition_variable done; |
| EXPECT_CALL(acl_cb_, Call(HidlVecMatches(preamble + 1, sizeof(preamble) - 1, |
| payload))) |
| .WillOnce(Notify(&mutex, &done)); |
| |
| // Fail if it takes longer than 100 ms. |
| auto timeout_time = |
| std::chrono::steady_clock::now() + std::chrono::milliseconds(100); |
| { |
| std::unique_lock<std::mutex> lock(mutex); |
| done.wait_until(lock, timeout_time); |
| } |
| } |
| |
| void WriteAndExpectInboundScoData(char* payload) { |
| // h4 type[1] + handle[2] + size[1] |
| char preamble[4] = {HCI_PACKET_TYPE_SCO_DATA, 20, 17, 0}; |
| preamble[3] = strlen(payload) & 0xFF; |
| |
| ALOGD("%s writing", __func__); |
| TEMP_FAILURE_RETRY(write(fake_uart_, preamble, sizeof(preamble))); |
| TEMP_FAILURE_RETRY(write(fake_uart_, payload, strlen(payload))); |
| |
| ALOGD("%s waiting", __func__); |
| std::mutex mutex; |
| std::condition_variable done; |
| EXPECT_CALL(sco_cb_, Call(HidlVecMatches(preamble + 1, sizeof(preamble) - 1, |
| payload))) |
| .WillOnce(Notify(&mutex, &done)); |
| |
| // Fail if it takes longer than 100 ms. |
| auto timeout_time = |
| std::chrono::steady_clock::now() + std::chrono::milliseconds(100); |
| { |
| std::unique_lock<std::mutex> lock(mutex); |
| done.wait_until(lock, timeout_time); |
| } |
| } |
| |
| void WriteAndExpectInboundEvent(char* payload) { |
| // h4 type[1] + event_code[1] + size[1] |
| char preamble[3] = {HCI_PACKET_TYPE_EVENT, 9, 0}; |
| preamble[2] = strlen(payload) & 0xFF; |
| ALOGD("%s writing", __func__); |
| TEMP_FAILURE_RETRY(write(fake_uart_, preamble, sizeof(preamble))); |
| TEMP_FAILURE_RETRY(write(fake_uart_, payload, strlen(payload))); |
| |
| ALOGD("%s waiting", __func__); |
| std::mutex mutex; |
| std::condition_variable done; |
| EXPECT_CALL(event_cb_, Call(HidlVecMatches(preamble + 1, |
| sizeof(preamble) - 1, payload))) |
| .WillOnce(Notify(&mutex, &done)); |
| |
| { |
| std::unique_lock<std::mutex> lock(mutex); |
| done.wait(lock); |
| } |
| } |
| |
| testing::MockFunction<void(const hidl_vec<uint8_t>&)> event_cb_; |
| testing::MockFunction<void(const hidl_vec<uint8_t>&)> acl_cb_; |
| testing::MockFunction<void(const hidl_vec<uint8_t>&)> sco_cb_; |
| async::AsyncFdWatcher fd_watcher_; |
| H4Protocol* protocol_; |
| int fake_uart_; |
| }; |
| |
| // 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); |
| } |
| |
| // Ensure we properly parse data coming from the UART |
| TEST_F(H4ProtocolTest, TestReads) { |
| WriteAndExpectInboundAclData(acl_data); |
| WriteAndExpectInboundScoData(sco_data); |
| WriteAndExpectInboundEvent(event_data); |
| } |
| |
| } // namespace implementation |
| } // namespace V1_0 |
| } // namespace bluetooth |
| } // namespace hardware |
| } // namespace android |