Merge "HCI: Le extended advertising packets"
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/enhanced_retransmission_mode_channel_data_controller.cc b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.cc
index 2a1b930..97f9702 100644
--- a/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.cc
+++ b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.cc
@@ -168,20 +168,20 @@
}
}
- void recv_i_frame(Final f, uint8_t tx_seq, uint8_t req_seq, SegmentationAndReassembly sar,
+ void recv_i_frame(Final f, uint8_t tx_seq, uint8_t req_seq, SegmentationAndReassembly sar, uint16_t sdu_size,
const packet::PacketView<true>& payload) {
if (rx_state_ == RxState::RECV) {
if (f == Final::NOT_SET && with_expected_tx_seq(tx_seq) && with_valid_req_seq(req_seq) && with_valid_f_bit(f) &&
!local_busy()) {
increment_expected_tx_seq();
pass_to_tx(req_seq, f);
- data_indication(sar, payload);
+ data_indication(sar, sdu_size, payload);
send_ack(Final::NOT_SET);
} else if (f == Final::POLL_RESPONSE && with_expected_tx_seq(tx_seq) && with_valid_req_seq(req_seq) &&
with_valid_f_bit(f) && !local_busy()) {
increment_expected_tx_seq();
pass_to_tx(req_seq, f);
- data_indication(sar, payload);
+ data_indication(sar, sdu_size, payload);
if (!rej_actioned_) {
retransmit_i_frames(req_seq);
send_pending_i_frames();
@@ -216,14 +216,14 @@
if (f == Final::NOT_SET && with_expected_tx_seq(tx_seq) && with_valid_req_seq(req_seq) && with_valid_f_bit(f)) {
increment_expected_tx_seq();
pass_to_tx(req_seq, f);
- data_indication(sar, payload);
+ data_indication(sar, sdu_size, payload);
send_ack(Final::NOT_SET);
rx_state_ = RxState::RECV;
} else if (f == Final::POLL_RESPONSE && with_expected_tx_seq(tx_seq) && with_valid_req_seq(req_seq) &&
with_valid_f_bit(f)) {
increment_expected_tx_seq();
pass_to_tx(req_seq, f);
- data_indication(sar, payload);
+ data_indication(sar, sdu_size, payload);
if (!rej_actioned_) {
retransmit_i_frames(req_seq);
send_pending_i_frames();
@@ -682,8 +682,8 @@
recv_f_bit(f);
}
- void data_indication(SegmentationAndReassembly sar, const packet::PacketView<true>& segment) {
- controller_->stage_for_reassembly(sar, segment);
+ void data_indication(SegmentationAndReassembly sar, uint16_t sdu_size, const packet::PacketView<true>& segment) {
+ controller_->stage_for_reassembly(sar, sdu_size, segment);
buffer_seq_ = (buffer_seq_ + 1) % kMaxTxWin;
}
@@ -825,8 +825,21 @@
LOG_WARN("Received invalid frame");
return;
}
- pimpl_->recv_i_frame(i_frame_view.GetF(), i_frame_view.GetTxSeq(), i_frame_view.GetReqSeq(), i_frame_view.GetSar(),
- i_frame_view.GetPayload());
+ Final f = i_frame_view.GetF();
+ uint8_t tx_seq = i_frame_view.GetTxSeq();
+ uint8_t req_seq = i_frame_view.GetReqSeq();
+ auto sar = i_frame_view.GetSar();
+ if (sar == SegmentationAndReassembly::START) {
+ auto i_frame_start_view = EnhancedInformationStartFrameView::Create(i_frame_view);
+ if (!i_frame_start_view.IsValid()) {
+ LOG_WARN("Received invalid I-Frame START");
+ return;
+ }
+ pimpl_->recv_i_frame(f, tx_seq, req_seq, sar, i_frame_start_view.GetL2capSduLength(),
+ i_frame_start_view.GetPayload());
+ } else {
+ pimpl_->recv_i_frame(f, tx_seq, req_seq, sar, 0, i_frame_view.GetPayload());
+ }
} else if (type == FrameType::S_FRAME) {
auto s_frame_view = EnhancedSupervisoryFrameView::Create(standard_frame_view);
if (!s_frame_view.IsValid()) {
@@ -872,8 +885,21 @@
LOG_WARN("Received invalid frame");
return;
}
- pimpl_->recv_i_frame(i_frame_view.GetF(), i_frame_view.GetTxSeq(), i_frame_view.GetReqSeq(), i_frame_view.GetSar(),
- i_frame_view.GetPayload());
+ Final f = i_frame_view.GetF();
+ uint8_t tx_seq = i_frame_view.GetTxSeq();
+ uint8_t req_seq = i_frame_view.GetReqSeq();
+ auto sar = i_frame_view.GetSar();
+ if (sar == SegmentationAndReassembly::START) {
+ auto i_frame_start_view = EnhancedInformationStartFrameWithFcsView::Create(i_frame_view);
+ if (!i_frame_start_view.IsValid()) {
+ LOG_WARN("Received invalid I-Frame START");
+ return;
+ }
+ pimpl_->recv_i_frame(f, tx_seq, req_seq, sar, i_frame_start_view.GetL2capSduLength(),
+ i_frame_start_view.GetPayload());
+ } else {
+ pimpl_->recv_i_frame(f, tx_seq, req_seq, sar, 0, i_frame_view.GetPayload());
+ }
} else if (type == FrameType::S_FRAME) {
auto s_frame_view = EnhancedSupervisoryFrameWithFcsView::Create(standard_frame_view);
if (!s_frame_view.IsValid()) {
@@ -908,10 +934,16 @@
return next;
}
-void ErtmController::stage_for_reassembly(SegmentationAndReassembly sar,
+void ErtmController::stage_for_reassembly(SegmentationAndReassembly sar, uint16_t sdu_size,
const packet::PacketView<kLittleEndian>& payload) {
switch (sar) {
case SegmentationAndReassembly::UNSEGMENTED:
+ if (sar_state_ != SegmentationAndReassembly::END) {
+ LOG_WARN("Received invalid SAR");
+ close_channel();
+ return;
+ }
+ // TODO: Enforce MTU
enqueue_buffer_.Enqueue(std::make_unique<packet::PacketView<kLittleEndian>>(payload), handler_);
break;
case SegmentationAndReassembly::START:
@@ -920,8 +952,10 @@
close_channel();
return;
}
+ // TODO: Enforce MTU
sar_state_ = SegmentationAndReassembly::START;
reassembly_stage_ = payload;
+ remaining_sdu_continuation_packet_size_ = sdu_size - payload.size();
break;
case SegmentationAndReassembly::CONTINUATION:
if (sar_state_ == SegmentationAndReassembly::END) {
@@ -930,6 +964,7 @@
return;
}
reassembly_stage_.AppendPacketView(payload);
+ remaining_sdu_continuation_packet_size_ -= payload.size();
break;
case SegmentationAndReassembly::END:
if (sar_state_ == SegmentationAndReassembly::END) {
@@ -937,9 +972,17 @@
close_channel();
return;
}
+ sar_state_ = SegmentationAndReassembly::END;
+ remaining_sdu_continuation_packet_size_ -= payload.size();
+ if (remaining_sdu_continuation_packet_size_ != 0) {
+ LOG_WARN("Received invalid END I-Frame");
+ reassembly_stage_ = PacketViewForReassembly(std::make_shared<std::vector<uint8_t>>());
+ remaining_sdu_continuation_packet_size_ = 0;
+ close_channel();
+ return;
+ }
reassembly_stage_.AppendPacketView(payload);
enqueue_buffer_.Enqueue(std::make_unique<packet::PacketView<kLittleEndian>>(reassembly_stage_), handler_);
- sar_state_ = SegmentationAndReassembly::END;
break;
}
}
diff --git a/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.h b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.h
index 14deea3..dd1ca64 100644
--- a/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.h
+++ b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.h
@@ -59,7 +59,19 @@
os::Handler* handler_;
std::queue<std::unique_ptr<packet::BasePacketBuilder>> pdu_queue_;
Scheduler* scheduler_;
+
+ // Configuration options
bool fcs_enabled_ = false;
+ uint16_t local_tx_window_ = 10;
+ uint16_t local_max_transmit_ = 20;
+ uint16_t local_retransmit_timeout_ms_ = 2000;
+ uint16_t local_monitor_timeout_ms_ = 12000;
+
+ uint16_t remote_tx_window_ = 10;
+ uint16_t remote_mps_ = 1010;
+
+ uint16_t size_each_packet_ =
+ (remote_mps_ - 4 /* basic L2CAP header */ - 2 /* SDU length */ - 2 /* Extended control */ - 2 /* FCS */);
class PacketViewForReassembly : public packet::PacketView<kLittleEndian> {
public:
@@ -83,8 +95,10 @@
PacketViewForReassembly reassembly_stage_{std::make_shared<std::vector<uint8_t>>()};
SegmentationAndReassembly sar_state_ = SegmentationAndReassembly::END;
+ uint16_t remaining_sdu_continuation_packet_size_ = 0;
- void stage_for_reassembly(SegmentationAndReassembly sar, const packet::PacketView<kLittleEndian>& payload);
+ void stage_for_reassembly(SegmentationAndReassembly sar, uint16_t sdu_size,
+ const packet::PacketView<kLittleEndian>& payload);
void send_pdu(std::unique_ptr<packet::BasePacketBuilder> pdu);
void close_channel();
@@ -92,18 +106,6 @@
void on_pdu_no_fcs(const packet::PacketView<true>& pdu);
void on_pdu_fcs(const packet::PacketView<true>& pdu);
- // Configuration options
- uint16_t local_tx_window_ = 10;
- uint16_t local_max_transmit_ = 20;
- uint16_t local_retransmit_timeout_ms_ = 2000;
- uint16_t local_monitor_timeout_ms_ = 12000;
-
- uint16_t remote_tx_window_ = 10;
- uint16_t remote_mps_ = 1010;
-
- uint16_t size_each_packet_ =
- (remote_mps_ - 4 /* basic L2CAP header */ - 2 /* SDU length */ - 2 /* Extended control */ - 2 /* FCS */);
-
struct impl;
std::unique_ptr<impl> pimpl_;
};
diff --git a/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller_test.cc b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller_test.cc
index 04ef82b..e1b5818 100644
--- a/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller_test.cc
+++ b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller_test.cc
@@ -108,6 +108,54 @@
EXPECT_EQ(data, "abcd");
}
+TEST_F(ErtmDataControllerTest, reassemble_valid_sdu) {
+ common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+ testing::MockScheduler scheduler;
+ ErtmController controller{1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+ auto segment1 = CreateSdu({'a'});
+ auto segment2 = CreateSdu({'b', 'c'});
+ auto segment3 = CreateSdu({'d', 'e', 'f'});
+ auto builder1 = EnhancedInformationStartFrameBuilder::Create(1, 0, Final::NOT_SET, 0, 6, std::move(segment1));
+ auto base_view = GetPacketView(std::move(builder1));
+ controller.OnPdu(base_view);
+ auto builder2 = EnhancedInformationFrameBuilder::Create(1, 1, Final::NOT_SET, 0,
+ SegmentationAndReassembly::CONTINUATION, std::move(segment2));
+ base_view = GetPacketView(std::move(builder2));
+ controller.OnPdu(base_view);
+ auto builder3 = EnhancedInformationFrameBuilder::Create(1, 2, Final::NOT_SET, 0, SegmentationAndReassembly::END,
+ std::move(segment3));
+ base_view = GetPacketView(std::move(builder3));
+ 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, "abcdef");
+}
+
+TEST_F(ErtmDataControllerTest, reassemble_invalid_sdu_size_in_start_frame) {
+ common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+ testing::MockScheduler scheduler;
+ ErtmController controller{1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+ auto segment1 = CreateSdu({'a'});
+ auto segment2 = CreateSdu({'b', 'c'});
+ auto segment3 = CreateSdu({'d', 'e', 'f'});
+ auto builder1 = EnhancedInformationStartFrameBuilder::Create(1, 0, Final::NOT_SET, 0, 10, std::move(segment1));
+ auto base_view = GetPacketView(std::move(builder1));
+ controller.OnPdu(base_view);
+ auto builder2 = EnhancedInformationFrameBuilder::Create(1, 1, Final::NOT_SET, 0,
+ SegmentationAndReassembly::CONTINUATION, std::move(segment2));
+ base_view = GetPacketView(std::move(builder2));
+ controller.OnPdu(base_view);
+ auto builder3 = EnhancedInformationFrameBuilder::Create(1, 2, Final::NOT_SET, 0, SegmentationAndReassembly::END,
+ std::move(segment3));
+ base_view = GetPacketView(std::move(builder3));
+ controller.OnPdu(base_view);
+ sync_handler(queue_handler_);
+ auto payload = channel_queue.GetUpEnd()->TryDequeue();
+ EXPECT_EQ(payload, nullptr);
+}
+
TEST_F(ErtmDataControllerTest, transmit_with_fcs) {
common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
testing::MockScheduler scheduler;
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