Merge "L2CAP ERTM: Drop invalid packet on reassembly"
diff --git a/gd/cert/gd_cert_device.py b/gd/cert/gd_cert_device.py
index b730b50..b579258 100644
--- a/gd/cert/gd_cert_device.py
+++ b/gd/cert/gd_cert_device.py
@@ -69,7 +69,7 @@
self.hal = hal_cert_pb2_grpc.HciHalCertStub(self.grpc_channel)
self.controller_read_only_property = cert_rootservice_pb2_grpc.ReadOnlyPropertyStub(self.grpc_channel)
self.hci = hci_cert_pb2_grpc.AclManagerCertStub(self.grpc_channel)
- self.l2cap = l2cap_cert_pb2_grpc.L2capModuleCertStub(self.grpc_channel)
+ self.l2cap = l2cap_cert_pb2_grpc.L2capClassicModuleCertStub(self.grpc_channel)
# Event streams
self.hal.hci_event_stream = EventStream(self.hal.FetchHciEvent)
diff --git a/gd/cert/gd_device.py b/gd/cert/gd_device.py
index 2cd49b7..17454f3 100644
--- a/gd/cert/gd_device.py
+++ b/gd/cert/gd_device.py
@@ -70,7 +70,7 @@
self.controller_read_only_property = facade_rootservice_pb2_grpc.ReadOnlyPropertyStub(self.grpc_channel)
self.hci = hci_facade_pb2_grpc.AclManagerFacadeStub(self.grpc_channel)
self.hci_classic_security = hci_facade_pb2_grpc.ClassicSecurityManagerFacadeStub(self.grpc_channel)
- self.l2cap = l2cap_facade_pb2_grpc.L2capModuleFacadeStub(self.grpc_channel)
+ self.l2cap = l2cap_facade_pb2_grpc.L2capClassicModuleFacadeStub(self.grpc_channel)
# Event streams
self.hal.hci_event_stream = EventStream(self.hal.FetchHciEvent)
diff --git a/gd/l2cap/Android.bp b/gd/l2cap/Android.bp
index 3d65446..60175e2 100644
--- a/gd/l2cap/Android.bp
+++ b/gd/l2cap/Android.bp
@@ -19,6 +19,7 @@
"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 @@
"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",
diff --git a/gd/l2cap/classic/cert/api.proto b/gd/l2cap/classic/cert/api.proto
index 687686f..a80e648 100644
--- a/gd/l2cap/classic/cert/api.proto
+++ b/gd/l2cap/classic/cert/api.proto
@@ -7,6 +7,8 @@
service L2capClassicModuleCert {
rpc SendL2capPacket(L2capPacket) returns (google.protobuf.Empty) {}
+ rpc SendIFrame(IFrame) returns (SendIFrameResult) {}
+ rpc SendSFrame(SFrame) returns (SendSFrameResult) {}
rpc SetupLink(SetupLinkRequest) returns (SetupLinkResponse) {}
rpc DisconnectLink(DisconnectLinkRequest) returns (google.protobuf.Empty) {}
@@ -33,6 +35,30 @@
bytes payload = 3;
}
+message IFrame {
+ facade.BluetoothAddress remote = 1;
+ uint32 channel = 2;
+ uint32 sar = 3;
+ uint32 tx_seq = 4;
+ uint32 req_seq = 5;
+ uint32 f = 6;
+ uint32 sdu_size = 7;
+ bytes information = 8;
+}
+
+message SendIFrameResult {}
+
+message SFrame {
+ facade.BluetoothAddress remote = 1;
+ uint32 channel = 2;
+ uint32 req_seq = 3;
+ uint32 f = 4;
+ uint32 p = 5;
+ uint32 s = 6;
+}
+
+message SendSFrameResult {}
+
message DisconnectLinkRequest {
facade.BluetoothAddress remote = 1;
}
diff --git a/gd/l2cap/classic/cert/cert.cc b/gd/l2cap/classic/cert/cert.cc
index 2de345c..abb3561 100644
--- a/gd/l2cap/classic/cert/cert.cc
+++ b/gd/l2cap/classic/cert/cert.cc
@@ -80,6 +80,37 @@
return ::grpc::Status::OK;
}
+ ::grpc::Status SendIFrame(::grpc::ServerContext* context, const ::bluetooth::l2cap::classic::cert::IFrame* request,
+ ::bluetooth::l2cap::classic::cert::SendIFrameResult* response) override {
+ std::unique_ptr<RawBuilder> packet = std::make_unique<RawBuilder>();
+ auto req_string = request->information();
+ packet->AddOctets(std::vector<uint8_t>(req_string.begin(), req_string.end()));
+ std::unique_ptr<BasePacketBuilder> l2cap_builder;
+ auto f = static_cast<Final>(request->f());
+ if (request->sar() == static_cast<int>(SegmentationAndReassembly::START)) {
+ l2cap_builder = EnhancedInformationStartFrameBuilder::Create(
+ request->channel(), request->tx_seq(), f, request->req_seq(), request->sdu_size(), std::move(packet));
+ } else {
+ l2cap_builder = EnhancedInformationFrameBuilder::Create(
+ request->channel(), request->tx_seq(), f, request->req_seq(),
+ static_cast<SegmentationAndReassembly>(request->sar()), std::move(packet));
+ }
+ outgoing_packet_queue_.push(std::move(l2cap_builder));
+ send_packet_from_queue();
+ return ::grpc::Status::OK;
+ }
+
+ ::grpc::Status SendSFrame(::grpc::ServerContext* context, const ::bluetooth::l2cap::classic::cert::SFrame* request,
+ ::bluetooth::l2cap::classic::cert::SendSFrameResult* response) override {
+ auto f = static_cast<Final>(request->f());
+ auto p = static_cast<Poll>(request->p());
+ auto s = static_cast<SupervisoryFunction>(request->s());
+ auto builder = EnhancedSupervisoryFrameBuilder::Create(request->channel(), s, p, f, request->req_seq());
+ outgoing_packet_queue_.push(std::move(builder));
+ send_packet_from_queue();
+ return ::grpc::Status::OK;
+ }
+
::grpc::Status SendConnectionRequest(::grpc::ServerContext* context, const cert::ConnectionRequest* request,
::google::protobuf::Empty* response) override {
auto builder = ConnectionRequestBuilder::Create(request->signal_id(), request->psm(), request->scid());
diff --git a/gd/l2cap/classic/cert/simple_l2cap_test.py b/gd/l2cap/classic/cert/simple_l2cap_test.py
index 38d39b6..607cfb2 100644
--- a/gd/l2cap/classic/cert/simple_l2cap_test.py
+++ b/gd/l2cap/classic/cert/simple_l2cap_test.py
@@ -215,6 +215,7 @@
log = log.data_packet
if (log.channel == scid):
log.payload = basic_frame_to_enhanced_information_frame(log.payload)
+ self.cert_device.l2cap.SendSFrame(l2cap_cert_pb2.SFrame(channel=self.scid_dcid_map[scid], req_seq=1, s=0))
data_received.append((log.channel, log.payload))
event_handler.on(lambda log : log.HasField("data_packet"), on_data_received)
logs = self.cert_device.l2cap.FetchL2capLog(l2cap_cert_pb2.FetchL2capLogRequest())
@@ -222,10 +223,12 @@
assert (2, b"123") in data_received
self.device_under_test.l2cap.SendDynamicChannelPacket(l2cap_facade_pb2.DynamicChannelPacket(psm=0x33, payload=b'abc'*34))
+
+ self.cert_device.l2cap.SendIFrame(l2cap_cert_pb2.IFrame(channel=self.scid_dcid_map[scid], req_seq=1, tx_seq=0, sar=0, information=b"abcd"))
+
logs = self.cert_device.l2cap.FetchL2capLog(l2cap_cert_pb2.FetchL2capLogRequest())
event_handler.execute(logs)
assert (scid, b"abc"*34) in data_received
- self.cert_device.l2cap.StopFetchingL2capLog(l2cap_cert_pb2.StopFetchingL2capLogRequest())
def test_connect_and_send_data(self):
self.device_under_test.l2cap.RegisterChannel(l2cap_facade_pb2.RegisterChannelRequest(channel=2))
diff --git a/gd/l2cap/internal/le_credit_based_channel_data_controller.cc b/gd/l2cap/internal/le_credit_based_channel_data_controller.cc
new file mode 100644
index 0000000..eb510c1
--- /dev/null
+++ b/gd/l2cap/internal/le_credit_based_channel_data_controller.cc
@@ -0,0 +1,112 @@
+/*
+ * 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
diff --git a/gd/l2cap/internal/le_credit_based_channel_data_controller.h b/gd/l2cap/internal/le_credit_based_channel_data_controller.h
new file mode 100644
index 0000000..9374cd6
--- /dev/null
+++ b/gd/l2cap/internal/le_credit_based_channel_data_controller.h
@@ -0,0 +1,84 @@
+/*
+ * 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
diff --git a/gd/l2cap/internal/le_credit_based_channel_data_controller_test.cc b/gd/l2cap/internal/le_credit_based_channel_data_controller_test.cc
new file mode 100644
index 0000000..071e976
--- /dev/null
+++ b/gd/l2cap/internal/le_credit_based_channel_data_controller_test.cc
@@ -0,0 +1,175 @@
+/*
+ * 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
diff --git a/gd/packet/fragmenting_inserter_unittest.cc b/gd/packet/fragmenting_inserter_unittest.cc
index 6179959..c2f1124 100644
--- a/gd/packet/fragmenting_inserter_unittest.cc
+++ b/gd/packet/fragmenting_inserter_unittest.cc
@@ -91,6 +91,36 @@
ASSERT_EQ(checksum, observer.GetValue());
}
+TEST(FragmentingInserterTest, testMtuBoundaries) {
+ constexpr size_t kPacketSize = 1024;
+ auto counts = RawBuilder();
+ for (size_t i = 0; i < kPacketSize; i++) {
+ counts.AddOctets1(static_cast<uint8_t>(i));
+ }
+
+ std::vector<std::unique_ptr<RawBuilder>> fragments_mtu_is_kPacketSize;
+ FragmentingInserter it(kPacketSize, std::back_insert_iterator(fragments_mtu_is_kPacketSize));
+ counts.Serialize(it);
+ it.finalize();
+ ASSERT_EQ(1, fragments_mtu_is_kPacketSize.size());
+ ASSERT_EQ(kPacketSize, fragments_mtu_is_kPacketSize[0]->size());
+
+ std::vector<std::unique_ptr<RawBuilder>> fragments_mtu_is_less;
+ FragmentingInserter it_less(kPacketSize - 1, std::back_insert_iterator(fragments_mtu_is_less));
+ counts.Serialize(it_less);
+ it_less.finalize();
+ ASSERT_EQ(2, fragments_mtu_is_less.size());
+ ASSERT_EQ(kPacketSize - 1, fragments_mtu_is_less[0]->size());
+ ASSERT_EQ(1, fragments_mtu_is_less[1]->size());
+
+ std::vector<std::unique_ptr<RawBuilder>> fragments_mtu_is_more;
+ FragmentingInserter it_more(kPacketSize + 1, std::back_insert_iterator(fragments_mtu_is_more));
+ counts.Serialize(it_more);
+ it_more.finalize();
+ ASSERT_EQ(1, fragments_mtu_is_more.size());
+ ASSERT_EQ(kPacketSize, fragments_mtu_is_more[0]->size());
+}
+
constexpr size_t kPacketSize = 128;
class FragmentingTest : public ::testing::TestWithParam<size_t> {
public:
diff --git a/gd/packet/parser/fields/custom_field_fixed_size.cc b/gd/packet/parser/fields/custom_field_fixed_size.cc
index 029f0aa..687d48d 100644
--- a/gd/packet/parser/fields/custom_field_fixed_size.cc
+++ b/gd/packet/parser/fields/custom_field_fixed_size.cc
@@ -55,6 +55,10 @@
// Do nothing.
}
+void CustomFieldFixedSize::GenInserter(std::ostream& s) const {
+ s << "insert(" << GetName() << "_, i);";
+}
+
void CustomFieldFixedSize::GenValidator(std::ostream&) const {
// Do nothing.
}
diff --git a/gd/packet/parser/fields/custom_field_fixed_size.h b/gd/packet/parser/fields/custom_field_fixed_size.h
index bd19eb0..97acff9 100644
--- a/gd/packet/parser/fields/custom_field_fixed_size.h
+++ b/gd/packet/parser/fields/custom_field_fixed_size.h
@@ -37,6 +37,8 @@
virtual void GenParameterValidator(std::ostream&) const override;
+ virtual void GenInserter(std::ostream& s) const override;
+
virtual void GenValidator(std::ostream&) const override;
std::string type_name_;
diff --git a/gd/packet/parser/fields/scalar_field.cc b/gd/packet/parser/fields/scalar_field.cc
index c6d2ecf..320534a 100644
--- a/gd/packet/parser/fields/scalar_field.cc
+++ b/gd/packet/parser/fields/scalar_field.cc
@@ -121,8 +121,6 @@
void ScalarField::GenInserter(std::ostream& s) const {
if (GetSize().bits() == 8) {
s << "i.insert_byte(" << GetName() << "_);";
- } else if (GetSize().bits() % 8 == 0) {
- s << "insert(" << GetName() << "_, i);";
} else {
s << "insert(" << GetName() << "_, i," << GetSize().bits() << ");";
}
diff --git a/gd/packet/parser/test/generated_packet_test.cc b/gd/packet/parser/test/generated_packet_test.cc
index 06f60b5..f8454a2 100644
--- a/gd/packet/parser/test/generated_packet_test.cc
+++ b/gd/packet/parser/test/generated_packet_test.cc
@@ -1825,6 +1825,58 @@
ASSERT_EQ(ltv_vector[i].value_, an_array[i].value_);
}
}
+
+vector<uint8_t> byte_sized{
+ 0x11, // 1
+ 0x21, 0x22, // 2
+ 0x31, 0x32, 0x33, // 3
+ 0x41, 0x42, 0x43, 0x44, // 4
+ 0x51, 0x52, 0x53, 0x54, 0x55, // 5
+ 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, // 6
+ 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, // 7
+ 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, // 8
+};
+
+TEST(GeneratedPacketTest, testByteSizedFields) {
+ uint64_t array[9]{
+ 0xbadbadbad,
+ 0x11, // 1
+ 0x2221, // 2
+ 0x333231, // 3
+ 0x44434241, // 4
+ 0x5554535251, // 5
+ 0x666564636261, // 6
+ 0x77767574737271, // 7
+ 0x8887868584838281, // 8
+ };
+ auto packet =
+ ByteSizedFieldsBuilder::Create(array[1], array[2], array[3], array[4], array[5], array[6], array[7], array[8]);
+ ASSERT_EQ(byte_sized.size(), packet->size());
+
+ std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+ BitInserter it(*packet_bytes);
+ packet->Serialize(it);
+
+ ASSERT_EQ(byte_sized.size(), packet_bytes->size());
+ for (size_t i = 0; i < byte_sized.size(); i++) {
+ ASSERT_EQ(byte_sized[i], packet_bytes->at(i));
+ }
+
+ PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+ auto view = ByteSizedFieldsView::Create(packet_bytes_view);
+ ASSERT_TRUE(view.IsValid());
+ ASSERT_EQ(array[1], view.GetOne());
+ ASSERT_EQ(array[2], view.GetTwo());
+ ASSERT_EQ(array[3], view.GetThree());
+ ASSERT_EQ(array[4], view.GetFour());
+ ASSERT_EQ(array[5], view.GetFive());
+ ASSERT_EQ(array[6], view.GetSix());
+ ASSERT_EQ(array[7], view.GetSeven());
+ ASSERT_EQ(array[8], view.GetEight());
+}
+
+DEFINE_AND_INSTANTIATE_ByteSizedFieldsReflectionTest(byte_sized);
+
} // namespace parser
} // namespace packet
} // namespace bluetooth
diff --git a/gd/packet/parser/test/test_packets.pdl b/gd/packet/parser/test/test_packets.pdl
index 3c24509..2b612cc 100644
--- a/gd/packet/parser/test/test_packets.pdl
+++ b/gd/packet/parser/test/test_packets.pdl
@@ -394,3 +394,14 @@
one_array : LengthTypeValueStruct[],
_padding_[40],
}
+
+packet ByteSizedFields {
+ one : 8,
+ two : 16,
+ three : 24,
+ four : 32,
+ five : 40,
+ six : 48,
+ seven : 56,
+ eight : 64,
+}
diff --git a/gd/packet/raw_builder.cc b/gd/packet/raw_builder.cc
index ec5159b..ec2ff68 100644
--- a/gd/packet/raw_builder.cc
+++ b/gd/packet/raw_builder.cc
@@ -17,6 +17,7 @@
#include "packet/raw_builder.h"
#include <algorithm>
+#include <utility>
#include "os/log.h"
@@ -27,7 +28,7 @@
namespace packet {
RawBuilder::RawBuilder(size_t max_bytes) : max_bytes_(max_bytes) {}
-RawBuilder::RawBuilder(std::vector<uint8_t> vec) : max_bytes_(vec.size()), payload_(vec) {}
+RawBuilder::RawBuilder(std::vector<uint8_t> vec) : payload_(std::move(vec)) {}
bool RawBuilder::AddOctets(size_t octets, const vector<uint8_t>& bytes) {
if (payload_.size() + octets > max_bytes_) return false;
diff --git a/gd/packet/raw_builder.h b/gd/packet/raw_builder.h
index 9b0e959..1c9552a 100644
--- a/gd/packet/raw_builder.h
+++ b/gd/packet/raw_builder.h
@@ -64,7 +64,7 @@
// - the new size of the payload is still <= |max_bytes_|
bool AddOctets(size_t octets, uint64_t value);
- size_t max_bytes_{255};
+ size_t max_bytes_{0xffff};
// Underlying containers for storing the actual packet
std::vector<uint8_t> payload_;
diff --git a/gd/packet/raw_builder_unittest.cc b/gd/packet/raw_builder_unittest.cc
index 5210fb6..64ca0ed 100644
--- a/gd/packet/raw_builder_unittest.cc
+++ b/gd/packet/raw_builder_unittest.cc
@@ -65,5 +65,48 @@
ASSERT_EQ(count, packet);
}
+TEST(RawBuilderTest, buildStartingWithVector) {
+ std::vector<uint8_t> count_first(count.begin(), count.begin() + 0x8);
+ std::unique_ptr<RawBuilder> count_builder = std::make_unique<RawBuilder>(count_first);
+ count_builder->AddOctets4(0x0b0a0908);
+ count_builder->AddOctets2(0x0d0c);
+ count_builder->AddOctets1(0x0e);
+ count_builder->AddOctets1(0x0f);
+ count_builder->AddOctets8(0x1716151413121110);
+ std::vector<uint8_t> count_last(count.begin() + 0x18, count.end());
+ count_builder->AddOctets(count_last);
+
+ ASSERT_EQ(count.size(), count_builder->size());
+
+ std::vector<uint8_t> packet;
+ BitInserter it(packet);
+
+ count_builder->Serialize(it);
+
+ ASSERT_EQ(count, packet);
+}
+
+TEST(RawBuilderTest, testMaxBytes) {
+ const size_t kMaxBytes = count.size();
+ std::unique_ptr<RawBuilder> count_builder = std::make_unique<RawBuilder>(kMaxBytes);
+ ASSERT_TRUE(count_builder->AddOctets(count));
+ ASSERT_FALSE(count_builder->AddOctets4(0x0b0a0908));
+ ASSERT_FALSE(count_builder->AddOctets2(0x0d0c));
+ ASSERT_FALSE(count_builder->AddOctets1(0x0e));
+ ASSERT_FALSE(count_builder->AddOctets1(0x0f));
+ ASSERT_FALSE(count_builder->AddOctets8(0x1716151413121110));
+ std::vector<uint8_t> count_last(count.begin() + 0x18, count.end());
+ ASSERT_FALSE(count_builder->AddOctets(count_last));
+
+ ASSERT_EQ(count.size(), count_builder->size());
+
+ std::vector<uint8_t> packet;
+ BitInserter it(packet);
+
+ count_builder->Serialize(it);
+
+ ASSERT_EQ(count, packet);
+}
+
} // namespace packet
} // namespace bluetooth