L2CAP ERTM FCS support

Bug: 144730758
Test: bluetooth_test_gd
Change-Id: I57b0c7f6a8c806e26c4f68ee75e8403e5a9090e9
diff --git a/gd/l2cap/internal/basic_mode_channel_data_controller.cc b/gd/l2cap/internal/basic_mode_channel_data_controller.cc
index 1a049aa..3575e25 100644
--- a/gd/l2cap/internal/basic_mode_channel_data_controller.cc
+++ b/gd/l2cap/internal/basic_mode_channel_data_controller.cc
@@ -16,6 +16,8 @@
 
 #include "l2cap/internal/basic_mode_channel_data_controller.h"
 
+#include "l2cap/l2cap_packets.h"
+
 namespace bluetooth {
 namespace l2cap {
 namespace internal {
@@ -31,11 +33,15 @@
   scheduler_->OnPacketsReady(cid_, 1);
 }
 
-void BasicModeDataController::OnPdu(BasicFrameView pdu) {
-  enqueue_buffer_.Enqueue(std::make_unique<PacketView<kLittleEndian>>(pdu.GetPayload()), handler_);
+void BasicModeDataController::OnPdu(packet::PacketView<true> pdu) {
+  auto basic_frame_view = BasicFrameView::Create(pdu);
+  if (!basic_frame_view.IsValid()) {
+    return;
+  }
+  enqueue_buffer_.Enqueue(std::make_unique<PacketView<kLittleEndian>>(basic_frame_view.GetPayload()), handler_);
 }
 
-std::unique_ptr<BasicFrameBuilder> BasicModeDataController::GetNextPacket() {
+std::unique_ptr<packet::BasePacketBuilder> BasicModeDataController::GetNextPacket() {
   auto next = std::move(pdu_queue_.front());
   pdu_queue_.pop();
   return next;
diff --git a/gd/l2cap/internal/basic_mode_channel_data_controller.h b/gd/l2cap/internal/basic_mode_channel_data_controller.h
index 41d03fa..8e40402 100644
--- a/gd/l2cap/internal/basic_mode_channel_data_controller.h
+++ b/gd/l2cap/internal/basic_mode_channel_data_controller.h
@@ -46,9 +46,9 @@
 
   void OnSdu(std::unique_ptr<packet::BasePacketBuilder> sdu) override;
 
-  void OnPdu(BasicFrameView pdu) override;
+  void OnPdu(packet::PacketView<true> pdu) override;
 
-  std::unique_ptr<BasicFrameBuilder> GetNextPacket() override;
+  std::unique_ptr<packet::BasePacketBuilder> GetNextPacket() override;
 
   void EnableFcs(bool enabled) override {}
   void SetRetransmissionAndFlowControlOptions(const RetransmissionAndFlowControlConfigurationOption& option) override {}
@@ -58,7 +58,7 @@
   Cid remote_cid_;
   os::EnqueueBuffer<UpperEnqueue> enqueue_buffer_;
   os::Handler* handler_;
-  std::queue<std::unique_ptr<BasicFrameBuilder>> pdu_queue_;
+  std::queue<std::unique_ptr<packet::BasePacketBuilder>> pdu_queue_;
   Scheduler* scheduler_;
 };
 
diff --git a/gd/l2cap/internal/basic_mode_channel_data_controller_test.cc b/gd/l2cap/internal/basic_mode_channel_data_controller_test.cc
index 35c3002..c4cc56d 100644
--- a/gd/l2cap/internal/basic_mode_channel_data_controller_test.cc
+++ b/gd/l2cap/internal/basic_mode_channel_data_controller_test.cc
@@ -74,22 +74,28 @@
   testing::MockScheduler scheduler;
   BasicModeDataController controller{1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
   EXPECT_CALL(scheduler, OnPacketsReady(1, 1));
-  controller.OnSdu(CreateSdu({1, 2, 3}));
+  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 payload = pdu_view.GetPayload();
+  std::string data = std::string(payload.begin(), payload.end());
+  EXPECT_EQ(data, "abcd");
 }
 
 TEST_F(BasicModeDataControllerTest, receive) {
   common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
   testing::MockScheduler scheduler;
   BasicModeDataController controller{1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
-  auto base_view = GetPacketView(CreateSdu({0, 0, 1, 0}));
-  auto basic_frame_view = BasicFrameView::Create(base_view);
-  EXPECT_TRUE(basic_frame_view.IsValid());
-  controller.OnPdu(basic_frame_view);
+  auto base_view = GetPacketView(BasicFrameBuilder::Create(1, CreateSdu({'a', 'b', 'c', 'd'})));
+  controller.OnPdu(base_view);
   sync_handler(queue_handler_);
   auto packet_view = channel_queue.GetUpEnd()->TryDequeue();
   EXPECT_NE(packet_view, nullptr);
+  std::string data = std::string(packet_view->begin(), packet_view->end());
+  EXPECT_EQ(data, "abcd");
 }
 
 }  // namespace
diff --git a/gd/l2cap/internal/data_controller.h b/gd/l2cap/internal/data_controller.h
index 33bfbcf..bc7c2e5 100644
--- a/gd/l2cap/internal/data_controller.h
+++ b/gd/l2cap/internal/data_controller.h
@@ -34,10 +34,10 @@
   virtual void OnSdu(std::unique_ptr<packet::BasePacketBuilder> sdu) = 0;
 
   // PDUs -> SDU and enqueue to channel queue end
-  virtual void OnPdu(BasicFrameView pdu) = 0;
+  virtual void OnPdu(packet::PacketView<true> pdu) = 0;
 
   // Used by Scheduler to get next PDU
-  virtual std::unique_ptr<BasicFrameBuilder> GetNextPacket() = 0;
+  virtual std::unique_ptr<packet::BasePacketBuilder> GetNextPacket() = 0;
 
   // Set FCS mode. This only applies to some modes (ERTM).
   virtual void EnableFcs(bool enabled) = 0;
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 08a4bbe..2a1b930 100644
--- a/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.cc
+++ b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.cc
@@ -555,13 +555,23 @@
 
   void _send_i_frame(SegmentationAndReassembly sar, std::unique_ptr<CopyablePacketBuilder> segment, uint8_t req_seq,
                      uint8_t tx_seq, uint16_t sdu_size = 0, Final f = Final::NOT_SET) {
-    std::unique_ptr<EnhancedInformationFrameBuilder> builder;
+    std::unique_ptr<packet::BasePacketBuilder> builder;
     if (sar == SegmentationAndReassembly::START) {
-      builder = EnhancedInformationStartFrameBuilder::Create(controller_->remote_cid_, tx_seq, f, req_seq, sdu_size,
-                                                             std::move(segment));
+      if (controller_->fcs_enabled_) {
+        builder = EnhancedInformationStartFrameWithFcsBuilder::Create(controller_->remote_cid_, tx_seq, f, req_seq,
+                                                                      sdu_size, std::move(segment));
+      } else {
+        builder = EnhancedInformationStartFrameBuilder::Create(controller_->remote_cid_, tx_seq, f, req_seq, sdu_size,
+                                                               std::move(segment));
+      }
     } else {
-      builder = EnhancedInformationFrameBuilder::Create(controller_->remote_cid_, tx_seq, f, req_seq, sar,
-                                                        std::move(segment));
+      if (controller_->fcs_enabled_) {
+        builder = EnhancedInformationFrameWithFcsBuilder::Create(controller_->remote_cid_, tx_seq, f, req_seq, sar,
+                                                                 std::move(segment));
+      } else {
+        builder = EnhancedInformationFrameBuilder::Create(controller_->remote_cid_, tx_seq, f, req_seq, sar,
+                                                          std::move(segment));
+      }
     }
     controller_->send_pdu(std::move(builder));
   }
@@ -575,7 +585,6 @@
     std::unique_ptr<CopyablePacketBuilder> copyable_packet_builder =
         std::make_unique<CopyablePacketBuilder>(std::get<2>(unacked_list_.find(next_tx_seq_)->second));
     _send_i_frame(sar, std::move(copyable_packet_builder), buffer_seq_, next_tx_seq_, sdu_size, f);
-    // TODO hsz fix me
     unacked_frames_++;
     frames_sent_++;
     retry_i_frames_[next_tx_seq_] = 1;
@@ -599,7 +608,12 @@
   }
 
   void _send_s_frame(SupervisoryFunction s, uint8_t req_seq, Poll p, Final f) {
-    auto builder = EnhancedSupervisoryFrameBuilder::Create(controller_->remote_cid_, s, p, f, req_seq);
+    std::unique_ptr<packet::BasePacketBuilder> builder;
+    if (controller_->fcs_enabled_) {
+      builder = EnhancedSupervisoryFrameWithFcsBuilder::Create(controller_->remote_cid_, s, p, f, req_seq);
+    } else {
+      builder = EnhancedSupervisoryFrameBuilder::Create(controller_->remote_cid_, s, p, f, req_seq);
+    }
     controller_->send_pdu(std::move(builder));
   }
 
@@ -770,13 +784,9 @@
 
 // Segmentation is handled here
 void ErtmController::OnSdu(std::unique_ptr<packet::BasePacketBuilder> sdu) {
-  // TODO: Optimize the calculation. We don't need to count for SDU length in CONTINUATION or END packets. We don't need
-  // to FCS when disabled.
-  size_t size_each_packet =
-      (remote_mps_ - 4 /* basic L2CAP header */ - 2 /* SDU length */ - 2 /* Extended control */ - 2 /* FCS */);
   auto sdu_size = sdu->size();
   std::vector<std::unique_ptr<packet::RawBuilder>> segments;
-  packet::FragmentingInserter fragmenting_inserter(size_each_packet, std::back_insert_iterator(segments));
+  packet::FragmentingInserter fragmenting_inserter(size_each_packet_, std::back_insert_iterator(segments));
   sdu->Serialize(fragmenting_inserter);
   fragmenting_inserter.finalize();
   if (segments.size() == 1) {
@@ -790,8 +800,20 @@
   pimpl_->data_request(SegmentationAndReassembly::END, std::move(segments.back()));
 }
 
-void ErtmController::OnPdu(BasicFrameView pdu) {
-  auto standard_frame_view = StandardFrameView::Create(pdu);
+void ErtmController::OnPdu(packet::PacketView<true> pdu) {
+  if (fcs_enabled_) {
+    on_pdu_fcs(pdu);
+  } else {
+    on_pdu_no_fcs(pdu);
+  }
+}
+
+void ErtmController::on_pdu_no_fcs(const packet::PacketView<true>& pdu) {
+  auto basic_frame_view = BasicFrameView::Create(pdu);
+  if (!basic_frame_view.IsValid()) {
+    return;
+  }
+  auto standard_frame_view = StandardFrameView::Create(basic_frame_view);
   if (!standard_frame_view.IsValid()) {
     LOG_WARN("Received invalid frame");
     return;
@@ -833,7 +855,54 @@
   }
 }
 
-std::unique_ptr<BasicFrameBuilder> ErtmController::GetNextPacket() {
+void ErtmController::on_pdu_fcs(const packet::PacketView<true>& pdu) {
+  auto basic_frame_view = BasicFrameWithFcsView::Create(pdu);
+  if (!basic_frame_view.IsValid()) {
+    return;
+  }
+  auto standard_frame_view = StandardFrameWithFcsView::Create(basic_frame_view);
+  if (!standard_frame_view.IsValid()) {
+    LOG_WARN("Received invalid frame");
+    return;
+  }
+  auto type = standard_frame_view.GetFrameType();
+  if (type == FrameType::I_FRAME) {
+    auto i_frame_view = EnhancedInformationFrameWithFcsView::Create(standard_frame_view);
+    if (!i_frame_view.IsValid()) {
+      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());
+  } else if (type == FrameType::S_FRAME) {
+    auto s_frame_view = EnhancedSupervisoryFrameWithFcsView::Create(standard_frame_view);
+    if (!s_frame_view.IsValid()) {
+      LOG_WARN("Received invalid frame");
+      return;
+    }
+    auto req_seq = s_frame_view.GetReqSeq();
+    auto f = s_frame_view.GetF();
+    auto p = s_frame_view.GetP();
+    switch (s_frame_view.GetS()) {
+      case SupervisoryFunction::RECEIVER_READY:
+        pimpl_->recv_rr(req_seq, p, f);
+        break;
+      case SupervisoryFunction::RECEIVER_NOT_READY:
+        pimpl_->recv_rnr(req_seq, p, f);
+        break;
+      case SupervisoryFunction::REJECT:
+        pimpl_->recv_rej(req_seq, p, f);
+        break;
+      case SupervisoryFunction::SELECT_REJECT:
+        pimpl_->recv_srej(req_seq, p, f);
+        break;
+    }
+  } else {
+    LOG_WARN("Received invalid frame");
+  }
+}
+
+std::unique_ptr<packet::BasePacketBuilder> ErtmController::GetNextPacket() {
   auto next = std::move(pdu_queue_.front());
   pdu_queue_.pop();
   return next;
@@ -879,7 +948,7 @@
   fcs_enabled_ = enabled;
 }
 
-void ErtmController::send_pdu(std::unique_ptr<BasicFrameBuilder> pdu) {
+void ErtmController::send_pdu(std::unique_ptr<packet::BasePacketBuilder> pdu) {
   pdu_queue_.emplace(std::move(pdu));
   scheduler_->OnPacketsReady(cid_, 1);
 }
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 2038fd3..14deea3 100644
--- a/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.h
+++ b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.h
@@ -47,8 +47,8 @@
   ~ErtmController();
   // Segmentation is handled here
   void OnSdu(std::unique_ptr<packet::BasePacketBuilder> sdu) override;
-  void OnPdu(BasicFrameView pdu) override;
-  std::unique_ptr<BasicFrameBuilder> GetNextPacket() 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;
 
@@ -57,7 +57,7 @@
   Cid remote_cid_;
   os::EnqueueBuffer<UpperEnqueue> enqueue_buffer_;
   os::Handler* handler_;
-  std::queue<std::unique_ptr<BasicFrameBuilder>> pdu_queue_;
+  std::queue<std::unique_ptr<packet::BasePacketBuilder>> pdu_queue_;
   Scheduler* scheduler_;
   bool fcs_enabled_ = false;
 
@@ -85,10 +85,13 @@
   SegmentationAndReassembly sar_state_ = SegmentationAndReassembly::END;
 
   void stage_for_reassembly(SegmentationAndReassembly sar, const packet::PacketView<kLittleEndian>& payload);
-  void send_pdu(std::unique_ptr<BasicFrameBuilder> pdu);
+  void send_pdu(std::unique_ptr<packet::BasePacketBuilder> pdu);
 
   void close_channel();
 
+  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;
@@ -98,6 +101,9 @@
   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 dd87e36..04ef82b 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
@@ -70,7 +70,29 @@
   os::Handler* queue_handler_ = nullptr;
 };
 
-TEST_F(ErtmDataControllerTest, receive) {
+TEST_F(ErtmDataControllerTest, transmit_no_fcs) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  ErtmController controller{1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  EXPECT_CALL(scheduler, OnPacketsReady(1, 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 standard_view = StandardFrameView::Create(pdu_view);
+  EXPECT_TRUE(standard_view.IsValid());
+  auto i_frame_view = EnhancedInformationFrameView::Create(standard_view);
+  EXPECT_TRUE(i_frame_view.IsValid());
+  auto payload = i_frame_view.GetPayload();
+  std::string data = std::string(payload.begin(), payload.end());
+  EXPECT_EQ(data, "abcd");
+  EXPECT_EQ(i_frame_view.GetTxSeq(), 0);
+  EXPECT_EQ(i_frame_view.GetReqSeq(), 0);
+}
+
+TEST_F(ErtmDataControllerTest, receive_no_fcs) {
   common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
   testing::MockScheduler scheduler;
   ErtmController controller{1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
@@ -78,9 +100,47 @@
   auto builder = EnhancedInformationFrameBuilder::Create(1, 0, Final::NOT_SET, 0,
                                                          SegmentationAndReassembly::UNSEGMENTED, std::move(segment));
   auto base_view = GetPacketView(std::move(builder));
-  auto basic_frame_view = BasicFrameView::Create(base_view);
-  EXPECT_TRUE(basic_frame_view.IsValid());
-  controller.OnPdu(basic_frame_view);
+  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(ErtmDataControllerTest, transmit_with_fcs) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  ErtmController controller{1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  controller.EnableFcs(true);
+  EXPECT_CALL(scheduler, OnPacketsReady(1, 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 = BasicFrameWithFcsView::Create(view);
+  EXPECT_TRUE(pdu_view.IsValid());
+  auto standard_view = StandardFrameWithFcsView::Create(pdu_view);
+  EXPECT_TRUE(standard_view.IsValid());
+  auto i_frame_view = EnhancedInformationFrameWithFcsView::Create(standard_view);
+  EXPECT_TRUE(i_frame_view.IsValid());
+  auto payload = i_frame_view.GetPayload();
+  std::string data = std::string(payload.begin(), payload.end());
+  EXPECT_EQ(data, "abcd");
+  EXPECT_EQ(i_frame_view.GetTxSeq(), 0);
+  EXPECT_EQ(i_frame_view.GetReqSeq(), 0);
+}
+
+TEST_F(ErtmDataControllerTest, receive_packet_with_fcs) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  ErtmController controller{1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  controller.EnableFcs(true);
+  auto segment = CreateSdu({'a', 'b', 'c', 'd'});
+  auto builder = EnhancedInformationFrameWithFcsBuilder::Create(
+      1, 0, Final::NOT_SET, 0, SegmentationAndReassembly::UNSEGMENTED, 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);
diff --git a/gd/l2cap/internal/receiver.cc b/gd/l2cap/internal/receiver.cc
index 437064e..e92de0b 100644
--- a/gd/l2cap/internal/receiver.cc
+++ b/gd/l2cap/internal/receiver.cc
@@ -48,7 +48,7 @@
     LOG_WARN("Received a packet with invalid cid: %d", cid);
     return;
   }
-  data_controller->OnPdu(basic_frame_view);
+  data_controller->OnPdu(*packet);
 }
 
 }  // namespace internal