Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 687b6e24 authored by Hansong Zhang's avatar Hansong Zhang
Browse files

L2CAP: LE Credit Based Channel Data Controller

Segment and reassemble LE information frame. Next step is to integrate
command signals to handle credit, and update MTU and MPS.

Test: bluetooth_test_gd
Bug: 144954675
Change-Id: I37defeedb5b7f73e77c61960d3b8e0d41722c9e9
parent 047df0fb
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ filegroup {
        "classic/l2cap_classic_module.cc",
        "internal/basic_mode_channel_data_controller.cc",
        "internal/enhanced_retransmission_mode_channel_data_controller.cc",
        "internal/le_credit_based_channel_data_controller.cc",
        "internal/receiver.cc",
        "internal/scheduler_fifo.cc",
        "internal/sender.cc",
@@ -45,6 +46,7 @@ filegroup {
        "internal/basic_mode_channel_data_controller_test.cc",
        "internal/enhanced_retransmission_mode_channel_data_controller_test.cc",
        "internal/fixed_channel_allocator_test.cc",
        "internal/le_credit_based_channel_data_controller_test.cc",
        "internal/receiver_test.cc",
        "internal/scheduler_fifo_test.cc",
        "internal/sender_test.cc",
+112 −0
Original line number Diff line number Diff line
/*
 * Copyright 2019 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 "l2cap/internal/le_credit_based_channel_data_controller.h"

#include "l2cap/l2cap_packets.h"
#include "packet/fragmenting_inserter.h"
#include "packet/raw_builder.h"

namespace bluetooth {
namespace l2cap {
namespace internal {

LeCreditBasedDataController::LeCreditBasedDataController(Cid cid, Cid remote_cid, UpperQueueDownEnd* channel_queue_end,
                                                         os::Handler* handler, Scheduler* scheduler)
    : cid_(cid), remote_cid_(remote_cid), enqueue_buffer_(channel_queue_end), handler_(handler), scheduler_(scheduler) {
}

void LeCreditBasedDataController::OnSdu(std::unique_ptr<packet::BasePacketBuilder> sdu) {
  auto sdu_size = sdu->size();
  if (sdu_size == 0) {
    LOG_WARN("Received empty SDU");
    return;
  }
  if (sdu_size > mtu_) {
    LOG_WARN("Received sdu_size %d > mtu %d", static_cast<int>(sdu_size), mtu_);
  }
  std::vector<std::unique_ptr<packet::RawBuilder>> segments;
  // TODO: We don't need to waste 2 bytes for continuation segment.
  packet::FragmentingInserter fragmenting_inserter(mps_ - 2, std::back_insert_iterator(segments));
  sdu->Serialize(fragmenting_inserter);
  fragmenting_inserter.finalize();
  std::unique_ptr<BasicFrameBuilder> builder;
  builder = FirstLeInformationFrameBuilder::Create(remote_cid_, sdu_size, std::move(segments[0]));
  pdu_queue_.emplace(std::move(builder));
  for (auto i = 1; i < segments.size(); i++) {
    builder = BasicFrameBuilder::Create(remote_cid_, std::move(segments[i]));
    pdu_queue_.emplace(std::move(builder));
  }
  scheduler_->OnPacketsReady(cid_, segments.size());
}

void LeCreditBasedDataController::OnPdu(packet::PacketView<true> pdu) {
  auto basic_frame_view = BasicFrameView::Create(pdu);
  if (!basic_frame_view.IsValid()) {
    LOG_WARN("Received invalid frame");
    return;
  }
  if (basic_frame_view.size() > mps_) {
    LOG_WARN("Received frame size %d > mps %d, dropping the packet", static_cast<int>(basic_frame_view.size()), mps_);
    return;
  }
  if (remaining_sdu_continuation_packet_size_ == 0) {
    auto start_frame_view = FirstLeInformationFrameView::Create(basic_frame_view);
    if (!start_frame_view.IsValid()) {
      LOG_WARN("Received invalid frame");
      return;
    }
    auto payload = start_frame_view.GetPayload();
    auto sdu_size = start_frame_view.GetL2capSduLength();
    remaining_sdu_continuation_packet_size_ = sdu_size - payload.size();
    reassembly_stage_ = payload;
  } else {
    auto payload = basic_frame_view.GetPayload();
    remaining_sdu_continuation_packet_size_ -= payload.size();
    reassembly_stage_.AppendPacketView(payload);
  }
  if (remaining_sdu_continuation_packet_size_ == 0) {
    enqueue_buffer_.Enqueue(std::make_unique<PacketView<kLittleEndian>>(reassembly_stage_), handler_);
  } else if (remaining_sdu_continuation_packet_size_ < 0 || reassembly_stage_.size() > mtu_) {
    LOG_WARN("Received larger SDU size than expected");
    reassembly_stage_ = PacketViewForReassembly(std::make_shared<std::vector<uint8_t>>());
    remaining_sdu_continuation_packet_size_ = 0;
    // TODO: Close channel
  }
}

std::unique_ptr<packet::BasePacketBuilder> LeCreditBasedDataController::GetNextPacket() {
  auto next = std::move(pdu_queue_.front());
  pdu_queue_.pop();
  return next;
}

void LeCreditBasedDataController::SetMtu(Mtu mtu) {
  mtu_ = mtu;
}

void LeCreditBasedDataController::SetMps(uint16_t mps) {
  mps_ = mps;
}

void LeCreditBasedDataController::OnCredit(uint16_t credits) {
  int total_credits = credits_ + credits;
  credits_ = total_credits > 0xffff ? 0xffff : total_credits;
}

}  // namespace internal
}  // namespace l2cap
}  // namespace bluetooth
+84 −0
Original line number Diff line number Diff line
/*
 * Copyright 2019 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.
 */

#pragma once

#include <memory>
#include <unordered_map>
#include <utility>

#include "common/bidi_queue.h"
#include "l2cap/cid.h"
#include "l2cap/internal/channel_impl.h"
#include "l2cap/internal/data_controller.h"
#include "l2cap/internal/scheduler.h"
#include "l2cap/l2cap_packets.h"
#include "l2cap/mtu.h"
#include "os/handler.h"
#include "os/queue.h"
#include "packet/base_packet_builder.h"
#include "packet/packet_view.h"

namespace bluetooth {
namespace l2cap {
namespace internal {

class LeCreditBasedDataController : public DataController {
 public:
  using UpperEnqueue = packet::PacketView<packet::kLittleEndian>;
  using UpperDequeue = packet::BasePacketBuilder;
  using UpperQueueDownEnd = common::BidiQueueEnd<UpperEnqueue, UpperDequeue>;
  LeCreditBasedDataController(Cid cid, Cid remote_cid, UpperQueueDownEnd* channel_queue_end, os::Handler* handler,
                              Scheduler* scheduler);

  void OnSdu(std::unique_ptr<packet::BasePacketBuilder> sdu) override;
  void OnPdu(packet::PacketView<true> pdu) override;
  std::unique_ptr<packet::BasePacketBuilder> GetNextPacket() override;

  void EnableFcs(bool enabled) override {}
  void SetRetransmissionAndFlowControlOptions(const RetransmissionAndFlowControlConfigurationOption& option) override {}

  // TODO: Set MTU and MPS from signalling channel
  void SetMtu(Mtu mtu);
  void SetMps(uint16_t mps);
  // TODO: Handle credits
  void OnCredit(uint16_t credits);

 private:
  Cid cid_;
  Cid remote_cid_;
  os::EnqueueBuffer<UpperEnqueue> enqueue_buffer_;
  os::Handler* handler_;
  std::queue<std::unique_ptr<packet::BasePacketBuilder>> pdu_queue_;
  Scheduler* scheduler_;
  Mtu mtu_ = 512;
  uint16_t mps_ = 251;
  uint16_t credits_ = 0;

  class PacketViewForReassembly : public packet::PacketView<kLittleEndian> {
   public:
    PacketViewForReassembly(const PacketView& packetView) : PacketView(packetView) {}
    void AppendPacketView(packet::PacketView<kLittleEndian> to_append) {
      Append(to_append);
    }
  };
  PacketViewForReassembly reassembly_stage_{std::make_shared<std::vector<uint8_t>>()};
  uint16_t remaining_sdu_continuation_packet_size_ = 0;
};

}  // namespace internal
}  // namespace l2cap
}  // namespace bluetooth
+175 −0
Original line number Diff line number Diff line
/*
 * Copyright 2019 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 "l2cap/internal/le_credit_based_channel_data_controller.h"

#include <gtest/gtest.h>

#include "l2cap/internal/scheduler_mock.h"
#include "l2cap/l2cap_packets.h"
#include "packet/raw_builder.h"

namespace bluetooth {
namespace l2cap {
namespace internal {
namespace {

std::unique_ptr<packet::BasePacketBuilder> CreateSdu(std::vector<uint8_t> payload) {
  auto raw_builder = std::make_unique<packet::RawBuilder>();
  raw_builder->AddOctets(payload);
  return raw_builder;
}

PacketView<kLittleEndian> GetPacketView(std::unique_ptr<packet::BasePacketBuilder> packet) {
  auto bytes = std::make_shared<std::vector<uint8_t>>();
  BitInserter i(*bytes);
  bytes->reserve(packet->size());
  packet->Serialize(i);
  return packet::PacketView<packet::kLittleEndian>(bytes);
}

void sync_handler(os::Handler* handler) {
  std::promise<void> promise;
  auto future = promise.get_future();
  handler->Post(common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)));
  auto status = future.wait_for(std::chrono::milliseconds(300));
  EXPECT_EQ(status, std::future_status::ready);
}

class LeCreditBasedDataControllerTest : public ::testing::Test {
 protected:
  void SetUp() override {
    thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
    user_handler_ = new os::Handler(thread_);
    queue_handler_ = new os::Handler(thread_);
  }

  void TearDown() override {
    queue_handler_->Clear();
    user_handler_->Clear();
    delete queue_handler_;
    delete user_handler_;
    delete thread_;
  }

  os::Thread* thread_ = nullptr;
  os::Handler* user_handler_ = nullptr;
  os::Handler* queue_handler_ = nullptr;
};

TEST_F(LeCreditBasedDataControllerTest, transmit_unsegmented) {
  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
  testing::MockScheduler scheduler;
  LeCreditBasedDataController controller{0x41, 0x41, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
  EXPECT_CALL(scheduler, OnPacketsReady(0x41, 1));
  controller.OnSdu(CreateSdu({'a', 'b', 'c', 'd'}));
  auto next_packet = controller.GetNextPacket();
  EXPECT_NE(next_packet, nullptr);
  auto view = GetPacketView(std::move(next_packet));
  auto pdu_view = BasicFrameView::Create(view);
  EXPECT_TRUE(pdu_view.IsValid());
  auto first_le_info_view = FirstLeInformationFrameView::Create(pdu_view);
  EXPECT_TRUE(first_le_info_view.IsValid());
  auto payload = first_le_info_view.GetPayload();
  std::string data = std::string(payload.begin(), payload.end());
  EXPECT_EQ(data, "abcd");
}

TEST_F(LeCreditBasedDataControllerTest, transmit_segmented) {
  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
  testing::MockScheduler scheduler;
  LeCreditBasedDataController controller{0x41, 0x41, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
  controller.SetMps(4);
  EXPECT_CALL(scheduler, OnPacketsReady(0x41, 2));
  // Should be divided into 'ab', and 'cd'
  controller.OnSdu(CreateSdu({'a', 'b', 'c', 'd'}));
  auto next_packet = controller.GetNextPacket();
  EXPECT_NE(next_packet, nullptr);
  auto view = GetPacketView(std::move(next_packet));
  auto pdu_view = BasicFrameView::Create(view);
  EXPECT_TRUE(pdu_view.IsValid());
  auto first_le_info_view = FirstLeInformationFrameView::Create(pdu_view);
  EXPECT_TRUE(first_le_info_view.IsValid());
  auto payload = first_le_info_view.GetPayload();
  std::string data = std::string(payload.begin(), payload.end());
  EXPECT_EQ(data, "ab");
  EXPECT_EQ(first_le_info_view.GetL2capSduLength(), 4);

  next_packet = controller.GetNextPacket();
  EXPECT_NE(next_packet, nullptr);
  view = GetPacketView(std::move(next_packet));
  pdu_view = BasicFrameView::Create(view);
  EXPECT_TRUE(pdu_view.IsValid());
  payload = pdu_view.GetPayload();
  data = std::string(payload.begin(), payload.end());
  EXPECT_EQ(data, "cd");
}

TEST_F(LeCreditBasedDataControllerTest, receive_unsegmented) {
  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
  testing::MockScheduler scheduler;
  LeCreditBasedDataController controller{0x41, 0x41, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
  auto segment = CreateSdu({'a', 'b', 'c', 'd'});
  auto builder = FirstLeInformationFrameBuilder::Create(0x41, 4, std::move(segment));
  auto base_view = GetPacketView(std::move(builder));
  controller.OnPdu(base_view);
  sync_handler(queue_handler_);
  auto payload = channel_queue.GetUpEnd()->TryDequeue();
  EXPECT_NE(payload, nullptr);
  std::string data = std::string(payload->begin(), payload->end());
  EXPECT_EQ(data, "abcd");
}

TEST_F(LeCreditBasedDataControllerTest, receive_segmented) {
  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
  testing::MockScheduler scheduler;
  LeCreditBasedDataController controller{0x41, 0x41, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
  auto segment1 = CreateSdu({'a', 'b', 'c', 'd'});
  auto builder1 = FirstLeInformationFrameBuilder::Create(0x41, 7, std::move(segment1));
  auto base_view = GetPacketView(std::move(builder1));
  controller.OnPdu(base_view);
  auto segment2 = CreateSdu({'e', 'f', 'g'});
  auto builder2 = BasicFrameBuilder::Create(0x41, std::move(segment2));
  base_view = GetPacketView(std::move(builder2));
  controller.OnPdu(base_view);
  sync_handler(queue_handler_);
  auto payload = channel_queue.GetUpEnd()->TryDequeue();
  EXPECT_NE(payload, nullptr);
  std::string data = std::string(payload->begin(), payload->end());
  EXPECT_EQ(data, "abcdefg");
}

TEST_F(LeCreditBasedDataControllerTest, receive_segmented_with_wrong_sdu_length) {
  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
  testing::MockScheduler scheduler;
  LeCreditBasedDataController controller{0x41, 0x41, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
  auto segment1 = CreateSdu({'a', 'b', 'c', 'd'});
  auto builder1 = FirstLeInformationFrameBuilder::Create(0x41, 5, std::move(segment1));
  auto base_view = GetPacketView(std::move(builder1));
  controller.OnPdu(base_view);
  auto segment2 = CreateSdu({'e', 'f', 'g'});
  auto builder2 = BasicFrameBuilder::Create(0x41, std::move(segment2));
  base_view = GetPacketView(std::move(builder2));
  controller.OnPdu(base_view);
  sync_handler(queue_handler_);
  auto payload = channel_queue.GetUpEnd()->TryDequeue();
  EXPECT_EQ(payload, nullptr);
}

}  // namespace
}  // namespace internal
}  // namespace l2cap
}  // namespace bluetooth