blob: 9d43c0f6f66429250be4f6a18f8cb96771b53aa3 [file] [log] [blame]
/*
* 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 <gtest/gtest.h>
#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<void> promise;
auto future = promise.get_future();
handler_->BindOnceOn(&promise, &std::promise<void>::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<uint8_t> packet) {
if (enqueue_promise_ != nullptr) {
enqueue_future_->wait();
}
enqueue_promise_ = std::make_unique<std::promise<void>>();
enqueue_future_ = std::make_unique<std::future<void>>(enqueue_promise_->get_future());
queue_up_end->RegisterEnqueue(handler_, common::Bind(&RoundRobinSchedulerTest::enqueue_callback,
common::Unretained(this), queue_up_end, packet));
}
std::unique_ptr<packet::BasePacketBuilder> enqueue_callback(AclConnection::QueueUpEnd* queue_up_end,
std::vector<uint8_t> packet) {
auto packet_one = std::make_unique<packet::RawBuilder>(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<std::vector<uint8_t>>();
bluetooth::packet::BitInserter i(*bytes);
bytes->reserve(packet->size());
packet->Serialize(i);
auto packet_view = bluetooth::packet::PacketView<bluetooth::packet::kLittleEndian>(bytes);
AclView acl_packet_view = AclView::Create(packet_view);
ASSERT_TRUE(acl_packet_view.IsValid());
PacketView<true> 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<uint8_t> 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<std::promise<void>>();
packet_future_ = std::make_unique<std::future<void>>(packet_promise_->get_future());
}
BidiQueue<AclView, AclBuilder> hci_queue_{3};
Thread* thread_;
Handler* handler_;
TestController* controller_;
RoundRobinScheduler* round_robin_scheduler_;
std::queue<AclView> sent_acl_packets_;
uint16_t packet_count_;
std::unique_ptr<std::promise<void>> packet_promise_;
std::unique_ptr<std::future<void>> packet_future_;
std::unique_ptr<std::promise<void>> enqueue_promise_;
std::unique_ptr<std::future<void>> enqueue_future_;
};
TEST_F(RoundRobinSchedulerTest, startup_teardown) {}
TEST_F(RoundRobinSchedulerTest, register_unregister_connection) {
uint16_t handle = 0x01;
auto connection_queue = std::make_shared<AclConnection::Queue>(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<AclConnection::Queue>(10);
round_robin_scheduler_->Register(RoundRobinScheduler::ConnectionType::CLASSIC, handle, connection_queue);
SetPacketFuture(2);
AclConnection::QueueUpEnd* queue_up_end = connection_queue->GetUpEnd();
std::vector<uint8_t> packet1 = {0x01, 0x02, 0x03};
std::vector<uint8_t> 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<AclConnection::Queue>(10);
auto le_connection_queue = std::make_shared<AclConnection::Queue>(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<uint8_t> packet = {0x01, 0x02, 0x03};
std::vector<uint8_t> 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<AclConnection::Queue>(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<uint8_t> packet = {0x01, 0x02, 0x03, i};
EnqueueAclUpEnd(queue_up_end, packet);
}
packet_future_->wait();
for (uint8_t i = 0; i < 10; i++) {
std::vector<uint8_t> 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<uint8_t> 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<AclConnection::Queue>(10);
auto connection_queue2 = std::make_shared<AclConnection::Queue>(10);
auto le_connection_queue1 = std::make_shared<AclConnection::Queue>(10);
auto le_connection_queue2 = std::make_shared<AclConnection::Queue>(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<uint8_t> 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<uint8_t> packet1 = {0x01, 0x02, 0x03, i};
std::vector<uint8_t> packet2 = {0x02, 0x02, 0x03, i};
std::vector<uint8_t> le_packet1 = {0x04, 0x05, 0x06, i};
std::vector<uint8_t> 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<uint8_t> packet1 = {0x01, 0x02, 0x03, i};
std::vector<uint8_t> packet2 = {0x02, 0x02, 0x03, i};
std::vector<uint8_t> le_packet1 = {0x04, 0x05, 0x06, i};
std::vector<uint8_t> 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<AclConnection::Queue>(10);
auto le_connection_queue = std::make_shared<AclConnection::Queue>(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<uint8_t> packet(controller_->hci_mtu_, 0xff);
std::vector<uint8_t> packet_part1(controller_->hci_mtu_, 0xff);
std::vector<uint8_t> packet_part2 = {0x03, 0x02, 0x01};
packet.insert(packet.end(), packet_part2.begin(), packet_part2.end());
std::vector<uint8_t> le_packet;
std::vector<uint8_t> le_packet_part1;
std::vector<uint8_t> le_packet_part2;
std::vector<uint8_t> 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<AclConnection::Queue>(20);
auto le_connection_queue = std::make_shared<AclConnection::Queue>(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<uint8_t> huge_packet(2000);
std::vector<uint8_t> packet = {0x01, 0x02, 0x03};
std::vector<uint8_t> 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