/* * Copyright 2020 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. */ #include "hci/acl_manager/round_robin_scheduler.h" #include #include "common/bidi_queue.h" #include "common/callback.h" #include "hci/acl_manager.h" #include "hci/controller.h" #include "hci/hci_packets.h" #include "os/handler.h" #include "os/log.h" #include "packet/raw_builder.h" using ::bluetooth::common::BidiQueue; using ::bluetooth::common::Callback; using ::bluetooth::os::Handler; using ::bluetooth::os::Thread; namespace bluetooth { namespace hci { namespace acl_manager { class TestController : public Controller { public: uint16_t GetNumAclPacketBuffers() const { return max_acl_packet_credits_; } uint16_t GetAclPacketLength() const { return hci_mtu_; } LeBufferSize GetLeBufferSize() const { LeBufferSize le_buffer_size; le_buffer_size.le_data_packet_length_ = le_hci_mtu_; le_buffer_size.total_num_le_packets_ = le_max_acl_packet_credits_; return le_buffer_size; } void RegisterCompletedAclPacketsCallback(CompletedAclPacketsCallback cb) { acl_credits_callback_ = cb; } void SendCompletedAclPacketsCallback(uint16_t handle, uint16_t credits) { acl_credits_callback_.Invoke(handle, credits); } void UnregisterCompletedAclPacketsCallback() { acl_credits_callback_ = {}; } const uint16_t max_acl_packet_credits_ = 10; const uint16_t hci_mtu_ = 1024; const uint16_t le_max_acl_packet_credits_ = 15; const uint16_t le_hci_mtu_ = 27; private: CompletedAclPacketsCallback acl_credits_callback_; }; class RoundRobinSchedulerTest : public ::testing::Test { public: void SetUp() override { thread_ = new Thread("thread", Thread::Priority::NORMAL); handler_ = new Handler(thread_); controller_ = new TestController(); round_robin_scheduler_ = new RoundRobinScheduler(handler_, controller_, hci_queue_.GetUpEnd()); hci_queue_.GetDownEnd()->RegisterDequeue( handler_, common::Bind(&RoundRobinSchedulerTest::HciDownEndDequeue, common::Unretained(this))); } void TearDown() override { hci_queue_.GetDownEnd()->UnregisterDequeue(); delete round_robin_scheduler_; delete controller_; handler_->Clear(); delete handler_; delete thread_; } void sync_handler() { std::promise promise; auto future = promise.get_future(); handler_->BindOnceOn(&promise, &std::promise::set_value).Invoke(); auto status = future.wait_for(std::chrono::milliseconds(3)); EXPECT_EQ(status, std::future_status::ready); } void EnqueueAclUpEnd(AclConnection::QueueUpEnd* queue_up_end, std::vector packet) { if (enqueue_promise_ != nullptr) { enqueue_future_->wait(); } enqueue_promise_ = std::make_unique>(); enqueue_future_ = std::make_unique>(enqueue_promise_->get_future()); queue_up_end->RegisterEnqueue(handler_, common::Bind(&RoundRobinSchedulerTest::enqueue_callback, common::Unretained(this), queue_up_end, packet)); } std::unique_ptr enqueue_callback(AclConnection::QueueUpEnd* queue_up_end, std::vector packet) { auto packet_one = std::make_unique(2000); packet_one->AddOctets(packet); queue_up_end->UnregisterEnqueue(); enqueue_promise_->set_value(); return packet_one; }; void HciDownEndDequeue() { auto packet = hci_queue_.GetDownEnd()->TryDequeue(); // Convert from a Builder to a View auto bytes = std::make_shared>(); bluetooth::packet::BitInserter i(*bytes); bytes->reserve(packet->size()); packet->Serialize(i); auto packet_view = bluetooth::packet::PacketView(bytes); AclView acl_packet_view = AclView::Create(packet_view); ASSERT_TRUE(acl_packet_view.IsValid()); PacketView count_view = acl_packet_view.GetPayload(); sent_acl_packets_.push(acl_packet_view); packet_count_--; if (packet_count_ == 0) { packet_promise_->set_value(); packet_promise_ = nullptr; } } void VerifyPacket(uint16_t handle, std::vector packet) { auto acl_packet_view = sent_acl_packets_.front(); ASSERT_EQ(handle, acl_packet_view.GetHandle()); auto payload = acl_packet_view.GetPayload(); for (size_t i = 0; i < payload.size(); i++) { ASSERT_EQ(payload[i], packet[i]); } sent_acl_packets_.pop(); } void SetPacketFuture(uint16_t count) { ASSERT_LOG(packet_promise_ == nullptr, "Promises, Promises, ... Only one at a time."); packet_count_ = count; packet_promise_ = std::make_unique>(); packet_future_ = std::make_unique>(packet_promise_->get_future()); } BidiQueue hci_queue_{3}; Thread* thread_; Handler* handler_; TestController* controller_; RoundRobinScheduler* round_robin_scheduler_; std::queue sent_acl_packets_; uint16_t packet_count_; std::unique_ptr> packet_promise_; std::unique_ptr> packet_future_; std::unique_ptr> enqueue_promise_; std::unique_ptr> enqueue_future_; }; TEST_F(RoundRobinSchedulerTest, startup_teardown) {} TEST_F(RoundRobinSchedulerTest, register_unregister_connection) { uint16_t handle = 0x01; auto connection_queue = std::make_shared(10); round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle, connection_queue); round_robin_scheduler_->Unregister(handle); } TEST_F(RoundRobinSchedulerTest, buffer_packet) { uint16_t handle = 0x01; auto connection_queue = std::make_shared(10); round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle, connection_queue); SetPacketFuture(2); AclConnection::QueueUpEnd* queue_up_end = connection_queue->GetUpEnd(); std::vector packet1 = {0x01, 0x02, 0x03}; std::vector packet2 = {0x04, 0x05, 0x06}; EnqueueAclUpEnd(queue_up_end, packet1); EnqueueAclUpEnd(queue_up_end, packet2); packet_future_->wait(); VerifyPacket(handle, packet1); VerifyPacket(handle, packet2); ASSERT_EQ(round_robin_scheduler_->GetCredits(), controller_->max_acl_packet_credits_ - 2); round_robin_scheduler_->Unregister(handle); } TEST_F(RoundRobinSchedulerTest, buffer_packet_from_two_connections) { uint16_t handle = 0x01; uint16_t le_handle = 0x02; auto connection_queue = std::make_shared(10); auto le_connection_queue = std::make_shared(10); round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle, connection_queue); round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::LE, le_handle, le_connection_queue); SetPacketFuture(2); AclConnection::QueueUpEnd* queue_up_end = connection_queue->GetUpEnd(); AclConnection::QueueUpEnd* le_queue_up_end = le_connection_queue->GetUpEnd(); std::vector packet = {0x01, 0x02, 0x03}; std::vector le_packet = {0x04, 0x05, 0x06}; EnqueueAclUpEnd(le_queue_up_end, le_packet); EnqueueAclUpEnd(queue_up_end, packet); packet_future_->wait(); VerifyPacket(le_handle, le_packet); VerifyPacket(handle, packet); ASSERT_EQ(round_robin_scheduler_->GetCredits(), controller_->max_acl_packet_credits_ - 1); ASSERT_EQ(round_robin_scheduler_->GetLeCredits(), controller_->le_max_acl_packet_credits_ - 1); round_robin_scheduler_->Unregister(handle); round_robin_scheduler_->Unregister(le_handle); } TEST_F(RoundRobinSchedulerTest, do_not_register_when_credits_is_zero) { uint16_t handle = 0x01; auto connection_queue = std::make_shared(15); round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle, connection_queue); SetPacketFuture(10); AclConnection::QueueUpEnd* queue_up_end = connection_queue->GetUpEnd(); for (uint8_t i = 0; i < 15; i++) { std::vector packet = {0x01, 0x02, 0x03, i}; EnqueueAclUpEnd(queue_up_end, packet); } packet_future_->wait(); for (uint8_t i = 0; i < 10; i++) { std::vector packet = {0x01, 0x02, 0x03, i}; VerifyPacket(handle, packet); } ASSERT_EQ(round_robin_scheduler_->GetCredits(), 0); SetPacketFuture(5); controller_->SendCompletedAclPacketsCallback(0x01, 10); sync_handler(); packet_future_->wait(); for (uint8_t i = 10; i < 15; i++) { std::vector packet = {0x01, 0x02, 0x03, i}; VerifyPacket(handle, packet); } ASSERT_EQ(round_robin_scheduler_->GetCredits(), 5); round_robin_scheduler_->Unregister(handle); } TEST_F(RoundRobinSchedulerTest, reveived_completed_callback_with_unknown_handle) { controller_->SendCompletedAclPacketsCallback(0x00, 1); sync_handler(); EXPECT_EQ(round_robin_scheduler_->GetCredits(), controller_->max_acl_packet_credits_); EXPECT_EQ(round_robin_scheduler_->GetLeCredits(), controller_->le_max_acl_packet_credits_); } TEST_F(RoundRobinSchedulerTest, buffer_packet_intervally) { uint16_t handle1 = 0x01; uint16_t handle2 = 0x02; uint16_t le_handle1 = 0x03; uint16_t le_handle2 = 0x04; auto connection_queue1 = std::make_shared(10); auto connection_queue2 = std::make_shared(10); auto le_connection_queue1 = std::make_shared(10); auto le_connection_queue2 = std::make_shared(10); SetPacketFuture(18); AclConnection::QueueUpEnd* queue_up_end1 = connection_queue1->GetUpEnd(); AclConnection::QueueUpEnd* queue_up_end2 = connection_queue2->GetUpEnd(); AclConnection::QueueUpEnd* le_queue_up_end1 = le_connection_queue1->GetUpEnd(); AclConnection::QueueUpEnd* le_queue_up_end2 = le_connection_queue2->GetUpEnd(); round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle1, connection_queue1); round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle2, connection_queue2); round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::LE, le_handle1, le_connection_queue1); round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::LE, le_handle2, le_connection_queue2); std::vector packet = {0x01, 0x02, 0x03}; EnqueueAclUpEnd(queue_up_end1, packet); EnqueueAclUpEnd(le_queue_up_end2, packet); for (uint8_t i = 0; i < 4; i++) { std::vector packet1 = {0x01, 0x02, 0x03, i}; std::vector packet2 = {0x02, 0x02, 0x03, i}; std::vector le_packet1 = {0x04, 0x05, 0x06, i}; std::vector le_packet2 = {0x05, 0x05, 0x06, i}; EnqueueAclUpEnd(queue_up_end1, packet1); EnqueueAclUpEnd(queue_up_end2, packet2); EnqueueAclUpEnd(le_queue_up_end1, le_packet1); EnqueueAclUpEnd(le_queue_up_end2, le_packet2); } packet_future_->wait(); VerifyPacket(handle1, packet); VerifyPacket(le_handle2, packet); for (uint8_t i = 0; i < 4; i++) { std::vector packet1 = {0x01, 0x02, 0x03, i}; std::vector packet2 = {0x02, 0x02, 0x03, i}; std::vector le_packet1 = {0x04, 0x05, 0x06, i}; std::vector le_packet2 = {0x05, 0x05, 0x06, i}; VerifyPacket(handle1, packet1); VerifyPacket(handle2, packet2); VerifyPacket(le_handle1, le_packet1); VerifyPacket(le_handle2, le_packet2); } ASSERT_EQ(round_robin_scheduler_->GetCredits(), controller_->max_acl_packet_credits_ - 9); ASSERT_EQ(round_robin_scheduler_->GetLeCredits(), controller_->le_max_acl_packet_credits_ - 9); round_robin_scheduler_->Unregister(handle1); round_robin_scheduler_->Unregister(handle2); round_robin_scheduler_->Unregister(le_handle1); round_robin_scheduler_->Unregister(le_handle2); } TEST_F(RoundRobinSchedulerTest, send_fragments_without_interval) { uint16_t handle = 0x01; uint16_t le_handle = 0x02; auto connection_queue = std::make_shared(10); auto le_connection_queue = std::make_shared(10); round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle, connection_queue); round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::LE, le_handle, le_connection_queue); SetPacketFuture(5); AclConnection::QueueUpEnd* queue_up_end = connection_queue->GetUpEnd(); AclConnection::QueueUpEnd* le_queue_up_end = le_connection_queue->GetUpEnd(); std::vector packet(controller_->hci_mtu_, 0xff); std::vector packet_part1(controller_->hci_mtu_, 0xff); std::vector packet_part2 = {0x03, 0x02, 0x01}; packet.insert(packet.end(), packet_part2.begin(), packet_part2.end()); std::vector le_packet; std::vector le_packet_part1; std::vector le_packet_part2; std::vector le_packet_part3; for (uint8_t i = 0; i < controller_->le_hci_mtu_; i++) { le_packet.push_back(i); le_packet_part1.push_back(i); le_packet_part2.push_back(i * 2); le_packet_part3.push_back(i * 3); } le_packet.insert(le_packet.end(), le_packet_part2.begin(), le_packet_part2.end()); le_packet.insert(le_packet.end(), le_packet_part3.begin(), le_packet_part3.end()); EnqueueAclUpEnd(le_queue_up_end, le_packet); EnqueueAclUpEnd(queue_up_end, packet); packet_future_->wait(); VerifyPacket(le_handle, le_packet_part1); VerifyPacket(le_handle, le_packet_part2); VerifyPacket(le_handle, le_packet_part3); VerifyPacket(handle, packet_part1); VerifyPacket(handle, packet_part2); ASSERT_EQ(round_robin_scheduler_->GetCredits(), controller_->max_acl_packet_credits_ - 2); ASSERT_EQ(round_robin_scheduler_->GetLeCredits(), controller_->le_max_acl_packet_credits_ - 3); round_robin_scheduler_->Unregister(handle); round_robin_scheduler_->Unregister(le_handle); } TEST_F(RoundRobinSchedulerTest, receive_le_credit_when_next_fragment_is_classic) { uint16_t handle = 0x01; uint16_t le_handle = 0x02; auto connection_queue = std::make_shared(20); auto le_connection_queue = std::make_shared(20); round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle, connection_queue); round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::LE, le_handle, le_connection_queue); SetPacketFuture(controller_->le_max_acl_packet_credits_ + controller_->max_acl_packet_credits_); AclConnection::QueueUpEnd* queue_up_end = connection_queue->GetUpEnd(); AclConnection::QueueUpEnd* le_queue_up_end = le_connection_queue->GetUpEnd(); std::vector huge_packet(2000); std::vector packet = {0x01, 0x02, 0x03}; std::vector le_packet = {0x04, 0x05, 0x06}; // Make le_acl_packet_credits_ = 0; for (uint16_t i = 0; i < controller_->le_max_acl_packet_credits_; i++) { EnqueueAclUpEnd(le_queue_up_end, le_packet); } // Make acl_packet_credits_ = 0 and remain 1 acl fragment in fragments_to_send_ for (uint16_t i = 0; i < controller_->max_acl_packet_credits_ - 1; i++) { EnqueueAclUpEnd(queue_up_end, packet); } EnqueueAclUpEnd(queue_up_end, huge_packet); packet_future_->wait(); // Trigger start_round_robin controller_->SendCompletedAclPacketsCallback(0x02, 1); std::this_thread::sleep_for(std::chrono::milliseconds(20)); ASSERT_EQ(round_robin_scheduler_->GetCredits(), 0); ASSERT_EQ(round_robin_scheduler_->GetLeCredits(), 1); round_robin_scheduler_->Unregister(handle); round_robin_scheduler_->Unregister(le_handle); } } // namespace acl_manager } // namespace hci } // namespace bluetooth