Merge changes I37defeed,I6fbe6ef8 am: 45413edfa2
am: 55ffc43032

Change-Id: I837c9ce87ca7d6845fe9f11e88b88a36dcb5dc15
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