Merge changes Ic6e58cb7,Ifbbcd7a9,I2214cc0f
am: 84f11f0281

Change-Id: If54d334068b77a38e5692e2f80bc678a51856039
diff --git a/gd/l2cap/classic/cert/cert.cc b/gd/l2cap/classic/cert/cert.cc
index abb3561..4f28319 100644
--- a/gd/l2cap/classic/cert/cert.cc
+++ b/gd/l2cap/classic/cert/cert.cc
@@ -164,10 +164,10 @@
     if (request->retransmission_config().mode() == ChannelRetransmissionFlowControlMode::ERTM) {
       auto option = std::make_unique<RetransmissionAndFlowControlConfigurationOption>();
       option->mode_ = RetransmissionAndFlowControlModeOption::ENHANCED_RETRANSMISSION;
-      option->tx_window_size_ = 10;
-      option->max_transmit_ = 20;
-      option->retransmission_time_out_ = 2000;
-      option->monitor_time_out_ = 12000;
+      option->tx_window_size_ = 5;
+      option->max_transmit_ = 1;
+      option->retransmission_time_out_ = 1000;
+      option->monitor_time_out_ = 2000;
       option->maximum_pdu_size_ = 1010;
       config.push_back(std::move(option));
     }
@@ -407,6 +407,16 @@
         LogEvent(response);
         break;
       }
+      case CommandCode::DISCONNECTION_REQUEST: {
+        DisconnectionRequestView view = DisconnectionRequestView::Create(control_view);
+        ASSERT(view.IsValid());
+        FetchL2capLogResponse response;
+        response.mutable_disconnection_request()->set_signal_id(control_view.GetIdentifier());
+        response.mutable_disconnection_request()->set_dcid(view.GetDestinationCid());
+        response.mutable_disconnection_request()->set_scid(view.GetSourceCid());
+        LogEvent(response);
+        break;
+      }
       case CommandCode::DISCONNECTION_RESPONSE: {
         DisconnectionResponseView view = DisconnectionResponseView::Create(control_view);
         ASSERT(view.IsValid());
diff --git a/gd/l2cap/classic/cert/simple_l2cap_test.py b/gd/l2cap/classic/cert/simple_l2cap_test.py
index 607cfb2..3320694 100644
--- a/gd/l2cap/classic/cert/simple_l2cap_test.py
+++ b/gd/l2cap/classic/cert/simple_l2cap_test.py
@@ -142,6 +142,7 @@
             self.cert_device.l2cap.SendConfigurationResponse(l2cap_cert_pb2.ConfigurationResponse(
                 scid=dcid,
                 signal_id=log.signal_id,
+                retransmission_config=l2cap_cert_pb2.ChannelRetransmissionFlowControlConfig(mode=self.retransmission_mode)
                 ))
         log_event_handler.on(is_configuration_request, handle_configuration_request)
 
@@ -404,3 +405,55 @@
         assert info_response[0][0] == signal_id
         assert info_response[0][1] == expected_log_type
         assert info_response[0][2] | expected_mask == expected_mask
+
+    def test_transmit_i_frames(self):
+        """
+        L2CAP/ERM/BV-01-C [Transmit I-frames]
+        """
+        self.retransmission_mode = l2cap_cert_pb2.ChannelRetransmissionFlowControlMode.ERTM
+        self.device_under_test.l2cap.RegisterChannel(l2cap_facade_pb2.RegisterChannelRequest(channel=2))
+        self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=0x33, retransmission_mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM))
+        self._setup_link()
+        scid = 0x0101
+        self._open_channel(scid=scid)
+        self.device_under_test.l2cap.SendDynamicChannelPacket(l2cap_facade_pb2.DynamicChannelPacket(psm=0x33, payload=b'abc'))
+        self.cert_device.l2cap.SendSFrame(l2cap_cert_pb2.SFrame(channel=self.scid_dcid_map[scid], req_seq=1, s=0))
+        self.device_under_test.l2cap.SendDynamicChannelPacket(l2cap_facade_pb2.DynamicChannelPacket(psm=0x33, payload=b'abc'))
+        self.cert_device.l2cap.SendSFrame(l2cap_cert_pb2.SFrame(channel=self.scid_dcid_map[scid], req_seq=2, s=0))
+        self.device_under_test.l2cap.SendDynamicChannelPacket(l2cap_facade_pb2.DynamicChannelPacket(psm=0x33, payload=b'abc'))
+        self.cert_device.l2cap.SendSFrame(l2cap_cert_pb2.SFrame(channel=self.scid_dcid_map[scid], req_seq=3, s=0))
+        data_received = []
+        event_handler = EventHandler()
+        def on_data_received(log):
+            log = log.data_packet
+            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())
+        event_handler.execute(logs)
+        assert len(data_received) == 3
+
+    def test_s_frame_transmissions_exceed_max_transmit(self):
+        """
+        L2CAP/ERM/BV-11-C [S-Frame Transmissions Exceed MaxTransmit]
+        """
+        self.retransmission_mode = l2cap_cert_pb2.ChannelRetransmissionFlowControlMode.ERTM
+        self.device_under_test.l2cap.RegisterChannel(l2cap_facade_pb2.RegisterChannelRequest(channel=2))
+        self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=0x33, retransmission_mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM))
+        self._setup_link()
+        scid = 0x0101
+        self._open_channel(scid=scid)
+        self.device_under_test.l2cap.SendDynamicChannelPacket(l2cap_facade_pb2.DynamicChannelPacket(psm=0x33, payload=b'abc'))
+        # Retransmission timer = 1, 1 * monitor timer = 2, so total timeout is 3
+        time.sleep(4)
+        disconnect_request = []
+        event_handler = EventHandler()
+
+        def on_disconnect_req(log):
+            log = log.disconnection_request
+            disconnect_request.append((log.dcid, log.scid))
+        event_handler.on(is_disconnection_request, on_disconnect_req)
+
+        logs = self.cert_device.l2cap.FetchL2capLog(l2cap_cert_pb2.FetchL2capLogRequest())
+        event_handler.execute(logs)
+        assert len(disconnect_request) == 1, "No disconnect request received"
+        assert disconnect_request[0] == (scid, self.scid_dcid_map[scid]), "Incorrect disconnect request received: scid %r, dcid %r" % (disconnect_request[0][0], disconnect_request[0][1])
diff --git a/gd/l2cap/classic/dynamic_channel.h b/gd/l2cap/classic/dynamic_channel.h
new file mode 100644
index 0000000..0bd5a82
--- /dev/null
+++ b/gd/l2cap/classic/dynamic_channel.h
@@ -0,0 +1,29 @@
+/*
+ * 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 "l2cap/dynamic_channel.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+
+using DynamicChannel = l2cap::DynamicChannel;
+
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/dynamic_channel_manager.h b/gd/l2cap/classic/dynamic_channel_manager.h
index 4810270..340d68d 100644
--- a/gd/l2cap/classic/dynamic_channel_manager.h
+++ b/gd/l2cap/classic/dynamic_channel_manager.h
@@ -19,9 +19,9 @@
 
 #include "hci/acl_manager.h"
 #include "hci/address.h"
+#include "l2cap/classic/dynamic_channel.h"
 #include "l2cap/classic/dynamic_channel_configuration_option.h"
 #include "l2cap/classic/dynamic_channel_service.h"
-#include "l2cap/dynamic_channel.h"
 #include "l2cap/l2cap_packets.h"
 #include "l2cap/psm.h"
 #include "l2cap/security_policy.h"
diff --git a/gd/l2cap/classic/internal/dynamic_channel_service_impl.h b/gd/l2cap/classic/internal/dynamic_channel_service_impl.h
index 672d24b..2fcf3fc 100644
--- a/gd/l2cap/classic/internal/dynamic_channel_service_impl.h
+++ b/gd/l2cap/classic/internal/dynamic_channel_service_impl.h
@@ -18,10 +18,10 @@
 
 #include "common/bind.h"
 
+#include "l2cap/classic/dynamic_channel.h"
 #include "l2cap/classic/dynamic_channel_configuration_option.h"
 #include "l2cap/classic/dynamic_channel_manager.h"
 #include "l2cap/classic/dynamic_channel_service.h"
-#include "l2cap/dynamic_channel.h"
 
 namespace bluetooth {
 namespace l2cap {
diff --git a/gd/l2cap/classic/internal/link.cc b/gd/l2cap/classic/internal/link.cc
index a23f5ec..142e32e 100644
--- a/gd/l2cap/classic/internal/link.cc
+++ b/gd/l2cap/classic/internal/link.cc
@@ -34,8 +34,9 @@
            DynamicChannelServiceManagerImpl* dynamic_service_manager,
            FixedChannelServiceManagerImpl* fixed_service_manager)
     : l2cap_handler_(l2cap_handler), acl_connection_(std::move(acl_connection)),
-      data_pipeline_manager_(l2cap_handler, acl_connection_->GetAclQueueEnd()), parameter_provider_(parameter_provider),
-      dynamic_service_manager_(dynamic_service_manager), fixed_service_manager_(fixed_service_manager),
+      data_pipeline_manager_(l2cap_handler, this, acl_connection_->GetAclQueueEnd()),
+      parameter_provider_(parameter_provider), dynamic_service_manager_(dynamic_service_manager),
+      fixed_service_manager_(fixed_service_manager),
       signalling_manager_(l2cap_handler_, this, &data_pipeline_manager_, dynamic_service_manager_,
                           &dynamic_channel_allocator_, fixed_service_manager_) {
   ASSERT(l2cap_handler_ != nullptr);
@@ -91,6 +92,7 @@
   auto channel = dynamic_channel_allocator_.AllocateChannel(psm, remote_cid, security_policy);
   if (channel != nullptr) {
     data_pipeline_manager_.AttachChannel(channel->GetCid(), channel);
+    RefreshRefCount();
   }
   channel->local_initiated_ = false;
   return channel;
@@ -101,6 +103,7 @@
   auto channel = dynamic_channel_allocator_.AllocateReservedChannel(reserved_cid, psm, remote_cid, security_policy);
   if (channel != nullptr) {
     data_pipeline_manager_.AttachChannel(channel->GetCid(), channel);
+    RefreshRefCount();
   }
   channel->local_initiated_ = true;
   return channel;
@@ -118,6 +121,7 @@
   }
   data_pipeline_manager_.DetachChannel(cid);
   dynamic_channel_allocator_.FreeChannel(cid);
+  RefreshRefCount();
 }
 
 void Link::RefreshRefCount() {
diff --git a/gd/l2cap/classic/internal/signalling_manager.h b/gd/l2cap/classic/internal/signalling_manager.h
index 81c8c31..3ae95dd 100644
--- a/gd/l2cap/classic/internal/signalling_manager.h
+++ b/gd/l2cap/classic/internal/signalling_manager.h
@@ -112,7 +112,6 @@
   l2cap::internal::DynamicChannelAllocator* channel_allocator_;
   FixedChannelServiceManagerImpl* fixed_service_manager_;
   std::unique_ptr<os::EnqueueBuffer<packet::BasePacketBuilder>> enqueue_buffer_;
-  PendingCommand last_sent_command_;
   std::queue<PendingCommand> pending_commands_;
   os::Alarm alarm_;
   SignalId next_signal_id_ = kInitialSignalId;
diff --git a/gd/l2cap/internal/data_pipeline_manager.cc b/gd/l2cap/internal/data_pipeline_manager.cc
index 1c8d973..674df95 100644
--- a/gd/l2cap/internal/data_pipeline_manager.cc
+++ b/gd/l2cap/internal/data_pipeline_manager.cc
@@ -29,7 +29,7 @@
 void DataPipelineManager::AttachChannel(Cid cid, std::shared_ptr<ChannelImpl> channel) {
   ASSERT(sender_map_.find(cid) == sender_map_.end());
   sender_map_.emplace(std::piecewise_construct, std::forward_as_tuple(cid),
-                      std::forward_as_tuple(handler_, scheduler_.get(), channel));
+                      std::forward_as_tuple(handler_, link_, scheduler_.get(), channel));
 }
 
 void DataPipelineManager::DetachChannel(Cid cid) {
diff --git a/gd/l2cap/internal/data_pipeline_manager.h b/gd/l2cap/internal/data_pipeline_manager.h
index fffbfc7..557a741 100644
--- a/gd/l2cap/internal/data_pipeline_manager.h
+++ b/gd/l2cap/internal/data_pipeline_manager.h
@@ -39,11 +39,12 @@
 namespace bluetooth {
 namespace l2cap {
 namespace internal {
+class ILink;
 
 /**
  * Manages data pipeline from channel queue end to link queue end, per link.
  * Contains a Scheduler and Receiver per link.
- * Contains a Sender and its corrsponding DataController per attached channel.
+ * Contains a Sender and its corresponding DataController per attached channel.
  */
 class DataPipelineManager {
  public:
@@ -54,8 +55,8 @@
   using LowerDequeue = UpperEnqueue;
   using LowerQueueUpEnd = common::BidiQueueEnd<LowerEnqueue, LowerDequeue>;
 
-  DataPipelineManager(os::Handler* handler, LowerQueueUpEnd* link_queue_up_end)
-      : handler_(handler), scheduler_(std::make_unique<Fifo>(this, link_queue_up_end, handler)),
+  DataPipelineManager(os::Handler* handler, ILink* link, LowerQueueUpEnd* link_queue_up_end)
+      : handler_(handler), link_(link), scheduler_(std::make_unique<Fifo>(this, link_queue_up_end, handler)),
         receiver_(link_queue_up_end, handler, this) {}
 
   virtual void AttachChannel(Cid cid, std::shared_ptr<ChannelImpl> channel);
@@ -67,6 +68,7 @@
 
  private:
   os::Handler* handler_;
+  ILink* link_;
   std::unique_ptr<Scheduler> scheduler_;
   Receiver receiver_;
   std::unordered_map<Cid, Sender> sender_map_;
diff --git a/gd/l2cap/internal/data_pipeline_manager_mock.h b/gd/l2cap/internal/data_pipeline_manager_mock.h
index f5a24f4..9c2f6c8 100644
--- a/gd/l2cap/internal/data_pipeline_manager_mock.h
+++ b/gd/l2cap/internal/data_pipeline_manager_mock.h
@@ -32,7 +32,7 @@
 class MockDataPipelineManager : public DataPipelineManager {
  public:
   MockDataPipelineManager(os::Handler* handler, LowerQueueUpEnd* link_queue_up_end)
-      : DataPipelineManager(handler, link_queue_up_end) {}
+      : DataPipelineManager(handler, nullptr, link_queue_up_end) {}
   MOCK_METHOD(void, AttachChannel, (Cid, std::shared_ptr<ChannelImpl>), (override));
   MOCK_METHOD(void, DetachChannel, (Cid), (override));
   MOCK_METHOD(DataController*, GetDataController, (Cid), (override));
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 97f9702..74e8a2f 100644
--- a/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.cc
+++ b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.cc
@@ -21,6 +21,7 @@
 #include <vector>
 
 #include "common/bind.h"
+#include "l2cap/internal/ilink.h"
 #include "os/alarm.h"
 #include "packet/fragmenting_inserter.h"
 #include "packet/raw_builder.h"
@@ -28,10 +29,10 @@
 namespace bluetooth {
 namespace l2cap {
 namespace internal {
-ErtmController::ErtmController(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),
-      pimpl_(std::make_unique<impl>(this, handler)) {}
+ErtmController::ErtmController(ILink* link, Cid cid, Cid remote_cid, UpperQueueDownEnd* channel_queue_end,
+                               os::Handler* handler, Scheduler* scheduler)
+    : link_(link), cid_(cid), remote_cid_(remote_cid), enqueue_buffer_(channel_queue_end), handler_(handler),
+      scheduler_(scheduler), pimpl_(std::make_unique<impl>(this, handler)) {}
 
 ErtmController::~ErtmController() = default;
 
@@ -153,7 +154,7 @@
       send_rr_or_rnr(Poll::POLL);
       // send rr or rnr(p=1)
       retry_count_ = 1;
-      start_retrans_timer();
+      start_monitor_timer();
       tx_state_ = TxState::WAIT_F;
     }
   }
@@ -770,7 +771,7 @@
   }
 
   void CloseChannel() {
-    // TODO: Needs a reference to signaller
+    controller_->close_channel();
   }
 
   void pop_srej_list() {
@@ -1005,7 +1006,7 @@
 }
 
 void ErtmController::close_channel() {
-  // TODO: Get a reference to signalling manager
+  link_->SendDisconnectionRequest(cid_, remote_cid_);
 }
 
 size_t ErtmController::CopyablePacketBuilder::size() const {
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 dd1ca64..184d6c1 100644
--- a/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.h
+++ b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.h
@@ -36,13 +36,14 @@
 namespace bluetooth {
 namespace l2cap {
 namespace internal {
+class ILink;
 
 class ErtmController : public DataController {
  public:
   using UpperEnqueue = packet::PacketView<packet::kLittleEndian>;
   using UpperDequeue = packet::BasePacketBuilder;
   using UpperQueueDownEnd = common::BidiQueueEnd<UpperEnqueue, UpperDequeue>;
-  ErtmController(Cid cid, Cid remote_cid, UpperQueueDownEnd* channel_queue_end, os::Handler* handler,
+  ErtmController(ILink* link, Cid cid, Cid remote_cid, UpperQueueDownEnd* channel_queue_end, os::Handler* handler,
                  Scheduler* scheduler);
   ~ErtmController();
   // Segmentation is handled here
@@ -53,6 +54,7 @@
   void SetRetransmissionAndFlowControlOptions(const RetransmissionAndFlowControlConfigurationOption& option) override;
 
  private:
+  ILink* link_;
   Cid cid_;
   Cid remote_cid_;
   os::EnqueueBuffer<UpperEnqueue> enqueue_buffer_;
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 e1b5818..f813898 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
@@ -18,6 +18,7 @@
 
 #include <gtest/gtest.h>
 
+#include "l2cap/internal/ilink_mock.h"
 #include "l2cap/internal/scheduler_mock.h"
 #include "l2cap/l2cap_packets.h"
 #include "packet/raw_builder.h"
@@ -73,7 +74,8 @@
 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};
+  testing::MockILink link;
+  ErtmController controller{&link, 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();
@@ -95,7 +97,8 @@
 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};
+  testing::MockILink link;
+  ErtmController controller{&link, 1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
   auto segment = CreateSdu({'a', 'b', 'c', 'd'});
   auto builder = EnhancedInformationFrameBuilder::Create(1, 0, Final::NOT_SET, 0,
                                                          SegmentationAndReassembly::UNSEGMENTED, std::move(segment));
@@ -111,7 +114,8 @@
 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};
+  testing::MockILink link;
+  ErtmController controller{&link, 1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
   auto segment1 = CreateSdu({'a'});
   auto segment2 = CreateSdu({'b', 'c'});
   auto segment3 = CreateSdu({'d', 'e', 'f'});
@@ -133,10 +137,11 @@
   EXPECT_EQ(data, "abcdef");
 }
 
-TEST_F(ErtmDataControllerTest, reassemble_invalid_sdu_size_in_start_frame) {
+TEST_F(ErtmDataControllerTest, reassemble_invalid_sdu_size_in_start_frame_will_disconnect) {
   common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
   testing::MockScheduler scheduler;
-  ErtmController controller{1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  testing::MockILink link;
+  ErtmController controller{&link, 1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
   auto segment1 = CreateSdu({'a'});
   auto segment2 = CreateSdu({'b', 'c'});
   auto segment3 = CreateSdu({'d', 'e', 'f'});
@@ -150,6 +155,7 @@
   auto builder3 = EnhancedInformationFrameBuilder::Create(1, 2, Final::NOT_SET, 0, SegmentationAndReassembly::END,
                                                           std::move(segment3));
   base_view = GetPacketView(std::move(builder3));
+  EXPECT_CALL(link, SendDisconnectionRequest(1, 1));
   controller.OnPdu(base_view);
   sync_handler(queue_handler_);
   auto payload = channel_queue.GetUpEnd()->TryDequeue();
@@ -159,7 +165,8 @@
 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};
+  testing::MockILink link;
+  ErtmController controller{&link, 1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
   controller.EnableFcs(true);
   EXPECT_CALL(scheduler, OnPacketsReady(1, 1));
   controller.OnSdu(CreateSdu({'a', 'b', 'c', 'd'}));
@@ -182,7 +189,8 @@
 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};
+  testing::MockILink link;
+  ErtmController controller{&link, 1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
   controller.EnableFcs(true);
   auto segment = CreateSdu({'a', 'b', 'c', 'd'});
   auto builder = EnhancedInformationFrameWithFcsBuilder::Create(
diff --git a/gd/l2cap/internal/ilink_mock.h b/gd/l2cap/internal/ilink_mock.h
new file mode 100644
index 0000000..15c642b
--- /dev/null
+++ b/gd/l2cap/internal/ilink_mock.h
@@ -0,0 +1,38 @@
+/*
+ * 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 "l2cap/internal/ilink.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+namespace testing {
+
+class MockILink : public ILink {
+ public:
+  MOCK_METHOD(hci::AddressWithType, GetDevice, (), (override));
+  MOCK_METHOD(void, SendDisconnectionRequest, (Cid, Cid), (override));
+};
+
+}  // namespace testing
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/sender.cc b/gd/l2cap/internal/sender.cc
index 1c146cf..d4884b2 100644
--- a/gd/l2cap/internal/sender.cc
+++ b/gd/l2cap/internal/sender.cc
@@ -28,9 +28,9 @@
 namespace l2cap {
 namespace internal {
 
-Sender::Sender(os::Handler* handler, Scheduler* scheduler, std::shared_ptr<ChannelImpl> channel)
-    : handler_(handler), queue_end_(channel->GetQueueDownEnd()), scheduler_(scheduler), channel_id_(channel->GetCid()),
-      remote_channel_id_(channel->GetRemoteCid()),
+Sender::Sender(os::Handler* handler, ILink* link, Scheduler* scheduler, std::shared_ptr<ChannelImpl> channel)
+    : handler_(handler), link_(link), queue_end_(channel->GetQueueDownEnd()), scheduler_(scheduler),
+      channel_id_(channel->GetCid()), remote_channel_id_(channel->GetRemoteCid()),
       data_controller_(std::make_unique<BasicModeDataController>(channel_id_, remote_channel_id_, queue_end_, handler_,
                                                                  scheduler_)) {
   try_register_dequeue();
@@ -82,7 +82,7 @@
   }
   if (mode == RetransmissionAndFlowControlModeOption::ENHANCED_RETRANSMISSION) {
     data_controller_ =
-        std::make_unique<ErtmController>(channel_id_, remote_channel_id_, queue_end_, handler_, scheduler_);
+        std::make_unique<ErtmController>(link_, channel_id_, remote_channel_id_, queue_end_, handler_, scheduler_);
     data_controller_->SetRetransmissionAndFlowControlOptions(config.local_retransmission_and_flow_control_);
     data_controller_->EnableFcs(config.fcs_type_ == FcsType::DEFAULT);
     return;
diff --git a/gd/l2cap/internal/sender.h b/gd/l2cap/internal/sender.h
index 46e0006..0e8e78d 100644
--- a/gd/l2cap/internal/sender.h
+++ b/gd/l2cap/internal/sender.h
@@ -37,6 +37,7 @@
 namespace l2cap {
 namespace internal {
 class Scheduler;
+class ILink;
 
 /**
  * A middle layer between L2CAP channel and outgoing packet scheduler.
@@ -48,7 +49,7 @@
   using UpperDequeue = packet::BasePacketBuilder;
   using UpperQueueDownEnd = common::BidiQueueEnd<UpperEnqueue, UpperDequeue>;
 
-  Sender(os::Handler* handler, Scheduler* scheduler, std::shared_ptr<ChannelImpl> channel);
+  Sender(os::Handler* handler, ILink* link, Scheduler* scheduler, std::shared_ptr<ChannelImpl> channel);
   ~Sender();
 
   /**
@@ -67,6 +68,7 @@
 
  private:
   os::Handler* handler_;
+  ILink* link_;
   UpperQueueDownEnd* queue_end_;
   Scheduler* scheduler_;
   const Cid channel_id_;
diff --git a/gd/l2cap/internal/sender_test.cc b/gd/l2cap/internal/sender_test.cc
index cecdfa4..bd24fd4 100644
--- a/gd/l2cap/internal/sender_test.cc
+++ b/gd/l2cap/internal/sender_test.cc
@@ -77,7 +77,7 @@
     EXPECT_CALL(*mock_channel_, GetQueueDownEnd()).WillRepeatedly(Return(channel_queue_.GetDownEnd()));
     EXPECT_CALL(*mock_channel_, GetCid()).WillRepeatedly(Return(cid_));
     EXPECT_CALL(*mock_channel_, GetRemoteCid()).WillRepeatedly(Return(cid_));
-    sender_ = new Sender(queue_handler_, &scheduler_, mock_channel_);
+    sender_ = new Sender(queue_handler_, nullptr, &scheduler_, mock_channel_);
   }
 
   void TearDown() override {
diff --git a/gd/l2cap/le/dynamic_channel.h b/gd/l2cap/le/dynamic_channel.h
new file mode 100644
index 0000000..db1d822
--- /dev/null
+++ b/gd/l2cap/le/dynamic_channel.h
@@ -0,0 +1,29 @@
+/*
+ * 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 "l2cap/dynamic_channel.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace le {
+
+using DynamicChannel = l2cap::DynamicChannel;
+
+}  // namespace le
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/internal/link.h b/gd/l2cap/le/internal/link.h
index 2807353..945f54a 100644
--- a/gd/l2cap/le/internal/link.h
+++ b/gd/l2cap/le/internal/link.h
@@ -37,7 +37,7 @@
   Link(os::Handler* l2cap_handler, std::unique_ptr<hci::AclConnection> acl_connection,
        l2cap::internal::ParameterProvider* parameter_provider)
       : l2cap_handler_(l2cap_handler), acl_connection_(std::move(acl_connection)),
-        data_pipeline_manager_(l2cap_handler, acl_connection_->GetAclQueueEnd()),
+        data_pipeline_manager_(l2cap_handler, this, acl_connection_->GetAclQueueEnd()),
         parameter_provider_(parameter_provider) {
     ASSERT(l2cap_handler_ != nullptr);
     ASSERT(acl_connection_ != nullptr);
diff --git a/gd/shim/l2cap.cc b/gd/shim/l2cap.cc
index 7af8f77..d703972 100644
--- a/gd/shim/l2cap.cc
+++ b/gd/shim/l2cap.cc
@@ -50,7 +50,8 @@
 
 using ServiceInterfaceCallback =
     std::function<void(l2cap::Psm psm, l2cap::classic::DynamicChannelManager::RegistrationResult result)>;
-using ConnectionInterfaceCallback = std::function<void(l2cap::Psm psm, std::unique_ptr<l2cap::DynamicChannel>)>;
+using ConnectionInterfaceCallback =
+    std::function<void(l2cap::Psm psm, std::unique_ptr<l2cap::classic::DynamicChannel>)>;
 
 std::unique_ptr<packet::RawBuilder> MakeUniquePacket(const uint8_t* data, size_t len) {
   packet::RawBuilder builder;
@@ -62,7 +63,7 @@
 
 class ConnectionInterface {
  public:
-  ConnectionInterface(ConnectionInterfaceDescriptor cid, std::unique_ptr<l2cap::DynamicChannel> channel,
+  ConnectionInterface(ConnectionInterfaceDescriptor cid, std::unique_ptr<l2cap::classic::DynamicChannel> channel,
                       os::Handler* handler)
       : cid_(cid), channel_(std::move(channel)), handler_(handler), on_data_ready_callback_(nullptr),
         on_connection_closed_callback_(nullptr), address_(channel_->GetDevice()) {
@@ -138,7 +139,7 @@
 
  private:
   const ConnectionInterfaceDescriptor cid_;
-  const std::unique_ptr<l2cap::DynamicChannel> channel_;
+  const std::unique_ptr<l2cap::classic::DynamicChannel> channel_;
   os::Handler* handler_;
 
   ReadDataReadyCallback on_data_ready_callback_;
@@ -154,7 +155,7 @@
 
 struct ConnectionInterfaceManager {
  public:
-  ConnectionInterfaceDescriptor AddChannel(std::unique_ptr<l2cap::DynamicChannel> channel);
+  ConnectionInterfaceDescriptor AddChannel(std::unique_ptr<l2cap::classic::DynamicChannel> channel);
   void RemoveConnection(ConnectionInterfaceDescriptor cid);
 
   void SetReadDataReadyCallback(ConnectionInterfaceDescriptor cid, ReadDataReadyCallback on_data_ready);
@@ -217,7 +218,8 @@
   return current_connection_interface_descriptor_++;
 }
 
-ConnectionInterfaceDescriptor ConnectionInterfaceManager::AddChannel(std::unique_ptr<l2cap::DynamicChannel> channel) {
+ConnectionInterfaceDescriptor ConnectionInterfaceManager::AddChannel(
+    std::unique_ptr<l2cap::classic::DynamicChannel> channel) {
   if (!HasResources()) {
     return kInvalidConnectionInterfaceDescriptor;
   }
@@ -265,7 +267,7 @@
       : connection_interface_manager_(connection_interface_manager), psm_(psm), address_(address),
         on_open_(std::move(on_open)), completed_(std::move(completed)) {}
 
-  void OnConnectionOpen(std::unique_ptr<l2cap::DynamicChannel> channel) {
+  void OnConnectionOpen(std::unique_ptr<l2cap::classic::DynamicChannel> channel) {
     LOG_DEBUG("Local initiated connection is open to device:%s for psm:%hd", address_.ToString().c_str(), psm_);
     ConnectionInterfaceDescriptor cid = connection_interface_manager_->AddChannel(std::move(channel));
     completed_.set_value(cid);
@@ -320,7 +322,7 @@
     completed_.set_value();
   }
 
-  void OnConnectionOpen(std::unique_ptr<l2cap::DynamicChannel> channel) {
+  void OnConnectionOpen(std::unique_ptr<l2cap::classic::DynamicChannel> channel) {
     LOG_DEBUG("Remote initiated connection is open from device:%s for psm:%hd", channel->GetDevice().ToString().c_str(),
               psm_);
     hci::Address address = channel->GetDevice();