Merge "RootCanal: Gracefully handle HCI Event"
diff --git a/gd/hci/acl_fragmenter.cc b/gd/hci/acl_fragmenter.cc
index 189e52d..fa3b31d 100644
--- a/gd/hci/acl_fragmenter.cc
+++ b/gd/hci/acl_fragmenter.cc
@@ -29,6 +29,7 @@
   std::vector<std::unique_ptr<packet::RawBuilder>> to_return;
   packet::FragmentingInserter fragmenting_inserter(mtu_, std::back_insert_iterator(to_return));
   packet_->Serialize(fragmenting_inserter);
+  fragmenting_inserter.finalize();
   return to_return;
 }
 
diff --git a/gd/l2cap/classic/cert/api.proto b/gd/l2cap/classic/cert/api.proto
index 8492323..c7e68d5 100644
--- a/gd/l2cap/classic/cert/api.proto
+++ b/gd/l2cap/classic/cert/api.proto
@@ -24,6 +24,7 @@
   rpc SendInformationResponse(InformationResponse) returns (SendInformationResponseResult) {}
 
   rpc FetchL2capLog(FetchL2capLogRequest) returns (stream FetchL2capLogResponse) {}
+  rpc StopFetchingL2capLog(StopFetchingL2capLogRequest) returns (StopFetchingL2capLogResponse) {}
 }
 
 message L2capPacket {
@@ -58,10 +59,27 @@
 
 message SendConnectionResponseResult {}
 
+enum ChannelRetransmissionFlowControlMode {
+  BASIC = 0;
+  ERTM = 3;
+  STREAM = 4;
+}
+
+message ChannelRetransmissionFlowControlConfig {
+  ChannelRetransmissionFlowControlMode mode = 1;
+  uint32 tx_window = 2;
+  uint32 max_transmit = 3;
+  uint32 retransmit_timeout = 4;
+  uint32 monitor_timeout = 5;
+  uint32 mps = 6;
+}
+
 message ConfigurationRequest {
   uint32 dcid = 1;
   uint32 signal_id = 2;
-  repeated string configuration = 3;
+  uint32 mtu = 3;
+  ChannelRetransmissionFlowControlConfig retransmission_config = 4;
+  bool fcs = 5;
 }
 
 message SendConfigurationRequestResult {}
@@ -69,7 +87,9 @@
 message ConfigurationResponse {
   uint32 scid = 1;
   uint32 signal_id = 2;
-  repeated string configuration = 3;
+  uint32 mtu = 3;
+  ChannelRetransmissionFlowControlConfig retransmission_config = 4;
+  bool fcs = 5;
 }
 
 message SendConfigurationResponseResult {}
@@ -107,6 +127,7 @@
   InformationRequestType type = 1;
   uint32 data = 2;
   uint32 signal_id = 3;
+  uint32 information_value = 4;
 }
 
 message SendInformationResponseResult {}
@@ -153,3 +174,7 @@
     LinkDown link_down = 21;
   }
 }
+
+message StopFetchingL2capLogRequest {}
+
+message StopFetchingL2capLogResponse {}
diff --git a/gd/l2cap/classic/cert/cert.cc b/gd/l2cap/classic/cert/cert.cc
index 2e2ac59..63c8593 100644
--- a/gd/l2cap/classic/cert/cert.cc
+++ b/gd/l2cap/classic/cert/cert.cc
@@ -105,7 +105,22 @@
   ::grpc::Status SendConfigurationRequest(
       ::grpc::ServerContext* context, const ::bluetooth::l2cap::classic::cert::ConfigurationRequest* request,
       ::bluetooth::l2cap::classic::cert::SendConfigurationRequestResult* response) override {
-    auto builder = ConfigurationRequestBuilder::Create(request->signal_id(), request->dcid(), Continuation::END, {});
+    std::vector<std::unique_ptr<ConfigurationOption>> config;
+    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->maximum_pdu_size_ = 1010;
+      config.push_back(std::move(option));
+      auto no_fcs = std::make_unique<FrameCheckSequenceOption>();
+      no_fcs->fcs_type_ = FcsType::NO_FCS;
+      config.push_back(std::move(no_fcs));
+    }
+    auto builder = ConfigurationRequestBuilder::Create(request->signal_id(), request->dcid(), Continuation::END,
+                                                       std::move(config));
     auto l2cap_builder = BasicFrameBuilder::Create(kClassicSignallingCid, std::move(builder));
     outgoing_packet_queue_.push(std::move(l2cap_builder));
     send_packet_from_queue();
@@ -115,8 +130,19 @@
   ::grpc::Status SendConfigurationResponse(
       ::grpc::ServerContext* context, const ::bluetooth::l2cap::classic::cert::ConfigurationResponse* request,
       ::bluetooth::l2cap::classic::cert::SendConfigurationResponseResult* response) override {
+    std::vector<std::unique_ptr<ConfigurationOption>> config;
+    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->maximum_pdu_size_ = 1010;
+      config.push_back(std::move(option));
+    }
     auto builder = ConfigurationResponseBuilder::Create(request->signal_id(), request->scid(), Continuation::END,
-                                                        ConfigurationResponseResult::SUCCESS, {});
+                                                        ConfigurationResponseResult::SUCCESS, std::move(config));
     auto l2cap_builder = BasicFrameBuilder::Create(kClassicSignallingCid, std::move(builder));
     outgoing_packet_queue_.push(std::move(l2cap_builder));
     send_packet_from_queue();
@@ -183,7 +209,7 @@
     switch (request->type()) {
       case InformationRequestType::CONNECTIONLESS_MTU: {
         auto builder = InformationResponseConnectionlessMtuBuilder::Create(request->signal_id(),
-                                                                           InformationRequestResult::NOT_SUPPORTED, 0);
+                                                                           InformationRequestResult::SUCCESS, 100);
         auto l2cap_builder = BasicFrameBuilder::Create(kClassicSignallingCid, std::move(builder));
         outgoing_packet_queue_.push(std::move(l2cap_builder));
         send_packet_from_queue();
@@ -191,7 +217,7 @@
       }
       case InformationRequestType::EXTENDED_FEATURES: {
         auto builder = InformationResponseExtendedFeaturesBuilder::Create(
-            request->signal_id(), InformationRequestResult::NOT_SUPPORTED, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+            request->signal_id(), InformationRequestResult::SUCCESS, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0);
         auto l2cap_builder = BasicFrameBuilder::Create(kClassicSignallingCid, std::move(builder));
         outgoing_packet_queue_.push(std::move(l2cap_builder));
         send_packet_from_queue();
@@ -224,7 +250,8 @@
 
   ::grpc::Status FetchL2capLog(::grpc::ServerContext* context, const FetchL2capLogRequest* request,
                                ::grpc::ServerWriter<FetchL2capLogResponse>* writer) override {
-    while (!context->IsCancelled()) {
+    fetching_l2cap_log_ = true;
+    while (!context->IsCancelled() && fetching_l2cap_log_) {
       if (!l2cap_log_.empty()) {
         auto& response = l2cap_log_.front();
         writer->Write(response);
@@ -241,6 +268,16 @@
     }
     return ::grpc::Status::OK;
   }
+
+  ::grpc::Status StopFetchingL2capLog(
+      ::grpc::ServerContext* context, const ::bluetooth::l2cap::classic::cert::StopFetchingL2capLogRequest* request,
+      ::bluetooth::l2cap::classic::cert::StopFetchingL2capLogResponse* response) override {
+    fetching_l2cap_log_ = false;
+    l2cap_log_cv_.notify_one();
+    return ::grpc::Status::OK;
+  }
+
+  bool fetching_l2cap_log_ = false;
   std::mutex l2cap_log_mutex_;
   std::queue<FetchL2capLogResponse> l2cap_log_;
   std::condition_variable l2cap_log_cv_;
@@ -393,15 +430,31 @@
         auto type = information_response_view.GetInfoType();
         switch (type) {
           case InformationRequestInfoType::CONNECTIONLESS_MTU: {
+            auto view = InformationResponseConnectionlessMtuView::Create(information_response_view);
+            if (!view.IsValid()) {
+              return;
+            }
             log_response.mutable_information_response()->set_type(InformationRequestType::CONNECTIONLESS_MTU);
+            log_response.mutable_information_response()->set_information_value(view.GetConnectionlessMtu());
             break;
           }
           case InformationRequestInfoType::EXTENDED_FEATURES_SUPPORTED: {
+            auto view = InformationResponseExtendedFeaturesView::Create(information_response_view);
+            if (!view.IsValid()) {
+              return;
+            }
             log_response.mutable_information_response()->set_type(InformationRequestType::EXTENDED_FEATURES);
+            int mask = view.GetEnhancedRetransmissionMode() << 3 | view.GetFcsOption() << 5;
+            log_response.mutable_information_response()->set_information_value(mask);
             break;
           }
           case InformationRequestInfoType::FIXED_CHANNELS_SUPPORTED: {
+            auto view = InformationResponseFixedChannelsView::Create(information_response_view);
+            if (!view.IsValid()) {
+              return;
+            }
             log_response.mutable_information_response()->set_type(InformationRequestType::FIXED_CHANNELS);
+            log_response.mutable_information_response()->set_information_value(view.GetFixedChannels());
             break;
           }
         }
diff --git a/gd/l2cap/classic/cert/simple_l2cap_test.py b/gd/l2cap/classic/cert/simple_l2cap_test.py
index d0fafb3..38d39b6 100644
--- a/gd/l2cap/classic/cert/simple_l2cap_test.py
+++ b/gd/l2cap/classic/cert/simple_l2cap_test.py
@@ -74,6 +74,9 @@
 def is_command_reject(log):
     return log.HasField("command_reject")
 
+def basic_frame_to_enhanced_information_frame(information_payload):
+    return information_payload[2:]
+
 class SimpleL2capTest(GdBaseTestClass):
     def setup_test(self):
         self.device_under_test = self.gd_devices[0]
@@ -105,21 +108,30 @@
         log_event_handler = EventHandler()
         self.next_scid = 0x40
         self.scid_dcid_map = {}
+        self.retransmission_mode = l2cap_cert_pb2.ChannelRetransmissionFlowControlMode.BASIC
         def handle_connection_request(log):
             log = log.connection_request
             self.cert_device.l2cap.SendConnectionResponse(l2cap_cert_pb2.ConnectionResponse(dcid=self.next_scid,scid=log.scid,
                                                                                             signal_id=log.signal_id))
             self.scid_dcid_map[self.next_scid] = log.scid
             self.next_scid += 1
-            self.cert_device.l2cap.SendConfigurationRequest(l2cap_cert_pb2.ConfigurationRequest(dcid=log.scid,
-                                                                                            signal_id=log.signal_id+1))
+            self.cert_device.l2cap.SendConfigurationRequest(l2cap_cert_pb2.ConfigurationRequest(
+                dcid=log.scid,
+                signal_id=log.signal_id+1,
+                retransmission_config=l2cap_cert_pb2.ChannelRetransmissionFlowControlConfig(
+                    mode=self.retransmission_mode
+                )))
         log_event_handler.on(is_connection_request, handle_connection_request)
 
         def handle_connection_response(log):
             log = log.connection_response
             self.scid_dcid_map[log.scid] = log.dcid
-            self.cert_device.l2cap.SendConfigurationRequest(l2cap_cert_pb2.ConfigurationRequest(dcid=log.dcid,
-                                                                                              signal_id=log.signal_id+1))
+            self.cert_device.l2cap.SendConfigurationRequest(l2cap_cert_pb2.ConfigurationRequest(
+                dcid=log.dcid,
+                signal_id=log.signal_id+1,
+                retransmission_config=l2cap_cert_pb2.ChannelRetransmissionFlowControlConfig(
+                    mode=self.retransmission_mode
+                )))
         log_event_handler.on(is_connection_response, handle_connection_response)
 
         def handle_configuration_request(log):
@@ -127,8 +139,10 @@
             if log.dcid not in self.scid_dcid_map:
                 return
             dcid = self.scid_dcid_map[log.dcid]
-            self.cert_device.l2cap.SendConfigurationResponse(l2cap_cert_pb2.ConfigurationResponse(scid=dcid,
-                                                                                            signal_id=log.signal_id))
+            self.cert_device.l2cap.SendConfigurationResponse(l2cap_cert_pb2.ConfigurationResponse(
+                scid=dcid,
+                signal_id=log.signal_id,
+                ))
         log_event_handler.on(is_configuration_request, handle_configuration_request)
 
         def handle_disconnection_request(log):
@@ -168,7 +182,7 @@
         self.event_handler.execute(logs)
         assert self.dut_address in link_up_handled
 
-    def _open_channel(self, scid=0x0101, psm=0x01):
+    def _open_channel(self, scid=0x0101, psm=0x33):
         self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=psm))
 
         configuration_response_handled = []
@@ -184,10 +198,38 @@
     def test_connect(self):
         self._setup_link()
         self._open_channel(scid=0x0101)
+        self.cert_device.l2cap.StopFetchingL2capLog(l2cap_cert_pb2.StopFetchingL2capLogRequest())
+
+    def test_connect_and_send_data_ertm_no_segmentation(self):
+        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.SendL2capPacket(l2cap_facade_pb2.L2capPacket(channel=2, payload=b"123"))
+
+        data_received = []
+        event_handler = EventHandler()
+        def on_data_received(log):
+            log = log.data_packet
+            if (log.channel == scid):
+                log.payload = basic_frame_to_enhanced_information_frame(log.payload)
+            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 (2, b"123") in data_received
+
+        self.device_under_test.l2cap.SendDynamicChannelPacket(l2cap_facade_pb2.DynamicChannelPacket(psm=0x33, payload=b'abc'*34))
+        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))
-        self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=0x01))
+        self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=0x33))
         self._setup_link()
         scid = 0x0101
         self._open_channel(scid=scid)
@@ -203,15 +245,17 @@
         event_handler.execute(logs)
         assert (2, b"123") in data_received
 
-        self.device_under_test.l2cap.SendDynamicChannelPacket(l2cap_facade_pb2.DynamicChannelPacket(psm=1, payload=b'abc'))
+        self.device_under_test.l2cap.SendDynamicChannelPacket(l2cap_facade_pb2.DynamicChannelPacket(psm=0x33, payload=b'abc'))
         logs = self.cert_device.l2cap.FetchL2capLog(l2cap_cert_pb2.FetchL2capLogRequest())
         event_handler.execute(logs)
         assert (scid, b"abc") in data_received
+        self.cert_device.l2cap.StopFetchingL2capLog(l2cap_cert_pb2.StopFetchingL2capLogRequest())
 
     def test_open_two_channels(self):
         self._setup_link()
         self._open_channel(scid=0x0101, psm=0x1)
         self._open_channel(scid=0x0102, psm=0x3)
+        self.cert_device.l2cap.StopFetchingL2capLog(l2cap_cert_pb2.StopFetchingL2capLogRequest())
 
     def test_accept_disconnect(self):
         """
@@ -225,6 +269,7 @@
         def handle_disconnection_response(log):
             log = log.disconnection_response
             disconnection_response_handled.append((log.scid, log.dcid))
+            self.cert_device.l2cap.StopFetchingL2capLog(l2cap_cert_pb2.StopFetchingL2capLogRequest())
         self.event_handler.on(is_disconnection_response, handle_disconnection_response)
         self.cert_device.l2cap.SendDisconnectionRequest(l2cap_cert_pb2.DisconnectionRequest(scid=scid, dcid=dcid, signal_id=2))
         logs = self.cert_device.l2cap.FetchL2capLog(l2cap_cert_pb2.FetchL2capLogRequest())
@@ -262,11 +307,13 @@
         initiate the configuration procedure.
         """
         psm = 1
+        # TODO: Use another test case
         self.device_under_test.l2cap.OpenChannel(l2cap_facade_pb2.OpenChannelRequest(remote=self.cert_address, psm=psm))
         connection_request = []
         def handle_connection_request(log):
             log = log.connection_request
             connection_request.append(log.psm)
+            self.cert_device.l2cap.StopFetchingL2capLog(l2cap_cert_pb2.StopFetchingL2capLogRequest())
         self.event_handler.on(is_connection_request, handle_connection_request)
         logs = self.cert_device.l2cap.FetchL2capLog(l2cap_cert_pb2.FetchL2capLogRequest())
         self.event_handler.execute(logs)
@@ -285,6 +332,7 @@
         def handle_echo_response(log):
             log = log.echo_response
             echo_response.append(log.signal_id)
+            self.cert_device.l2cap.StopFetchingL2capLog(l2cap_cert_pb2.StopFetchingL2capLogRequest())
         self.event_handler.on(is_echo_response, handle_echo_response)
         logs = self.cert_device.l2cap.FetchL2capLog(l2cap_cert_pb2.FetchL2capLogRequest())
         self.event_handler.execute(logs)
@@ -303,6 +351,7 @@
         def handle_command_reject(log):
             log = log.command_reject
             command_reject.append(log.signal_id)
+            self.cert_device.l2cap.StopFetchingL2capLog(l2cap_cert_pb2.StopFetchingL2capLogRequest())
         self.event_handler.on(is_command_reject, handle_command_reject)
         logs = self.cert_device.l2cap.FetchL2capLog(l2cap_cert_pb2.FetchL2capLogRequest())
         self.event_handler.execute(logs)
@@ -321,7 +370,34 @@
         def handle_info_response(log):
             log = log.information_response
             info_response.append((log.signal_id, log.type))
+            self.cert_device.l2cap.StopFetchingL2capLog(l2cap_cert_pb2.StopFetchingL2capLogRequest())
         self.event_handler.on(is_information_response, handle_info_response)
         logs = self.cert_device.l2cap.FetchL2capLog(l2cap_cert_pb2.FetchL2capLogRequest())
         self.event_handler.execute(logs)
         assert (signal_id, l2cap_cert_pb2.InformationRequestType.FIXED_CHANNELS) in info_response
+
+
+    def test_extended_feature_info_response_ertm(self):
+        """
+        L2CAP/EXF/BV-01-C [Extended Features Information Response for Enhanced
+        Retransmission Mode]
+        """
+        self._setup_link()
+        signal_id = 3
+        self.cert_device.l2cap.SendInformationRequest(
+            l2cap_cert_pb2.InformationRequest(
+                type=l2cap_cert_pb2.InformationRequestType.EXTENDED_FEATURES, signal_id=signal_id))
+        info_response = []
+        def handle_info_response(log):
+            log = log.information_response
+            info_response.append((log.signal_id, log.type, log.information_value))
+            self.cert_device.l2cap.StopFetchingL2capLog(l2cap_cert_pb2.StopFetchingL2capLogRequest())
+        self.event_handler.on(is_information_response, handle_info_response)
+        logs = self.cert_device.l2cap.FetchL2capLog(l2cap_cert_pb2.FetchL2capLogRequest())
+        self.event_handler.execute(logs)
+        expected_log_type = l2cap_cert_pb2.InformationRequestType.EXTENDED_FEATURES
+        expected_mask = 1 << 3
+        assert len(info_response) == 1
+        assert info_response[0][0] == signal_id
+        assert info_response[0][1] == expected_log_type
+        assert info_response[0][2] | expected_mask == expected_mask
diff --git a/gd/l2cap/classic/dynamic_channel_configuration_option.h b/gd/l2cap/classic/dynamic_channel_configuration_option.h
new file mode 100644
index 0000000..3f3cc02
--- /dev/null
+++ b/gd/l2cap/classic/dynamic_channel_configuration_option.h
@@ -0,0 +1,48 @@
+/*
+ * 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/mtu.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+
+/**
+ * Configuration Option specified by L2CAP Channel user on a dynamic channel. L2CAP module will configure the channel
+ * based on user provided option.
+ */
+struct DynamicChannelConfigurationOption {
+  enum class RetransmissionAndFlowControlMode {
+    L2CAP_BASIC,
+    ENHANCED_RETRANSMISSION,
+  };
+  /**
+   * Retransmission and flow control mode. Currently L2CAP_BASIC and ENHANCED_RETRANSMISSION.
+   * If the remote doesn't support a mode, it might fall back to basic, as this is a negotiable option.
+   */
+  RetransmissionAndFlowControlMode channel_mode = RetransmissionAndFlowControlMode::L2CAP_BASIC;
+
+  /**
+   * Maximum SDU size that the L2CAP Channel user is able to process.
+   */
+  Mtu incoming_mtu = kDefaultClassicMtu;
+};
+
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/dynamic_channel_manager.cc b/gd/l2cap/classic/dynamic_channel_manager.cc
index 915b8fd..123fdd4 100644
--- a/gd/l2cap/classic/dynamic_channel_manager.cc
+++ b/gd/l2cap/classic/dynamic_channel_manager.cc
@@ -24,12 +24,14 @@
 namespace l2cap {
 namespace classic {
 
-bool DynamicChannelManager::ConnectChannel(hci::Address device, Psm psm, OnConnectionOpenCallback on_connection_open,
+bool DynamicChannelManager::ConnectChannel(hci::Address device, DynamicChannelConfigurationOption configuration_option,
+                                           Psm psm, OnConnectionOpenCallback on_connection_open,
                                            OnConnectionFailureCallback on_fail_callback, os::Handler* handler) {
   internal::Link::PendingDynamicChannelConnection pending_dynamic_channel_connection{
       .handler_ = handler,
       .on_open_callback_ = std::move(on_connection_open),
       .on_fail_callback_ = std::move(on_fail_callback),
+      .configuration_ = configuration_option,
   };
   l2cap_layer_handler_->Post(common::BindOnce(&internal::LinkManager::ConnectDynamicChannelServices,
                                               common::Unretained(link_manager_), device,
@@ -38,13 +40,16 @@
   return true;
 }
 
-bool DynamicChannelManager::RegisterService(Psm psm, const SecurityPolicy& security_policy,
+bool DynamicChannelManager::RegisterService(Psm psm, DynamicChannelConfigurationOption configuration_option,
+                                            const SecurityPolicy& security_policy,
                                             OnRegistrationCompleteCallback on_registration_complete,
                                             OnConnectionOpenCallback on_connection_open, os::Handler* handler) {
   internal::DynamicChannelServiceImpl::PendingRegistration pending_registration{
       .user_handler_ = handler,
       .on_registration_complete_callback_ = std::move(on_registration_complete),
-      .on_connection_open_callback_ = std::move(on_connection_open)};
+      .on_connection_open_callback_ = std::move(on_connection_open),
+      .configuration_ = configuration_option,
+  };
   l2cap_layer_handler_->Post(common::BindOnce(&internal::DynamicChannelServiceManagerImpl::Register,
                                               common::Unretained(service_manager_), psm,
                                               std::move(pending_registration)));
diff --git a/gd/l2cap/classic/dynamic_channel_manager.h b/gd/l2cap/classic/dynamic_channel_manager.h
index b6c0518..340d68d 100644
--- a/gd/l2cap/classic/dynamic_channel_manager.h
+++ b/gd/l2cap/classic/dynamic_channel_manager.h
@@ -20,6 +20,7 @@
 #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/l2cap_packets.h"
 #include "l2cap/psm.h"
@@ -89,11 +90,13 @@
    * @param on_open_callback: A callback to indicate success of a connection initiated from a remote device.
    * @param on_fail_callback: A callback to indicate connection failure along with a status code.
    * @param handler: The handler context in which to execute the @callback parameters.
+   * @param configuration_option: The configuration options for this channel
    *
    * Returns: true if connection was able to be initiated, false otherwise.
    */
-  bool ConnectChannel(hci::Address device, Psm psm, OnConnectionOpenCallback on_connection_open,
-                      OnConnectionFailureCallback on_fail_callback, os::Handler* handler);
+  bool ConnectChannel(hci::Address device, DynamicChannelConfigurationOption configuration_option, Psm psm,
+                      OnConnectionOpenCallback on_connection_open, OnConnectionFailureCallback on_fail_callback,
+                      os::Handler* handler);
 
   /**
    * Register a service to receive incoming connections bound to a specific channel.
@@ -114,9 +117,10 @@
    *        not SUCCESS, it means service is not registered due to reasons like PSM already take
    * @param on_open_callback: A callback to indicate success of a connection initiated from a remote device.
    * @param handler: The handler context in which to execute the @callback parameter.
+   * @param configuration_option: The configuration options for this channel
    */
-  bool RegisterService(Psm psm, const SecurityPolicy& security_policy,
-                       OnRegistrationCompleteCallback on_registration_complete,
+  bool RegisterService(Psm psm, DynamicChannelConfigurationOption configuration_option,
+                       const SecurityPolicy& security_policy, OnRegistrationCompleteCallback on_registration_complete,
                        OnConnectionOpenCallback on_connection_open, os::Handler* handler);
 
   friend class L2capClassicModule;
diff --git a/gd/l2cap/classic/facade.cc b/gd/l2cap/classic/facade.cc
index f2b2df3..9613c75 100644
--- a/gd/l2cap/classic/facade.cc
+++ b/gd/l2cap/classic/facade.cc
@@ -99,8 +99,9 @@
                              const ::bluetooth::l2cap::classic::OpenChannelRequest* request,
                              ::google::protobuf::Empty* response) override {
     auto psm = request->psm();
+    auto mode = request->mode();
     dynamic_channel_helper_map_.emplace(
-        psm, std::make_unique<L2capDynamicChannelHelper>(this, l2cap_layer_, facade_handler_, psm));
+        psm, std::make_unique<L2capDynamicChannelHelper>(this, l2cap_layer_, facade_handler_, psm, mode));
     hci::Address peer;
     ASSERT(hci::Address::FromString(request->remote().address(), peer));
     dynamic_channel_helper_map_[psm]->Connect(peer);
@@ -183,27 +184,37 @@
 
   ::grpc::Status SetDynamicChannel(::grpc::ServerContext* context, const SetEnableDynamicChannelRequest* request,
                                    google::protobuf::Empty* response) override {
-    dynamic_channel_helper_map_.emplace(request->psm(), std::make_unique<L2capDynamicChannelHelper>(
-                                                            this, l2cap_layer_, facade_handler_, request->psm()));
+    dynamic_channel_helper_map_.emplace(
+        request->psm(), std::make_unique<L2capDynamicChannelHelper>(this, l2cap_layer_, facade_handler_, request->psm(),
+                                                                    request->retransmission_mode()));
     return ::grpc::Status::OK;
   }
 
   class L2capDynamicChannelHelper {
    public:
     L2capDynamicChannelHelper(L2capModuleFacadeService* service, L2capClassicModule* l2cap_layer, os::Handler* handler,
-                              Psm psm)
+                              Psm psm, RetransmissionFlowControlMode mode)
         : facade_service_(service), l2cap_layer_(l2cap_layer), handler_(handler), psm_(psm) {
       dynamic_channel_manager_ = l2cap_layer_->GetDynamicChannelManager();
+      DynamicChannelConfigurationOption configuration_option;
+      if (mode == RetransmissionFlowControlMode::BASIC) {
+        configuration_option.channel_mode =
+            DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::L2CAP_BASIC;
+      } else if (mode == RetransmissionFlowControlMode::ERTM) {
+        configuration_option.channel_mode =
+            DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::ENHANCED_RETRANSMISSION;
+      }
       dynamic_channel_manager_->RegisterService(
-          psm, {},
+          psm, configuration_option, {},
           common::BindOnce(&L2capDynamicChannelHelper::on_l2cap_service_registration_complete,
                            common::Unretained(this)),
           common::Bind(&L2capDynamicChannelHelper::on_connection_open, common::Unretained(this)), handler_);
     }
 
     void Connect(hci::Address address) {
+      // TODO: specify channel mode
       dynamic_channel_manager_->ConnectChannel(
-          address, psm_, common::Bind(&L2capDynamicChannelHelper::on_connection_open, common::Unretained(this)),
+          address, {}, psm_, common::Bind(&L2capDynamicChannelHelper::on_connection_open, common::Unretained(this)),
           common::Bind(&L2capDynamicChannelHelper::on_connect_fail, common::Unretained(this)), handler_);
     }
 
@@ -238,7 +249,7 @@
     }
 
     std::unique_ptr<packet::BasePacketBuilder> enqueue_callback(std::vector<uint8_t> packet) {
-      auto packet_one = std::make_unique<packet::RawBuilder>();
+      auto packet_one = std::make_unique<packet::RawBuilder>(2000);
       packet_one->AddOctets(packet);
       channel_->GetQueueUpEnd()->UnregisterEnqueue();
       return packet_one;
diff --git a/gd/l2cap/classic/facade.proto b/gd/l2cap/classic/facade.proto
index 21add63..7eb780d 100644
--- a/gd/l2cap/classic/facade.proto
+++ b/gd/l2cap/classic/facade.proto
@@ -30,9 +30,15 @@
   facade.BluetoothAddress remote = 1;
 }
 
+enum RetransmissionFlowControlMode {
+  BASIC = 0;
+  ERTM = 3;
+}
+
 message OpenChannelRequest {
   facade.BluetoothAddress remote = 1;
   uint32 psm = 2;
+  RetransmissionFlowControlMode mode = 3;
 }
 
 message ConfigureChannelRequest {
@@ -78,6 +84,7 @@
 message SetEnableDynamicChannelRequest {
   uint32 psm = 1;
   bool enable = 2;
+  RetransmissionFlowControlMode retransmission_mode = 3;
 }
 
 message DynamicChannelPacket {
diff --git a/gd/l2cap/classic/internal/dynamic_channel_impl.cc b/gd/l2cap/classic/internal/dynamic_channel_impl.cc
index bd56187..ba613a6 100644
--- a/gd/l2cap/classic/internal/dynamic_channel_impl.cc
+++ b/gd/l2cap/classic/internal/dynamic_channel_impl.cc
@@ -19,6 +19,7 @@
 #include "l2cap/cid.h"
 #include "l2cap/classic/internal/dynamic_channel_impl.h"
 #include "l2cap/classic/internal/link.h"
+#include "l2cap/internal/sender.h"
 #include "l2cap/psm.h"
 #include "l2cap/security_policy.h"
 #include "os/handler.h"
@@ -97,6 +98,10 @@
   incoming_configuration_status_ = status;
 }
 
+void DynamicChannelImpl::SetSender(l2cap::internal::Sender* sender) {
+  sender_ = sender;
+}
+
 Mtu DynamicChannelImpl::GetIncomingMtu() const {
   return incoming_mtu_;
 }
@@ -105,16 +110,9 @@
   incoming_mtu_ = mtu;
 }
 
-RetransmissionAndFlowControlModeOption DynamicChannelImpl::GetChannelMode() const {
-  return mode_;
-}
-
-void DynamicChannelImpl::SetChannelMode(RetransmissionAndFlowControlModeOption mode) {
-  mode_ = mode;
-}
-
-FcsType DynamicChannelImpl::GetFcsType() const {
-  return fcs_type_;
+void DynamicChannelImpl::SetRetransmissionFlowControlConfig(
+    const RetransmissionAndFlowControlConfigurationOption& option) {
+  sender_->SetChannelRetransmissionFlowControlMode(option.mode_);
 }
 
 void DynamicChannelImpl::SetFcsType(FcsType fcs_type) {
diff --git a/gd/l2cap/classic/internal/dynamic_channel_impl.h b/gd/l2cap/classic/internal/dynamic_channel_impl.h
index ba75814..ac1f02c 100644
--- a/gd/l2cap/classic/internal/dynamic_channel_impl.h
+++ b/gd/l2cap/classic/internal/dynamic_channel_impl.h
@@ -76,13 +76,17 @@
   virtual ConfigurationStatus GetIncomingConfigurationStatus() const;
   virtual void SetIncomingConfigurationStatus(ConfigurationStatus status);
 
+  /**
+   * Callback from the Scheduler to notify the Sender for this channel. On config update, channel might notify the
+   * configuration to Sender
+   */
+  void SetSender(l2cap::internal::Sender* sender) override;
+
   virtual Mtu GetIncomingMtu() const;
   virtual void SetIncomingMtu(Mtu mtu);
 
-  virtual RetransmissionAndFlowControlModeOption GetChannelMode() const;
-  virtual void SetChannelMode(RetransmissionAndFlowControlModeOption mode);
+  virtual void SetRetransmissionFlowControlConfig(const RetransmissionAndFlowControlConfigurationOption& mode);
 
-  virtual FcsType GetFcsType() const;
   virtual void SetFcsType(FcsType fcs_type);
 
   // TODO(cmanton) Do something a little bit better than this
@@ -109,8 +113,8 @@
   ConfigurationStatus outgoing_configuration_status_ = ConfigurationStatus::NOT_CONFIGURED;
   ConfigurationStatus incoming_configuration_status_ = ConfigurationStatus::NOT_CONFIGURED;
 
+  l2cap::internal::Sender* sender_ = nullptr;
   Mtu incoming_mtu_ = kDefaultClassicMtu;
-  RetransmissionAndFlowControlModeOption mode_ = RetransmissionAndFlowControlModeOption::L2CAP_BASIC;
   // TODO: Add all RetransmissionAndFlowControlConfigurationOptions
   FcsType fcs_type_ = FcsType::DEFAULT;
 
diff --git a/gd/l2cap/classic/internal/dynamic_channel_service_impl.h b/gd/l2cap/classic/internal/dynamic_channel_service_impl.h
index 57e5df4..2fcf3fc 100644
--- a/gd/l2cap/classic/internal/dynamic_channel_service_impl.h
+++ b/gd/l2cap/classic/internal/dynamic_channel_service_impl.h
@@ -19,6 +19,7 @@
 #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"
 
@@ -34,23 +35,31 @@
     os::Handler* user_handler_ = nullptr;
     DynamicChannelManager::OnRegistrationCompleteCallback on_registration_complete_callback_;
     DynamicChannelManager::OnConnectionOpenCallback on_connection_open_callback_;
+    DynamicChannelConfigurationOption configuration_;
   };
 
   virtual void NotifyChannelCreation(std::unique_ptr<DynamicChannel> channel) {
     user_handler_->Post(common::BindOnce(on_connection_open_callback_, std::move(channel)));
   }
 
+  DynamicChannelConfigurationOption GetConfigOption() const {
+    return config_option_;
+  }
+
   friend class DynamicChannelServiceManagerImpl;
 
  protected:
   // protected access for mocking
   DynamicChannelServiceImpl(os::Handler* user_handler,
-                            DynamicChannelManager::OnConnectionOpenCallback on_connection_open_callback)
-      : user_handler_(user_handler), on_connection_open_callback_(std::move(on_connection_open_callback)) {}
+                            DynamicChannelManager::OnConnectionOpenCallback on_connection_open_callback,
+                            DynamicChannelConfigurationOption config_option)
+      : user_handler_(user_handler), on_connection_open_callback_(std::move(on_connection_open_callback)),
+        config_option_(config_option) {}
 
  private:
   os::Handler* user_handler_ = nullptr;
   DynamicChannelManager::OnConnectionOpenCallback on_connection_open_callback_;
+  DynamicChannelConfigurationOption config_option_;
 };
 
 }  // namespace internal
diff --git a/gd/l2cap/classic/internal/dynamic_channel_service_manager_impl.cc b/gd/l2cap/classic/internal/dynamic_channel_service_manager_impl.cc
index d40bfae..a44a72e 100644
--- a/gd/l2cap/classic/internal/dynamic_channel_service_manager_impl.cc
+++ b/gd/l2cap/classic/internal/dynamic_channel_service_manager_impl.cc
@@ -40,7 +40,8 @@
   } else {
     service_map_.try_emplace(psm,
                              DynamicChannelServiceImpl(pending_registration.user_handler_,
-                                                       std::move(pending_registration.on_connection_open_callback_)));
+                                                       std::move(pending_registration.on_connection_open_callback_),
+                                                       pending_registration.configuration_));
     std::unique_ptr<DynamicChannelService> user_service(new DynamicChannelService(psm, this, l2cap_layer_handler_));
     pending_registration.user_handler_->Post(
         common::BindOnce(std::move(pending_registration.on_registration_complete_callback_),
diff --git a/gd/l2cap/classic/internal/fixed_channel_impl.cc b/gd/l2cap/classic/internal/fixed_channel_impl.cc
index 9bfaef8..8ac6c94 100644
--- a/gd/l2cap/classic/internal/fixed_channel_impl.cc
+++ b/gd/l2cap/classic/internal/fixed_channel_impl.cc
@@ -94,6 +94,8 @@
   link_->RefreshRefCount();
 }
 
+void FixedChannelImpl::SetSender(l2cap::internal::Sender* sender) {}
+
 }  // namespace internal
 }  // namespace classic
 }  // namespace l2cap
diff --git a/gd/l2cap/classic/internal/fixed_channel_impl.h b/gd/l2cap/classic/internal/fixed_channel_impl.h
index 563a770..6fde5c6 100644
--- a/gd/l2cap/classic/internal/fixed_channel_impl.h
+++ b/gd/l2cap/classic/internal/fixed_channel_impl.h
@@ -74,14 +74,7 @@
   Cid GetRemoteCid() const {
     return cid_;
   }
-
-  RetransmissionAndFlowControlModeOption GetChannelMode() const {
-    return RetransmissionAndFlowControlModeOption::L2CAP_BASIC;
-  }
-
-  void SetChannelMode(RetransmissionAndFlowControlModeOption) {
-    LOG_ERROR("Setting channel mode on a fixed channel cid 0x%02hx", cid_);
-  }
+  void SetSender(l2cap::internal::Sender* sender) override;
 
  private:
   // Constructor states
diff --git a/gd/l2cap/classic/internal/link.cc b/gd/l2cap/classic/internal/link.cc
index 055e77c..7b5178f 100644
--- a/gd/l2cap/classic/internal/link.cc
+++ b/gd/l2cap/classic/internal/link.cc
@@ -110,12 +110,10 @@
   return channel;
 }
 
-void Link::SetChannelRetransmissionFlowControlMode(Cid cid, RetransmissionAndFlowControlModeOption mode) {
-  if (dynamic_channel_allocator_.FindChannelByCid(cid) == nullptr) {
-    LOG_ERROR("Channel doesn't exist: %d", cid);
-    return;
-  }
-  scheduler_->SetChannelRetransmissionFlowControlMode(cid, mode);
+classic::DynamicChannelConfigurationOption Link::GetConfigurationForInitialConfiguration(Cid cid) {
+  ASSERT(local_cid_to_pending_dynamic_channel_connection_map_.find(cid) !=
+         local_cid_to_pending_dynamic_channel_connection_map_.end());
+  return local_cid_to_pending_dynamic_channel_connection_map_[cid].configuration_;
 }
 
 void Link::FreeDynamicChannel(Cid cid) {
@@ -139,12 +137,12 @@
   }
 }
 
-void Link::NotifyChannelCreation(Cid cid, std::unique_ptr<DynamicChannel> channel) {
+void Link::NotifyChannelCreation(Cid cid, std::unique_ptr<DynamicChannel> user_channel) {
   ASSERT(local_cid_to_pending_dynamic_channel_connection_map_.find(cid) !=
          local_cid_to_pending_dynamic_channel_connection_map_.end());
   auto& pending_dynamic_channel_connection = local_cid_to_pending_dynamic_channel_connection_map_[cid];
   pending_dynamic_channel_connection.handler_->Post(
-      common::BindOnce(std::move(pending_dynamic_channel_connection.on_open_callback_), std::move(channel)));
+      common::BindOnce(std::move(pending_dynamic_channel_connection.on_open_callback_), std::move(user_channel)));
   local_cid_to_pending_dynamic_channel_connection_map_.erase(cid);
 }
 
@@ -159,6 +157,30 @@
   local_cid_to_pending_dynamic_channel_connection_map_.erase(cid);
 }
 
+void Link::SetRemoteConnectionlessMtu(Mtu mtu) {
+  remote_mtu_ = mtu;
+}
+
+Mtu Link::GetRemoteConnectionlessMtu() const {
+  return remote_mtu_;
+}
+
+void Link::SetRemoteSupportsErtm(bool supported) {
+  remote_supports_ertm_ = supported;
+}
+
+bool Link::GetRemoteSupportsErtm() const {
+  return remote_supports_ertm_;
+}
+
+void Link::SetRemoteSupportsFcs(bool supported) {
+  remote_supports_fcs_ = supported;
+}
+
+bool Link::GetRemoteSupportsFcs() const {
+  return remote_supports_fcs_;
+}
+
 }  // namespace internal
 }  // namespace classic
 }  // namespace l2cap
diff --git a/gd/l2cap/classic/internal/link.h b/gd/l2cap/classic/internal/link.h
index f192e3e..2c6c1b8 100644
--- a/gd/l2cap/classic/internal/link.h
+++ b/gd/l2cap/classic/internal/link.h
@@ -20,6 +20,7 @@
 #include <unordered_map>
 
 #include "hci/acl_manager.h"
+#include "l2cap/classic/dynamic_channel_configuration_option.h"
 #include "l2cap/classic/internal/dynamic_channel_allocator.h"
 #include "l2cap/classic/internal/dynamic_channel_impl.h"
 #include "l2cap/classic/internal/dynamic_channel_service_manager_impl.h"
@@ -55,6 +56,7 @@
     os::Handler* handler_;
     DynamicChannelManager::OnConnectionOpenCallback on_open_callback_;
     DynamicChannelManager::OnConnectionFailureCallback on_fail_callback_;
+    classic::DynamicChannelConfigurationOption configuration_;
   };
 
   // ACL methods
@@ -87,7 +89,7 @@
   virtual std::shared_ptr<DynamicChannelImpl> AllocateReservedDynamicChannel(Cid reserved_cid, Psm psm, Cid remote_cid,
                                                                              SecurityPolicy security_policy);
 
-  virtual void SetChannelRetransmissionFlowControlMode(Cid cid, RetransmissionAndFlowControlModeOption mode);
+  virtual classic::DynamicChannelConfigurationOption GetConfigurationForInitialConfiguration(Cid cid);
 
   virtual void FreeDynamicChannel(Cid cid);
 
@@ -97,6 +99,14 @@
   virtual void NotifyChannelCreation(Cid cid, std::unique_ptr<DynamicChannel> channel);
   virtual void NotifyChannelFail(Cid cid);
 
+  // Information received from signalling channel
+  virtual void SetRemoteConnectionlessMtu(Mtu mtu);
+  virtual Mtu GetRemoteConnectionlessMtu() const;
+  virtual void SetRemoteSupportsErtm(bool supported);
+  virtual bool GetRemoteSupportsErtm() const;
+  virtual void SetRemoteSupportsFcs(bool supported);
+  virtual bool GetRemoteSupportsFcs() const;
+
  private:
   os::Handler* l2cap_handler_;
   l2cap::internal::FixedChannelAllocator<FixedChannelImpl, Link> fixed_channel_allocator_{this, l2cap_handler_};
@@ -110,6 +120,9 @@
   ClassicSignallingManager signalling_manager_;
   std::unordered_map<Cid, PendingDynamicChannelConnection> local_cid_to_pending_dynamic_channel_connection_map_;
   os::Alarm link_idle_disconnect_alarm_{l2cap_handler_};
+  Mtu remote_mtu_ = kMinimumClassicMtu;
+  bool remote_supports_ertm_ = false;
+  bool remote_supports_fcs_ = false;
   DISALLOW_COPY_AND_ASSIGN(Link);
 };
 
diff --git a/gd/l2cap/classic/internal/signalling_manager.cc b/gd/l2cap/classic/internal/signalling_manager.cc
index 147a571..66a3f51 100644
--- a/gd/l2cap/classic/internal/signalling_manager.cc
+++ b/gd/l2cap/classic/internal/signalling_manager.cc
@@ -147,7 +147,39 @@
   }
   send_connection_response(signal_id, remote_cid, new_channel->GetCid(), ConnectionResponseResult::SUCCESS,
                            ConnectionResponseStatus::NO_FURTHER_INFORMATION_AVAILABLE);
-  SendConfigurationRequest(remote_cid, {});
+  auto* service = dynamic_service_manager_->GetService(psm);
+  auto initial_config = service->GetConfigOption();
+  if (!link_->GetRemoteSupportsErtm()) {
+    initial_config.channel_mode = DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::L2CAP_BASIC;
+  }
+  auto mtu_configuration = std::make_unique<MtuConfigurationOption>();
+  mtu_configuration->mtu_ = initial_config.incoming_mtu;
+  auto fcs_option = std::make_unique<FrameCheckSequenceOption>();
+  fcs_option->fcs_type_ = FcsType::NO_FCS;
+  auto retransmission_flow_control_configuration = std::make_unique<RetransmissionAndFlowControlConfigurationOption>();
+  switch (initial_config.channel_mode) {
+    case DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::L2CAP_BASIC:
+      retransmission_flow_control_configuration->mode_ = RetransmissionAndFlowControlModeOption::L2CAP_BASIC;
+      break;
+    case DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::ENHANCED_RETRANSMISSION:
+      retransmission_flow_control_configuration->mode_ =
+          RetransmissionAndFlowControlModeOption::ENHANCED_RETRANSMISSION;
+      // TODO: Decide where to put initial values
+      retransmission_flow_control_configuration->tx_window_size_ = 10;
+      retransmission_flow_control_configuration->max_transmit_ = 20;
+      retransmission_flow_control_configuration->retransmission_time_out_ = 2000;
+      retransmission_flow_control_configuration->monitor_time_out_ = 12000;
+      retransmission_flow_control_configuration->maximum_pdu_size_ = 1010;
+      break;
+  }
+
+  new_channel->SetRetransmissionFlowControlConfig(*retransmission_flow_control_configuration);
+  new_channel->SetIncomingMtu(initial_config.incoming_mtu);
+  std::vector<std::unique_ptr<ConfigurationOption>> config;
+  config.emplace_back(std::move(mtu_configuration));
+  config.emplace_back(std::move(retransmission_flow_control_configuration));
+  config.emplace_back(std::move(fcs_option));
+  SendConfigurationRequest(remote_cid, std::move(config));
 }
 
 void ClassicSignallingManager::OnConnectionResponse(SignalId signal_id, Cid remote_cid, Cid cid,
@@ -179,6 +211,33 @@
     return;
   }
   alarm_.Cancel();
+  auto initial_config = link_->GetConfigurationForInitialConfiguration(new_channel->GetCid());
+  if (!link_->GetRemoteSupportsErtm()) {
+    initial_config.channel_mode = DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::L2CAP_BASIC;
+  }
+  auto mtu_configuration = std::make_unique<MtuConfigurationOption>();
+  mtu_configuration->mtu_ = initial_config.incoming_mtu;
+  auto retransmission_flow_control_configuration = std::make_unique<RetransmissionAndFlowControlConfigurationOption>();
+  switch (initial_config.channel_mode) {
+    case DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::L2CAP_BASIC:
+      retransmission_flow_control_configuration->mode_ = RetransmissionAndFlowControlModeOption::L2CAP_BASIC;
+      break;
+    case DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::ENHANCED_RETRANSMISSION:
+      retransmission_flow_control_configuration->mode_ =
+          RetransmissionAndFlowControlModeOption::ENHANCED_RETRANSMISSION;
+      // TODO: Decide where to put initial values
+      retransmission_flow_control_configuration->tx_window_size_ = 10;
+      retransmission_flow_control_configuration->max_transmit_ = 20;
+      retransmission_flow_control_configuration->retransmission_time_out_ = 2000;
+      retransmission_flow_control_configuration->monitor_time_out_ = 12000;
+      retransmission_flow_control_configuration->maximum_pdu_size_ = 1010;
+      break;
+  }
+  std::vector<std::unique_ptr<ConfigurationOption>> config;
+  config.emplace_back(std::move(mtu_configuration));
+  if (initial_config.channel_mode != DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::L2CAP_BASIC) {
+    config.emplace_back(std::move(retransmission_flow_control_configuration));
+  }
   SendConfigurationRequest(remote_cid, {});
 }
 
@@ -202,7 +261,7 @@
       }
       case ConfigurationOptionType::RETRANSMISSION_AND_FLOW_CONTROL: {
         auto config = RetransmissionAndFlowControlConfigurationOption::Specialize(option.get());
-        channel->SetChannelMode(config->mode_);
+        channel->SetRetransmissionFlowControlConfig(*config);
         break;
       }
       case ConfigurationOptionType::FRAME_CHECK_SEQUENCE: {
@@ -235,7 +294,7 @@
 
 void ClassicSignallingManager::OnConfigurationResponse(SignalId signal_id, Cid cid, Continuation is_continuation,
                                                        ConfigurationResponseResult result,
-                                                       std::vector<std::unique_ptr<ConfigurationOption>> option) {
+                                                       std::vector<std::unique_ptr<ConfigurationOption>> options) {
   if (pending_commands_.empty()) {
     LOG_WARN("Unexpected response: no pending request");
     return;
@@ -250,6 +309,8 @@
     handle_send_next_command();
     return;
   }
+
+  RetransmissionAndFlowControlConfigurationOption rfc_option;
   channel->SetOutgoingConfigurationStatus(DynamicChannelImpl::ConfigurationStatus::CONFIGURED);
   if (channel->GetIncomingConfigurationStatus() == DynamicChannelImpl::ConfigurationStatus::CONFIGURED) {
     std::unique_ptr<DynamicChannel> user_channel = std::make_unique<DynamicChannel>(channel, handler_);
@@ -331,15 +392,15 @@
 void ClassicSignallingManager::OnInformationRequest(SignalId signal_id, InformationRequestInfoType type) {
   switch (type) {
     case InformationRequestInfoType::CONNECTIONLESS_MTU: {
-      auto response = InformationResponseConnectionlessMtuBuilder::Create(signal_id.Value(),
-                                                                          InformationRequestResult::NOT_SUPPORTED, 0);
+      auto response = InformationResponseConnectionlessMtuBuilder::Create(
+          signal_id.Value(), InformationRequestResult::SUCCESS, kDefaultClassicMtu);
       enqueue_buffer_->Enqueue(std::move(response), handler_);
       break;
     }
     case InformationRequestInfoType::EXTENDED_FEATURES_SUPPORTED: {
       // TODO: implement this response
       auto response = InformationResponseExtendedFeaturesBuilder::Create(
-          signal_id.Value(), InformationRequestResult::NOT_SUPPORTED, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+          signal_id.Value(), InformationRequestResult::SUCCESS, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0);
       enqueue_buffer_->Enqueue(std::move(response), handler_);
       break;
     }
@@ -352,7 +413,7 @@
   }
 }
 
-void ClassicSignallingManager::OnInformationResponse(SignalId signal_id, const InformationResponseView& view) {
+void ClassicSignallingManager::OnInformationResponse(SignalId signal_id, const InformationResponseView& response) {
   if (pending_commands_.empty()) {
     LOG_WARN("Unexpected response: no pending request");
     return;
@@ -364,7 +425,40 @@
       last_sent_command.command_code_ != CommandCode::INFORMATION_REQUEST) {
     return;
   }
-  // TODO (hsz): Store the information response
+
+  auto type = response.GetInfoType();
+  switch (type) {
+    case InformationRequestInfoType::CONNECTIONLESS_MTU: {
+      auto view = InformationResponseConnectionlessMtuView::Create(response);
+      if (!view.IsValid()) {
+        LOG_WARN("Invalid InformationResponseConnectionlessMtu received");
+        return;
+      }
+      link_->SetRemoteConnectionlessMtu(view.GetConnectionlessMtu());
+      break;
+    }
+    case InformationRequestInfoType::EXTENDED_FEATURES_SUPPORTED: {
+      auto view = InformationResponseExtendedFeaturesView::Create(response);
+      if (!view.IsValid()) {
+        LOG_WARN("Invalid InformationResponseExtendedFeatures received");
+        return;
+      }
+      link_->SetRemoteSupportsErtm((view.GetEnhancedRetransmissionMode()));
+      link_->SetRemoteSupportsFcs(view.GetFcsOption());
+      // We don't care about other parameters
+      break;
+    }
+    case InformationRequestInfoType::FIXED_CHANNELS_SUPPORTED: {
+      auto view = InformationResponseFixedChannelsView::Create(response);
+      if (!view.IsValid()) {
+        LOG_WARN("Invalid InformationResponseFixedChannel received");
+        return;
+      }
+      // We don't use fixed channels (connectionless or BR/EDR security) for now so we don't care
+      break;
+    }
+  }
+
   alarm_.Cancel();
   handle_send_next_command();
 }
diff --git a/gd/l2cap/classic/internal/signalling_manager.h b/gd/l2cap/classic/internal/signalling_manager.h
index 0ec6a28..0e17ec7 100644
--- a/gd/l2cap/classic/internal/signalling_manager.h
+++ b/gd/l2cap/classic/internal/signalling_manager.h
@@ -92,7 +92,7 @@
 
   void OnInformationRequest(SignalId signal_id, InformationRequestInfoType type);
 
-  void OnInformationResponse(SignalId signal_id, const InformationResponseView& view);
+  void OnInformationResponse(SignalId signal_id, const InformationResponseView& response);
 
  private:
   void on_incoming_packet();
diff --git a/gd/l2cap/internal/channel_impl.h b/gd/l2cap/internal/channel_impl.h
index 92f2bbb..ae17660 100644
--- a/gd/l2cap/internal/channel_impl.h
+++ b/gd/l2cap/internal/channel_impl.h
@@ -23,6 +23,7 @@
 namespace bluetooth {
 namespace l2cap {
 namespace internal {
+class Sender;
 
 /**
  * Common interface for internal channel implementation
@@ -48,15 +49,11 @@
   virtual Cid GetRemoteCid() const = 0;
 
   /**
-   * Return one of the supported channel mode as defined above
+   * Callback from the Scheduler to notify the Sender for this channel. On config update, channel might notify the
+   * configuration change to Sender.
+   * Fixed channel doesn't need to implement it, as it doesn't need to send config update to Sender.
    */
-  virtual RetransmissionAndFlowControlModeOption GetChannelMode() const = 0;
-
-  /**
-   * Invoked by the command signalling manager to update the channel mode. Does NOT apply to fixed channel, OR LE
-   * credit-based flow control channel
-   */
-  virtual void SetChannelMode(RetransmissionAndFlowControlModeOption) = 0;
+  virtual void SetSender(l2cap::internal::Sender* sender) = 0;
 };
 
 }  // namespace internal
diff --git a/gd/l2cap/internal/channel_impl_mock.h b/gd/l2cap/internal/channel_impl_mock.h
index 0fe61e5..710e69a 100644
--- a/gd/l2cap/internal/channel_impl_mock.h
+++ b/gd/l2cap/internal/channel_impl_mock.h
@@ -34,8 +34,7 @@
               GetQueueDownEnd, (), (override));
   MOCK_METHOD(Cid, GetCid, (), (const, override));
   MOCK_METHOD(Cid, GetRemoteCid, (), (const, override));
-  MOCK_METHOD(RetransmissionAndFlowControlModeOption, GetChannelMode, (), (const, override));
-  MOCK_METHOD(void, SetChannelMode, (RetransmissionAndFlowControlModeOption), (override));
+  MOCK_METHOD(void, SetSender, (l2cap::internal::Sender*), (override));
 };
 
 }  // namespace testing
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 ca4ca1f..c31ad85 100644
--- a/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.cc
+++ b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.cc
@@ -76,8 +76,10 @@
   bool local_busy_ = false;
   int unacked_frames_ = 0;
   // TODO: Instead of having a map, we may consider about a better data structure
-  std::map<uint8_t, std::pair<SegmentationAndReassembly, CopyablePacketBuilder>> unacked_list_;
-  std::queue<std::pair<SegmentationAndReassembly, std::unique_ptr<packet::BasePacketBuilder>>> pending_frames_;
+  // Map from TxSeq to (SAR, SDU size for START packet, information payload)
+  std::map<uint8_t, std::tuple<SegmentationAndReassembly, uint16_t, std::shared_ptr<packet::RawBuilder>>> unacked_list_;
+  // Stores (SAR, SDU size for START packet, information payload)
+  std::queue<std::tuple<SegmentationAndReassembly, uint16_t, std::unique_ptr<packet::RawBuilder>>> pending_frames_;
   int retry_count_ = 0;
   std::map<uint8_t /* tx_seq, */, int /* count */> retry_i_frames_;
   bool rnr_sent_ = false;
@@ -92,13 +94,14 @@
 
   // Events (@see 8.6.5.4)
 
-  void data_request(SegmentationAndReassembly sar, std::unique_ptr<packet::BasePacketBuilder> pdu) {
+  void data_request(SegmentationAndReassembly sar, std::unique_ptr<packet::RawBuilder> pdu, uint16_t sdu_size = 0) {
+    // Note: sdu_size only applies to START packet
     if (tx_state_ == TxState::XMIT && !remote_busy() && rem_window_not_full()) {
-      send_data(sar, std::move(pdu));
+      send_data(sar, sdu_size, std::move(pdu));
     } else if (tx_state_ == TxState::XMIT && (remote_busy() || rem_window_full())) {
-      pend_data(sar, std::move(pdu));
+      pend_data(sar, sdu_size, std::move(pdu));
     } else if (tx_state_ == TxState::WAIT_F) {
-      pend_data(sar, std::move(pdu));
+      pend_data(sar, sdu_size, std::move(pdu));
     }
   }
 
@@ -550,18 +553,28 @@
 
   // Actions (@see 8.6.5.6)
 
-  void _send_i_frame(SegmentationAndReassembly sar, std::unique_ptr<packet::BasePacketBuilder> segment, uint8_t req_seq,
-                     uint8_t tx_seq, Final f = Final::NOT_SET) {
-    auto builder =
-        ExtendedInformationFrameBuilder::Create(controller_->remote_cid_, f, req_seq, sar, tx_seq, std::move(segment));
+  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;
+    if (sar == SegmentationAndReassembly::START) {
+      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));
+    }
     controller_->send_pdu(std::move(builder));
   }
 
-  void send_data(SegmentationAndReassembly sar, std::unique_ptr<packet::BasePacketBuilder> segment,
+  void send_data(SegmentationAndReassembly sar, uint16_t sdu_size, std::unique_ptr<packet::RawBuilder> segment,
                  Final f = Final::NOT_SET) {
+    std::shared_ptr<packet::RawBuilder> shared_segment(segment.release());
     unacked_list_.emplace(std::piecewise_construct, std::forward_as_tuple(next_tx_seq_),
-                          std::forward_as_tuple(sar, std::move(segment)));
-    _send_i_frame(sar, unacked_list_.find(next_tx_seq_)->second.second.Create(), buffer_seq_, next_tx_seq_, f);
+                          std::forward_as_tuple(sar, sdu_size, shared_segment));
+
+    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_++;
@@ -570,8 +583,8 @@
     start_retrans_timer();
   }
 
-  void pend_data(SegmentationAndReassembly sar, std::unique_ptr<packet::BasePacketBuilder> data) {
-    pending_frames_.emplace(std::make_pair(sar, std::move(data)));
+  void pend_data(SegmentationAndReassembly sar, uint16_t sdu_size, std::unique_ptr<packet::RawBuilder> data) {
+    pending_frames_.emplace(std::make_tuple(sar, sdu_size, std::move(data)));
   }
 
   void process_req_seq(uint8_t req_seq) {
@@ -702,8 +715,10 @@
     uint8_t i = req_seq;
     Final f = (p == Poll::NOT_SET ? Final::NOT_SET : Final::POLL_RESPONSE);
     while (unacked_list_.find(i) == unacked_list_.end()) {
-      _send_i_frame(unacked_list_.find(i)->second.first, unacked_list_.find(i)->second.second.Create(), buffer_seq_, i,
-                    f);
+      std::unique_ptr<CopyablePacketBuilder> copyable_packet_builder =
+          std::make_unique<CopyablePacketBuilder>(std::get<2>(unacked_list_.find(i)->second));
+      _send_i_frame(std::get<0>(unacked_list_.find(i)->second), std::move(copyable_packet_builder), buffer_seq_, i,
+                    std::get<1>(unacked_list_.find(i)->second), f);
       retry_i_frames_[i]++;
       if (retry_i_frames_[i] == controller_->local_max_transmit_) {
         CloseChannel();
@@ -720,8 +735,10 @@
       LOG_ERROR("Received invalid SREJ");
       return;
     }
-    _send_i_frame(unacked_list_.find(req_seq)->second.first, unacked_list_.find(req_seq)->second.second.Create(),
-                  buffer_seq_, req_seq, f);
+    std::unique_ptr<CopyablePacketBuilder> copyable_packet_builder =
+        std::make_unique<CopyablePacketBuilder>(std::get<2>(unacked_list_.find(req_seq)->second));
+    _send_i_frame(std::get<0>(unacked_list_.find(req_seq)->second), std::move(copyable_packet_builder), buffer_seq_,
+                  req_seq, std::get<1>(unacked_list_.find(req_seq)->second), f);
     retry_i_frames_[req_seq]++;
     start_retrans_timer();
   }
@@ -732,7 +749,7 @@
     }
     while (rem_window_not_full() && !pending_frames_.empty()) {
       auto& frame = pending_frames_.front();
-      send_data(frame.first, std::move(frame.second), f);
+      send_data(std::get<0>(frame), std::get<1>(frame), std::move(std::get<2>(frame)), f);
       pending_frames_.pop();
       f = Final::NOT_SET;
     }
@@ -753,25 +770,24 @@
 
 // Segmentation is handled here
 void ErtmController::OnSdu(std::unique_ptr<packet::BasePacketBuilder> sdu) {
-  LOG_ERROR("Not implemented");
   // 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.
-  auto size_each_packet =
+  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));
   sdu->Serialize(fragmenting_inserter);
+  fragmenting_inserter.finalize();
   if (segments.size() == 1) {
-    pimpl_->data_request(SegmentationAndReassembly::UNSEGMENTED, std::move(sdu));
+    pimpl_->data_request(SegmentationAndReassembly::UNSEGMENTED, std::move(segments[0]));
     return;
   }
-  auto sar = SegmentationAndReassembly::START;
-  for (auto i = 0; i < segments.size() - 1; i++) {
-    pimpl_->data_request(sar, std::move(segments[i]));
-    sar = SegmentationAndReassembly::CONTINUATION;
+  pimpl_->data_request(SegmentationAndReassembly::START, std::move(segments[0]), sdu_size);
+  for (auto i = 1; i < segments.size() - 1; i++) {
+    pimpl_->data_request(SegmentationAndReassembly::CONTINUATION, std::move(segments[i]));
   }
-  sar = SegmentationAndReassembly::END;
-  pimpl_->data_request(sar, std::move(segments.back()));
+  pimpl_->data_request(SegmentationAndReassembly::END, std::move(segments.back()));
 }
 
 void ErtmController::OnPdu(BasicFrameView pdu) {
@@ -876,10 +892,6 @@
   builder_->Serialize(it);
 }
 
-std::unique_ptr<BasePacketBuilder> ErtmController::CopyablePacketBuilder::Create() {
-  return std::unique_ptr<packet::BasePacketBuilder>(builder_.get());
-}
-
 }  // namespace internal
 }  // namespace l2cap
 }  // namespace bluetooth
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 1e36f49..0566e0b 100644
--- a/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.h
+++ b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.h
@@ -31,6 +31,7 @@
 #include "os/queue.h"
 #include "packet/base_packet_builder.h"
 #include "packet/packet_view.h"
+#include "packet/raw_builder.h"
 
 namespace bluetooth {
 namespace l2cap {
@@ -62,7 +63,6 @@
   class PacketViewForReassembly : public packet::PacketView<kLittleEndian> {
    public:
     PacketViewForReassembly(const PacketView& packetView) : PacketView(packetView) {}
-    PacketViewForReassembly(nullptr_t) : PacketView(nullptr) {}
     void AppendPacketView(packet::PacketView<kLittleEndian> to_append) {
       Append(to_append);
     }
@@ -70,19 +70,17 @@
 
   class CopyablePacketBuilder : public packet::BasePacketBuilder {
    public:
-    CopyablePacketBuilder(std::unique_ptr<packet::BasePacketBuilder> builder) : builder_(builder.release()) {}
+    CopyablePacketBuilder(std::shared_ptr<packet::RawBuilder> builder) : builder_(std::move(builder)) {}
 
     void Serialize(BitInserter& it) const override;
 
     size_t size() const override;
 
-    std::unique_ptr<packet::BasePacketBuilder> Create();
-
    private:
-    std::shared_ptr<packet::BasePacketBuilder> builder_;
+    std::shared_ptr<packet::RawBuilder> builder_;
   };
 
-  PacketViewForReassembly reassembly_stage_{nullptr};
+  PacketViewForReassembly reassembly_stage_{std::make_shared<std::vector<uint8_t>>()};
   SegmentationAndReassembly sar_state_ = SegmentationAndReassembly::END;
 
   void stage_for_reassembly(SegmentationAndReassembly sar, const packet::PacketView<kLittleEndian>& payload);
@@ -92,17 +90,13 @@
 
   // Configuration options
   // TODO: Configure these number
-  [[maybe_unused]] uint16_t local_tx_window_ = 10;
-  [[maybe_unused]] uint16_t local_max_transmit_ = 20;
-  [[maybe_unused]] uint16_t local_retransmit_timeout_ms_ = 2000;
-  [[maybe_unused]] uint16_t local_monitor_timeout_ms_ = 12000;
-  [[maybe_unused]] uint16_t local_mps_ = 1010;
+  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;
 
-  [[maybe_unused]] uint16_t remote_tx_window_ = 10;
-  [[maybe_unused]] uint16_t remote_max_transmit_ = 20;
-  [[maybe_unused]] uint16_t remote_retransmit_timeout_ms_ = 2000;
-  [[maybe_unused]] uint16_t remote_monitor_timeout_ms_ = 12000;
-  [[maybe_unused]] uint16_t remote_mps_ = 1010;
+  uint16_t remote_tx_window_ = 10;
+  uint16_t remote_mps_ = 1010;
 
   struct impl;
   std::unique_ptr<impl> pimpl_;
diff --git a/gd/l2cap/internal/scheduler.h b/gd/l2cap/internal/scheduler.h
index d400aca..59f2bfd 100644
--- a/gd/l2cap/internal/scheduler.h
+++ b/gd/l2cap/internal/scheduler.h
@@ -20,6 +20,7 @@
 
 #include "common/bidi_queue.h"
 #include "l2cap/cid.h"
+#include "l2cap/classic/dynamic_channel_configuration_option.h"
 #include "l2cap/internal/channel_impl.h"
 #include "l2cap/internal/data_controller.h"
 #include "l2cap/internal/sender.h"
@@ -50,6 +51,7 @@
 
   /**
    * Attach the channel with the specified ChannelQueueDownEnd into the scheduler.
+   * Scheduler needs to notify the channel its Sender through SetSender().
    *
    * @param cid The channel to attach to the scheduler.
    * @param channel The reference to a DynamicChannelImpl object. Use nullptr for fixed channel.
@@ -65,16 +67,11 @@
   virtual void DetachChannel(Cid cid) {}
 
   /**
-   * Callback from the segmenter to indicate that the scheduler could dequeue number_packets from it
+   * Callback from the sender to indicate that the scheduler could dequeue number_packets from it
    */
   virtual void OnPacketsReady(Cid cid, int number_packets) {}
 
   /**
-   * Set the channel mode for a cid
-   */
-  virtual void SetChannelRetransmissionFlowControlMode(Cid cid, RetransmissionAndFlowControlModeOption mode) {}
-
-  /**
    * Get the data controller for Reassembler
    */
   virtual DataController* GetDataController(Cid cid) {
diff --git a/gd/l2cap/internal/scheduler_fifo.cc b/gd/l2cap/internal/scheduler_fifo.cc
index f60f38f..7454cbe 100644
--- a/gd/l2cap/internal/scheduler_fifo.cc
+++ b/gd/l2cap/internal/scheduler_fifo.cc
@@ -30,21 +30,24 @@
 }
 
 Fifo::~Fifo() {
-  segmenter_map_.clear();
+  sender_map_.clear();
   if (link_queue_enqueue_registered_) {
     link_queue_up_end_->UnregisterEnqueue();
   }
 }
 
 void Fifo::AttachChannel(Cid cid, std::shared_ptr<ChannelImpl> channel) {
-  ASSERT(segmenter_map_.find(cid) == segmenter_map_.end());
-  segmenter_map_.emplace(std::piecewise_construct, std::forward_as_tuple(cid),
-                         std::forward_as_tuple(handler_, this, 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_, this, channel));
+  if (channel->GetCid() >= kFirstDynamicChannel) {
+    channel->SetSender(&sender_map_.find(cid)->second);
+  }
 }
 
 void Fifo::DetachChannel(Cid cid) {
-  ASSERT(segmenter_map_.find(cid) != segmenter_map_.end());
-  segmenter_map_.erase(cid);
+  ASSERT(sender_map_.find(cid) != sender_map_.end());
+  sender_map_.erase(cid);
 }
 
 void Fifo::OnPacketsReady(Cid cid, int number_packets) {
@@ -60,9 +63,9 @@
   if (channel_id_and_number_packets.second == 0) {
     next_to_dequeue_and_num_packets.pop();
   }
-  auto packet = segmenter_map_.find(channel_id)->second.GetNextPacket();
+  auto packet = sender_map_.find(channel_id)->second.GetNextPacket();
 
-  segmenter_map_.find(channel_id)->second.OnPacketSent();
+  sender_map_.find(channel_id)->second.OnPacketSent();
   if (next_to_dequeue_and_num_packets.empty()) {
     link_queue_up_end_->UnregisterEnqueue();
     link_queue_enqueue_registered_ = false;
@@ -79,16 +82,11 @@
   link_queue_enqueue_registered_ = true;
 }
 
-void Fifo::SetChannelRetransmissionFlowControlMode(Cid cid, RetransmissionAndFlowControlModeOption mode) {
-  ASSERT(segmenter_map_.find(cid) != segmenter_map_.end());
-  segmenter_map_.find(cid)->second.SetChannelRetransmissionFlowControlMode(mode);
-}
-
 DataController* Fifo::GetDataController(Cid cid) {
-  if (segmenter_map_.find(cid) == segmenter_map_.end()) {
+  if (sender_map_.find(cid) == sender_map_.end()) {
     return nullptr;
   }
-  return segmenter_map_.find(cid)->second.GetDataController();
+  return sender_map_.find(cid)->second.GetDataController();
 }
 
 }  // namespace internal
diff --git a/gd/l2cap/internal/scheduler_fifo.h b/gd/l2cap/internal/scheduler_fifo.h
index 6be3a09..b20ea4f 100644
--- a/gd/l2cap/internal/scheduler_fifo.h
+++ b/gd/l2cap/internal/scheduler_fifo.h
@@ -39,13 +39,12 @@
   void AttachChannel(Cid cid, std::shared_ptr<ChannelImpl> channel) override;
   void DetachChannel(Cid cid) override;
   void OnPacketsReady(Cid cid, int number_packets) override;
-  void SetChannelRetransmissionFlowControlMode(Cid cid, RetransmissionAndFlowControlModeOption mode) override;
   DataController* GetDataController(Cid cid) override;
 
  private:
   LowerQueueUpEnd* link_queue_up_end_;
   os::Handler* handler_;
-  std::unordered_map<Cid, Sender> segmenter_map_;
+  std::unordered_map<Cid, Sender> sender_map_;
   std::queue<std::pair<Cid, int>> next_to_dequeue_and_num_packets;
 
   bool link_queue_enqueue_registered_ = false;
diff --git a/gd/l2cap/internal/scheduler_fifo_test.cc b/gd/l2cap/internal/scheduler_fifo_test.cc
index 3a53a36..ff78838 100644
--- a/gd/l2cap/internal/scheduler_fifo_test.cc
+++ b/gd/l2cap/internal/scheduler_fifo_test.cc
@@ -72,14 +72,10 @@
 
   auto mock_channel_1 = std::make_shared<testing::MockChannelImpl>();
   EXPECT_CALL(*mock_channel_1, GetQueueDownEnd()).WillRepeatedly(Return(channel_one_queue_.GetDownEnd()));
-  EXPECT_CALL(*mock_channel_1, GetChannelMode())
-      .WillRepeatedly(Return(RetransmissionAndFlowControlModeOption::L2CAP_BASIC));
   EXPECT_CALL(*mock_channel_1, GetCid()).WillRepeatedly(Return(1));
   EXPECT_CALL(*mock_channel_1, GetRemoteCid()).WillRepeatedly(Return(1));
   auto mock_channel_2 = std::make_shared<testing::MockChannelImpl>();
   EXPECT_CALL(*mock_channel_2, GetQueueDownEnd()).WillRepeatedly(Return(channel_two_queue_.GetDownEnd()));
-  EXPECT_CALL(*mock_channel_2, GetChannelMode())
-      .WillRepeatedly(Return(RetransmissionAndFlowControlModeOption::L2CAP_BASIC));
   EXPECT_CALL(*mock_channel_2, GetCid()).WillRepeatedly(Return(2));
   EXPECT_CALL(*mock_channel_2, GetRemoteCid()).WillRepeatedly(Return(2));
   fifo_->AttachChannel(1, mock_channel_1);
diff --git a/gd/l2cap/internal/sender.cc b/gd/l2cap/internal/sender.cc
index 5296eff..ffe2755 100644
--- a/gd/l2cap/internal/sender.cc
+++ b/gd/l2cap/internal/sender.cc
@@ -59,7 +59,7 @@
   if (mode_ == mode) {
     return;
   }
-  if (mode_ == RetransmissionAndFlowControlModeOption::L2CAP_BASIC) {
+  if (mode == RetransmissionAndFlowControlModeOption::L2CAP_BASIC) {
     data_controller_ =
         std::make_unique<BasicModeDataController>(channel_id_, remote_channel_id_, queue_end_, handler_, scheduler_);
     return;
@@ -71,6 +71,10 @@
   }
 }
 
+void Sender::SetIncomingMtu(Mtu mtu) {
+  // TODO: Enforce MTU
+}
+
 DataController* Sender::GetDataController() {
   return data_controller_.get();
 }
diff --git a/gd/l2cap/internal/sender.h b/gd/l2cap/internal/sender.h
index 97d509c..22f7737 100644
--- a/gd/l2cap/internal/sender.h
+++ b/gd/l2cap/internal/sender.h
@@ -25,6 +25,7 @@
 #include "l2cap/cid.h"
 #include "l2cap/internal/channel_impl.h"
 #include "l2cap/internal/data_controller.h"
+#include "l2cap/mtu.h"
 #include "os/handler.h"
 #include "os/queue.h"
 #include "packet/base_packet_builder.h"
@@ -49,7 +50,7 @@
   ~Sender();
 
   /**
-   * Callback from scheduler to indicate that scheduler already dequeued a packet from segmenter's queue.
+   * Callback from scheduler to indicate that scheduler already dequeued a packet from sender's queue.
    * Segmenter can continue dequeuing from channel queue end.
    */
   void OnPacketSent();
@@ -61,6 +62,8 @@
 
   void SetChannelRetransmissionFlowControlMode(RetransmissionAndFlowControlModeOption mode);
 
+  void SetIncomingMtu(Mtu mtu);
+
   DataController* GetDataController();
 
  private:
diff --git a/gd/l2cap/internal/sender_test.cc b/gd/l2cap/internal/sender_test.cc
index 8f67813..cb16245 100644
--- a/gd/l2cap/internal/sender_test.cc
+++ b/gd/l2cap/internal/sender_test.cc
@@ -67,15 +67,13 @@
     queue_handler_ = new os::Handler(thread_);
     mock_channel_ = std::make_shared<testing::MockChannelImpl>();
     EXPECT_CALL(*mock_channel_, GetQueueDownEnd()).WillRepeatedly(Return(channel_queue_.GetDownEnd()));
-    EXPECT_CALL(*mock_channel_, GetChannelMode())
-        .WillRepeatedly(Return(RetransmissionAndFlowControlModeOption::L2CAP_BASIC));
     EXPECT_CALL(*mock_channel_, GetCid()).WillRepeatedly(Return(0x41));
     EXPECT_CALL(*mock_channel_, GetRemoteCid()).WillRepeatedly(Return(0x41));
-    segmenter_ = new Sender(queue_handler_, &scheduler_, mock_channel_);
+    sender_ = new Sender(queue_handler_, &scheduler_, mock_channel_);
   }
 
   void TearDown() override {
-    delete segmenter_;
+    delete sender_;
     queue_handler_->Clear();
     user_handler_->Clear();
     delete queue_handler_;
@@ -88,7 +86,7 @@
   os::Handler* queue_handler_ = nullptr;
   common::BidiQueue<Sender::UpperEnqueue, Sender::UpperDequeue> channel_queue_{10};
   std::shared_ptr<testing::MockChannelImpl> mock_channel_;
-  Sender* segmenter_ = nullptr;
+  Sender* sender_ = nullptr;
   FakeScheduler scheduler_;
 };
 
@@ -101,7 +99,7 @@
       queue_handler_, common::Bind(&L2capSegmenterTest::enqueue_callback, common::Unretained(this)));
   auto status = future.wait_for(std::chrono::milliseconds(3));
   EXPECT_EQ(status, std::future_status::ready);
-  auto packet = segmenter_->GetNextPacket();
+  auto packet = sender_->GetNextPacket();
   EXPECT_NE(packet, nullptr);
 }
 
diff --git a/gd/l2cap/l2cap_packets.pdl b/gd/l2cap/l2cap_packets.pdl
index ee9f3ed..ae44cb1 100644
--- a/gd/l2cap/l2cap_packets.pdl
+++ b/gd/l2cap/l2cap_packets.pdl
@@ -346,11 +346,11 @@
 }
 
 
-struct RetransmissionAndFlowControlConfigurationOption : ConfigurationOption (type = RETRANSMISSION_AND_FLOW_CONTROL, length = 8) {
+struct RetransmissionAndFlowControlConfigurationOption : ConfigurationOption (type = RETRANSMISSION_AND_FLOW_CONTROL, length = 9) {
   mode : RetransmissionAndFlowControlModeOption,
   tx_window_size : 8, // 1-32 for Flow Control and Retransmission, 1-63 for Enhanced
   max_transmit : 8,
-  retransmission_time_out : 8,
+  retransmission_time_out : 16,
   monitor_time_out : 16,
   maximum_pdu_size : 16,
 }
diff --git a/gd/l2cap/le/internal/fixed_channel_impl.cc b/gd/l2cap/le/internal/fixed_channel_impl.cc
index 5f85139..9ae4e1f 100644
--- a/gd/l2cap/le/internal/fixed_channel_impl.cc
+++ b/gd/l2cap/le/internal/fixed_channel_impl.cc
@@ -107,12 +107,10 @@
   return cid_;
 }
 
-RetransmissionAndFlowControlModeOption FixedChannelImpl::GetChannelMode() const {
-  return RetransmissionAndFlowControlModeOption::L2CAP_BASIC;
+void FixedChannelImpl::SetSender(l2cap::internal::Sender* sender) {
+  ASSERT_LOG(false, "Should not set sender for fixed channel");
 }
 
-void FixedChannelImpl::SetChannelMode(RetransmissionAndFlowControlModeOption option) {}
-
 }  // namespace internal
 }  // namespace le
 }  // namespace l2cap
diff --git a/gd/l2cap/le/internal/fixed_channel_impl.h b/gd/l2cap/le/internal/fixed_channel_impl.h
index 972640e..82a4125 100644
--- a/gd/l2cap/le/internal/fixed_channel_impl.h
+++ b/gd/l2cap/le/internal/fixed_channel_impl.h
@@ -55,9 +55,7 @@
 
   Cid GetCid() const override;
   Cid GetRemoteCid() const override;
-  RetransmissionAndFlowControlModeOption GetChannelMode() const override;
-  void SetChannelMode(RetransmissionAndFlowControlModeOption option) override;
-
+  void SetSender(l2cap::internal::Sender* sender) override;
   virtual void OnClosed(hci::ErrorCode status);
 
   virtual std::string ToString() {
diff --git a/gd/packet/fragmenting_inserter.cc b/gd/packet/fragmenting_inserter.cc
index 3d8917c..90f87c6 100644
--- a/gd/packet/fragmenting_inserter.cc
+++ b/gd/packet/fragmenting_inserter.cc
@@ -23,7 +23,7 @@
 
 FragmentingInserter::FragmentingInserter(size_t mtu,
                                          std::back_insert_iterator<std::vector<std::unique_ptr<RawBuilder>>> iterator)
-    : BitInserter(to_construct_bit_inserter_), mtu_(mtu), curr_packet_(std::make_unique<RawBuilder>()),
+    : BitInserter(to_construct_bit_inserter_), mtu_(mtu), curr_packet_(std::make_unique<RawBuilder>(mtu)),
       iterator_(iterator) {}
 
 void FragmentingInserter::insert_bits(uint8_t byte, size_t num_bits) {
@@ -36,7 +36,7 @@
     curr_packet_->AddOctets1(new_byte);
     if (curr_packet_->size() >= mtu_) {
       iterator_ = std::move(curr_packet_);
-      curr_packet_ = std::make_unique<RawBuilder>();
+      curr_packet_ = std::make_unique<RawBuilder>(mtu_);
     }
     total_bits -= 8;
     new_value = new_value >> 8;
diff --git a/gd/shim/l2cap.cc b/gd/shim/l2cap.cc
index 6177272..d703972 100644
--- a/gd/shim/l2cap.cc
+++ b/gd/shim/l2cap.cc
@@ -396,11 +396,13 @@
       std::make_shared<ServiceInterface>(&connection_interface_manager_, psm, on_open, std::move(completed));
   psm_to_service_interface_map_.emplace(psm, service_interface);
 
+  // TODO(cmanton): Use the configuration option from user
   service_interface->RegisterService(
       [this](l2cap::Psm psm, l2cap::SecurityPolicy security_policy,
              l2cap::classic::DynamicChannelManager::OnRegistrationCompleteCallback on_registration_complete,
              l2cap::classic::DynamicChannelManager::OnConnectionOpenCallback on_connection_open) {
-        bool rc = dynamic_channel_manager_->RegisterService(psm, security_policy, std::move(on_registration_complete),
+        bool rc = dynamic_channel_manager_->RegisterService(psm, l2cap::classic::DynamicChannelConfigurationOption(),
+                                                            security_policy, std::move(on_registration_complete),
                                                             on_connection_open, handler_);
         ASSERT_LOG(rc == true, "Failed to register classic service");
       });
@@ -418,9 +420,10 @@
   // TODO(cmanton) hash psm/address pair into unordered map for pending_connection
   // This is ok for now
   psm_to_pending_connection_map_[psm] = pending_connection;
-
+  // TODO(cmanton): Add ERTM mode support by changing configuratio_option in ConnectChannel()
   bool rc = dynamic_channel_manager_->ConnectChannel(
-      address, psm, common::Bind(&PendingConnection::OnConnectionOpen, common::Unretained(pending_connection.get())),
+      address, l2cap::classic::DynamicChannelConfigurationOption(), psm,
+      common::Bind(&PendingConnection::OnConnectionOpen, common::Unretained(pending_connection.get())),
       common::BindOnce(&PendingConnection::OnConnectionFailure, common::Unretained(pending_connection.get())),
       handler_);
   ASSERT_LOG(rc == true, "Failed to create classic connection");
diff --git a/hci/Android.bp b/hci/Android.bp
index 6d57ebf..ddfef50 100644
--- a/hci/Android.bp
+++ b/hci/Android.bp
@@ -3,6 +3,7 @@
     defaults: ["fluoride_defaults"],
     shared_libs: [
         "android.hardware.bluetooth@1.0",
+        "android.hardware.bluetooth@1.1",
         "libhidlbase",
     ],
 }
diff --git a/hci/include/bt_hci_bdroid.h b/hci/include/bt_hci_bdroid.h
index cf2c113..110354f 100644
--- a/hci/include/bt_hci_bdroid.h
+++ b/hci/include/bt_hci_bdroid.h
@@ -53,15 +53,17 @@
 #define MSG_SUB_EVT_MASK 0x00FF /* eq. BT_SUB_EVT_MASK */
 
 /* Message event ID passed from Host/Controller lib to stack */
-#define MSG_HC_TO_STACK_HCI_ERR 0x1300      /* eq. BT_EVT_TO_BTU_HCIT_ERR */
 #define MSG_HC_TO_STACK_HCI_ACL 0x1100      /* eq. BT_EVT_TO_BTU_HCI_ACL */
 #define MSG_HC_TO_STACK_HCI_SCO 0x1200      /* eq. BT_EVT_TO_BTU_HCI_SCO */
+#define MSG_HC_TO_STACK_HCI_ERR 0x1300      /* eq. BT_EVT_TO_BTU_HCIT_ERR */
+#define MSG_HC_TO_STACK_HCI_ISO 0x1700      /* eq. BT_EVT_TO_BTU_HCI_ISO */
 #define MSG_HC_TO_STACK_HCI_EVT 0x1000      /* eq. BT_EVT_TO_BTU_HCI_EVT */
 #define MSG_HC_TO_STACK_L2C_SEG_XMIT 0x1900 /* BT_EVT_TO_BTU_L2C_SEG_XMIT */
 
 /* Message event ID passed from stack to vendor lib */
 #define MSG_STACK_TO_HC_HCI_ACL 0x2100 /* eq. BT_EVT_TO_LM_HCI_ACL */
 #define MSG_STACK_TO_HC_HCI_SCO 0x2200 /* eq. BT_EVT_TO_LM_HCI_SCO */
+#define MSG_STACK_TO_HC_HCI_ISO 0x2d00 /* eq. BT_EVT_TO_LM_HCI_ISO */
 #define MSG_STACK_TO_HC_HCI_CMD 0x2000 /* eq. BT_EVT_TO_LM_HCI_CMD */
 
 /* Local Bluetooth Controller ID for BR/EDR */
diff --git a/hci/include/hci_hal.h b/hci/include/hci_hal.h
index f73e116..6fbb13e 100644
--- a/hci/include/hci_hal.h
+++ b/hci/include/hci_hal.h
@@ -29,7 +29,8 @@
   DATA_TYPE_COMMAND = 1,
   DATA_TYPE_ACL = 2,
   DATA_TYPE_SCO = 3,
-  DATA_TYPE_EVENT = 4
+  DATA_TYPE_EVENT = 4,
+  DATA_TYPE_ISO = 5
 } serial_data_type_t;
 
 typedef void (*data_ready_cb)(serial_data_type_t type);
diff --git a/hci/include/hci_internals.h b/hci/include/hci_internals.h
index 8fe0041..bf9430f 100644
--- a/hci/include/hci_internals.h
+++ b/hci/include/hci_internals.h
@@ -26,3 +26,5 @@
 #define HCI_SCO_PREAMBLE_SIZE 3
 // 1 byte for event code, 1 byte for parameter length (Volume 2, Part E, 5.4.4)
 #define HCI_EVENT_PREAMBLE_SIZE 2
+// 2 bytes for handle, 2 bytes for data length (Volume 2, Part E, 5.4.5)
+#define HCI_ISO_PREAMBLE_SIZE 4
\ No newline at end of file
diff --git a/hci/include/hci_layer.h b/hci/include/hci_layer.h
index cc74469..bf4efee 100644
--- a/hci/include/hci_layer.h
+++ b/hci/include/hci_layer.h
@@ -37,15 +37,17 @@
 #define MSG_SUB_EVT_MASK 0x00FF /* eq. BT_SUB_EVT_MASK */
 
 /* Message event ID passed from Host/Controller lib to stack */
-#define MSG_HC_TO_STACK_HCI_ERR 0x1300      /* eq. BT_EVT_TO_BTU_HCIT_ERR */
 #define MSG_HC_TO_STACK_HCI_ACL 0x1100      /* eq. BT_EVT_TO_BTU_HCI_ACL */
 #define MSG_HC_TO_STACK_HCI_SCO 0x1200      /* eq. BT_EVT_TO_BTU_HCI_SCO */
+#define MSG_HC_TO_STACK_HCI_ERR 0x1300      /* eq. BT_EVT_TO_BTU_HCIT_ERR */
+#define MSG_HC_TO_STACK_HCI_ISO 0x1700      /* eq. BT_EVT_TO_BTU_HCI_ISO */
 #define MSG_HC_TO_STACK_HCI_EVT 0x1000      /* eq. BT_EVT_TO_BTU_HCI_EVT */
 #define MSG_HC_TO_STACK_L2C_SEG_XMIT 0x1900 /* BT_EVT_TO_BTU_L2C_SEG_XMIT */
 
 /* Message event ID passed from stack to vendor lib */
 #define MSG_STACK_TO_HC_HCI_ACL 0x2100 /* eq. BT_EVT_TO_LM_HCI_ACL */
 #define MSG_STACK_TO_HC_HCI_SCO 0x2200 /* eq. BT_EVT_TO_LM_HCI_SCO */
+#define MSG_STACK_TO_HC_HCI_ISO 0x2d00 /* eq. BT_EVT_TO_LM_HCI_ISO */
 #define MSG_STACK_TO_HC_HCI_CMD 0x2000 /* eq. BT_EVT_TO_LM_HCI_CMD */
 
 /* Local Bluetooth Controller ID for BR/EDR */
diff --git a/hci/src/hci_inject.cc b/hci/src/hci_inject.cc
index 01b8cfd..6d3e8e0 100644
--- a/hci/src/hci_inject.cc
+++ b/hci/src/hci_inject.cc
@@ -39,6 +39,7 @@
   HCI_PACKET_ACL_DATA = 2,
   HCI_PACKET_SCO_DATA = 3,
   HCI_PACKET_EVENT = 4,
+  HCI_PACKET_ISO_DATA = 5,
 } hci_packet_t;
 
 typedef struct {
@@ -118,6 +119,8 @@
       return MSG_STACK_TO_HC_HCI_ACL;
     case HCI_PACKET_SCO_DATA:
       return MSG_STACK_TO_HC_HCI_SCO;
+    case HCI_PACKET_ISO_DATA:
+      return MSG_STACK_TO_HC_HCI_ISO;
     default:
       LOG_ERROR(LOG_TAG, "%s unsupported packet type: %d", __func__, packet);
       return -1;
diff --git a/hci/src/hci_layer.cc b/hci/src/hci_layer.cc
index 1a4f703..da6054b 100644
--- a/hci/src/hci_layer.cc
+++ b/hci/src/hci_layer.cc
@@ -161,6 +161,11 @@
   packet_fragmenter->reassemble_and_dispatch(packet);
 }
 
+void iso_data_received(BT_HDR* packet) {
+  btsnoop->capture(packet, true);
+  packet_fragmenter->reassemble_and_dispatch(packet);
+}
+
 // Module lifecycle functions
 
 static future_t* hci_module_shut_down();
diff --git a/hci/src/hci_layer_android.cc b/hci/src/hci_layer_android.cc
index d3fa526..c4aded1 100644
--- a/hci/src/hci_layer_android.cc
+++ b/hci/src/hci_layer_android.cc
@@ -32,6 +32,8 @@
 #include <android/hardware/bluetooth/1.0/IBluetoothHci.h>
 #include <android/hardware/bluetooth/1.0/IBluetoothHciCallbacks.h>
 #include <android/hardware/bluetooth/1.0/types.h>
+#include <android/hardware/bluetooth/1.1/IBluetoothHci.h>
+#include <android/hardware/bluetooth/1.1/IBluetoothHciCallbacks.h>
 #include <hwbinder/ProcessState.h>
 
 #define LOG_PATH "/data/misc/bluetooth/logs/firmware_events.log"
@@ -43,16 +45,18 @@
 using ::android::hardware::Return;
 using ::android::hardware::Void;
 using ::android::hardware::bluetooth::V1_0::HciPacket;
-using ::android::hardware::bluetooth::V1_0::IBluetoothHci;
-using ::android::hardware::bluetooth::V1_0::IBluetoothHciCallbacks;
 using ::android::hardware::bluetooth::V1_0::Status;
 
+using namespace ::android::hardware::bluetooth;
+
 extern void initialization_complete();
 extern void hci_event_received(const base::Location& from_here, BT_HDR* packet);
 extern void acl_event_received(BT_HDR* packet);
 extern void sco_data_received(BT_HDR* packet);
+extern void iso_data_received(BT_HDR* packet);
 
-android::sp<IBluetoothHci> btHci;
+android::sp<V1_0::IBluetoothHci> btHci;
+android::sp<V1_1::IBluetoothHci> btHci_1_1;
 
 class BluetoothHciDeathRecipient : public hidl_death_recipient {
  public:
@@ -63,7 +67,7 @@
 };
 android::sp<BluetoothHciDeathRecipient> bluetoothHciDeathRecipient = new BluetoothHciDeathRecipient();
 
-class BluetoothHciCallbacks : public IBluetoothHciCallbacks {
+class BluetoothHciCallbacks : public V1_1::IBluetoothHciCallbacks {
  public:
   BluetoothHciCallbacks() {
     buffer_allocator = buffer_allocator_get_interface();
@@ -107,13 +111,26 @@
     return Void();
   }
 
+  Return<void> isoDataReceived(const hidl_vec<uint8_t>& data) override {
+    BT_HDR* packet = WrapPacketAndCopy(MSG_HC_TO_STACK_HCI_ISO, data);
+    iso_data_received(packet);
+    return Void();
+  }
+
   const allocator_t* buffer_allocator;
 };
 
 void hci_initialize() {
   LOG_INFO(LOG_TAG, "%s", __func__);
 
-  btHci = IBluetoothHci::getService();
+  btHci_1_1 = V1_1::IBluetoothHci::getService();
+
+  if (btHci_1_1 != nullptr) {
+    btHci = btHci_1_1;
+  } else {
+    btHci = V1_0::IBluetoothHci::getService();
+  }
+
   // If android.hardware.bluetooth* is not found, Bluetooth can not continue.
   CHECK(btHci != nullptr);
   auto death_link = btHci->linkToDeath(bluetoothHciDeathRecipient, 0);
@@ -126,8 +143,13 @@
 
   // Block allows allocation of a variable that might be bypassed by goto.
   {
-    android::sp<IBluetoothHciCallbacks> callbacks = new BluetoothHciCallbacks();
-    btHci->initialize(callbacks);
+    android::sp<V1_1::IBluetoothHciCallbacks> callbacks =
+        new BluetoothHciCallbacks();
+    if (btHci_1_1 != nullptr) {
+      btHci_1_1->initialize(callbacks);
+    } else {
+      btHci->initialize(callbacks);
+    }
   }
 }
 
@@ -157,6 +179,13 @@
     case MSG_STACK_TO_HC_HCI_SCO:
       btHci->sendScoData(data);
       break;
+    case MSG_STACK_TO_HC_HCI_ISO:
+      if (btHci_1_1 != nullptr) {
+        btHci_1_1->sendIsoData(data);
+      } else {
+        LOG_ERROR(LOG_TAG, "ISO is not supported in HAL v1.0");
+      }
+      break;
     default:
       LOG_ERROR(LOG_TAG, "Unknown packet type (%d)", event);
       break;
diff --git a/main/Android.bp b/main/Android.bp
index fec6644..ef7dba6 100644
--- a/main/Android.bp
+++ b/main/Android.bp
@@ -54,6 +54,7 @@
     logtags: ["../EventLogTags.logtags"],
     shared_libs: [
         "android.hardware.bluetooth@1.0",
+        "android.hardware.bluetooth@1.1",
         "android.hardware.bluetooth.a2dp@1.0",
         "android.hardware.bluetooth.audio@2.0",
         "libaudioclient",
diff --git a/stack/Android.bp b/stack/Android.bp
index 825459b..b573aaf 100644
--- a/stack/Android.bp
+++ b/stack/Android.bp
@@ -202,6 +202,7 @@
     ],
     shared_libs: [
         "android.hardware.bluetooth@1.0",
+        "android.hardware.bluetooth@1.1",
         "android.hardware.bluetooth.a2dp@1.0",
         "android.hardware.bluetooth.audio@2.0",
         "libaudioclient",
diff --git a/stack/btu/btu_task.cc b/stack/btu/btu_task.cc
index 49c9697..8838206 100644
--- a/stack/btu/btu_task.cc
+++ b/stack/btu/btu_task.cc
@@ -71,6 +71,11 @@
       btu_hcif_send_cmd((uint8_t)(p_msg->event & BT_SUB_EVT_MASK), p_msg);
       break;
 
+    case BT_EVT_TO_BTU_HCI_ISO:
+      // TODO: implement handler
+      osi_free(p_msg);
+      break;
+
     default:
       osi_free(p_msg);
       break;
diff --git a/stack/include/bt_types.h b/stack/include/bt_types.h
index 7804328..01e8248 100644
--- a/stack/include/bt_types.h
+++ b/stack/include/bt_types.h
@@ -80,6 +80,9 @@
 /* HCI command from upper layer     */
 #define BT_EVT_TO_BTU_HCI_CMD 0x1600
 
+/* ISO Data from HCI                */
+#define BT_EVT_TO_BTU_HCI_ISO 0x1700
+
 /* L2CAP segment(s) transmitted     */
 #define BT_EVT_TO_BTU_L2C_SEG_XMIT 0x1900
 
@@ -119,6 +122,8 @@
 #define BT_EVT_TO_LM_HCI_ACL_ACK 0x2b00
 /* LM Diagnostics commands          */
 #define BT_EVT_TO_LM_DIAG 0x2c00
+/* HCI ISO Data                     */
+#define BT_EVT_TO_LM_HCI_ISO 0x2d00
 
 #define BT_EVT_TO_BTM_CMDS 0x2f00
 #define BT_EVT_TO_BTM_PM_MDCHG_EVT (0x0001 | BT_EVT_TO_BTM_CMDS)
diff --git a/test/rootcanal/Android.bp b/test/rootcanal/Android.bp
index f2d3293..bd62e50 100644
--- a/test/rootcanal/Android.bp
+++ b/test/rootcanal/Android.bp
@@ -14,7 +14,7 @@
 // limitations under the License.
 
 cc_binary {
-    name: "android.hardware.bluetooth@1.0-service.sim",
+    name: "android.hardware.bluetooth@1.1-service.sim",
     proprietary: true,
     relative_install_path: "hw",
     srcs: [
@@ -25,6 +25,7 @@
     header_libs: ["libbluetooth_headers"],
     shared_libs: [
         "android.hardware.bluetooth@1.0",
+        "android.hardware.bluetooth@1.1",
         "libbase",
         "libchrome",
         "libcutils",
@@ -56,11 +57,11 @@
         "system/bt/internal_include",
         "system/bt/stack/include",
     ],
-    init_rc: ["android.hardware.bluetooth@1.0-service.sim.rc"],
+    init_rc: ["android.hardware.bluetooth@1.1-service.sim.rc"],
 }
 
 cc_library_shared {
-    name: "android.hardware.bluetooth@1.0-impl-sim",
+    name: "android.hardware.bluetooth@1.1-impl-sim",
     proprietary: true,
     relative_install_path: "hw",
     srcs: [
@@ -70,6 +71,7 @@
     header_libs: ["libbluetooth_headers"],
     shared_libs: [
         "android.hardware.bluetooth@1.0",
+        "android.hardware.bluetooth@1.1",
         "libbase",
         "libchrome",
         "libcutils",
diff --git a/test/rootcanal/android.hardware.bluetooth@1.0-service.sim.rc b/test/rootcanal/android.hardware.bluetooth@1.0-service.sim.rc
deleted file mode 100644
index 9d99c8a..0000000
--- a/test/rootcanal/android.hardware.bluetooth@1.0-service.sim.rc
+++ /dev/null
@@ -1,4 +0,0 @@
-service vendor.bluetooth-1-0 /vendor/bin/hw/android.hardware.bluetooth@1.0-service.sim
-    class hal
-    user bluetooth
-    group bluetooth
diff --git a/test/rootcanal/android.hardware.bluetooth@1.1-service.sim.rc b/test/rootcanal/android.hardware.bluetooth@1.1-service.sim.rc
new file mode 100644
index 0000000..2626841
--- /dev/null
+++ b/test/rootcanal/android.hardware.bluetooth@1.1-service.sim.rc
@@ -0,0 +1,4 @@
+service vendor.bluetooth-1-1 /vendor/bin/hw/android.hardware.bluetooth@1.1-service.sim
+    class hal
+    user bluetooth
+    group bluetooth
diff --git a/test/rootcanal/bluetooth_hci.cc b/test/rootcanal/bluetooth_hci.cc
index 9978556..3a636f5 100644
--- a/test/rootcanal/bluetooth_hci.cc
+++ b/test/rootcanal/bluetooth_hci.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#define LOG_TAG "android.hardware.bluetooth@1.0.sim"
+#define LOG_TAG "android.hardware.bluetooth@1.1.sim"
 
 #include "bluetooth_hci.h"
 
@@ -29,7 +29,7 @@
 namespace android {
 namespace hardware {
 namespace bluetooth {
-namespace V1_0 {
+namespace V1_1 {
 namespace sim {
 
 using android::hardware::hidl_vec;
@@ -71,7 +71,19 @@
 
 BluetoothHci::BluetoothHci() : death_recipient_(new BluetoothDeathRecipient(this)) {}
 
-Return<void> BluetoothHci::initialize(const sp<IBluetoothHciCallbacks>& cb) {
+Return<void> BluetoothHci::initialize(
+    const sp<V1_0::IBluetoothHciCallbacks>& cb) {
+  return initialize_impl(cb, nullptr);
+}
+
+Return<void> BluetoothHci::initialize_1_1(
+    const sp<V1_1::IBluetoothHciCallbacks>& cb) {
+  return initialize_impl(cb, cb);
+}
+
+Return<void> BluetoothHci::initialize_impl(
+    const sp<V1_0::IBluetoothHciCallbacks>& cb,
+    const sp<V1_1::IBluetoothHciCallbacks>& cb_1_1) {
   LOG_INFO("%s", __func__);
 
   if (cb == nullptr) {
@@ -124,6 +136,18 @@
         }
       });
 
+  if (cb_1_1 != nullptr) {
+    controller_->RegisterIsoChannel(
+        [this, cb_1_1](std::shared_ptr<std::vector<uint8_t>> packet) {
+          hidl_vec<uint8_t> iso_packet(packet->begin(), packet->end());
+          auto ret = cb_1_1->isoDataReceived(iso_packet);
+          if (!ret.isOk()) {
+            CHECK(death_recipient_->getHasDied())
+                << "Error sending iso callback, but no death notification.";
+          }
+        });
+  }
+
   controller_->RegisterTaskScheduler(
       [this](std::chrono::milliseconds delay, const TaskCallback& task) {
         return async_manager_.ExecAsync(delay, task);
@@ -177,7 +201,7 @@
     }
   };
 
-  auto init_ret = cb->initializationComplete(Status::SUCCESS);
+  auto init_ret = cb->initializationComplete(V1_0::Status::SUCCESS);
   if (!init_ret.isOk()) {
     CHECK(death_recipient_->getHasDied())
         << "Error sending init callback, but no death notification.";
@@ -217,6 +241,15 @@
   return Void();
 }
 
+Return<void> BluetoothHci::sendIsoData(const hidl_vec<uint8_t>& packet) {
+  async_manager_.ExecAsync(std::chrono::milliseconds(0), [this, packet]() {
+    std::shared_ptr<std::vector<uint8_t>> packet_copy =
+        std::shared_ptr<std::vector<uint8_t>>(new std::vector<uint8_t>(packet));
+    controller_->HandleIso(packet_copy);
+  });
+  return Void();
+}
+
 void BluetoothHci::SetUpHciServer(int port, const std::function<void(int)>& connection_callback) {
   int socket_fd = remote_hci_transport_.SetUp(port);
 
@@ -341,7 +374,7 @@
 }
 
 }  // namespace sim
-}  // namespace V1_0
+}  // namespace V1_1
 }  // namespace bluetooth
 }  // namespace hardware
 }  // namespace android
diff --git a/test/rootcanal/bluetooth_hci.h b/test/rootcanal/bluetooth_hci.h
index 130b85c..99eda9f 100644
--- a/test/rootcanal/bluetooth_hci.h
+++ b/test/rootcanal/bluetooth_hci.h
@@ -18,7 +18,7 @@
 
 #include "os/log.h"
 
-#include <android/hardware/bluetooth/1.0/IBluetoothHci.h>
+#include <android/hardware/bluetooth/1.1/IBluetoothHci.h>
 
 #include <hidl/MQDescriptor.h>
 
@@ -33,7 +33,7 @@
 namespace android {
 namespace hardware {
 namespace bluetooth {
-namespace V1_0 {
+namespace V1_1 {
 namespace sim {
 
 class BluetoothDeathRecipient;
@@ -42,7 +42,10 @@
  public:
   BluetoothHci();
 
-  ::android::hardware::Return<void> initialize(const sp<IBluetoothHciCallbacks>& cb) override;
+  ::android::hardware::Return<void> initialize(
+      const sp<V1_0::IBluetoothHciCallbacks>& cb) override;
+  ::android::hardware::Return<void> initialize_1_1(
+      const sp<V1_1::IBluetoothHciCallbacks>& cb) override;
 
   ::android::hardware::Return<void> sendHciCommand(const ::android::hardware::hidl_vec<uint8_t>& packet) override;
 
@@ -50,6 +53,9 @@
 
   ::android::hardware::Return<void> sendScoData(const ::android::hardware::hidl_vec<uint8_t>& packet) override;
 
+  ::android::hardware::Return<void> sendIsoData(
+      const ::android::hardware::hidl_vec<uint8_t>& packet) override;
+
   ::android::hardware::Return<void> close() override;
 
   static void OnPacketReady();
@@ -57,6 +63,10 @@
   static BluetoothHci* get();
 
  private:
+  ::android::hardware::Return<void> initialize_impl(
+      const sp<V1_0::IBluetoothHciCallbacks>& cb,
+      const sp<V1_1::IBluetoothHciCallbacks>& cb_1_1);
+
   sp<BluetoothDeathRecipient> death_recipient_;
 
   std::function<void(sp<BluetoothDeathRecipient>&)> unlink_cb_;
@@ -96,7 +106,7 @@
 extern "C" IBluetoothHci* HIDL_FETCH_IBluetoothHci(const char* name);
 
 }  // namespace sim
-}  // namespace V1_0
+}  // namespace V1_1
 }  // namespace bluetooth
 }  // namespace hardware
 }  // namespace android
diff --git a/test/rootcanal/service.cc b/test/rootcanal/service.cc
index 1abfb7a..7856dcf 100644
--- a/test/rootcanal/service.cc
+++ b/test/rootcanal/service.cc
@@ -14,11 +14,11 @@
 // limitations under the License.
 //
 
-#define LOG_TAG "android.hardware.bluetooth@1.0-service.sim"
+#define LOG_TAG "android.hardware.bluetooth@1.1-service.sim"
 
 #include "os/log.h"
 
-#include <android/hardware/bluetooth/1.0/IBluetoothHci.h>
+#include <android/hardware/bluetooth/1.1/IBluetoothHci.h>
 #include <hidl/HidlSupport.h>
 #include <hidl/HidlTransportSupport.h>
 
@@ -27,8 +27,8 @@
 using ::android::sp;
 using ::android::hardware::configureRpcThreadpool;
 using ::android::hardware::joinRpcThreadpool;
-using ::android::hardware::bluetooth::V1_0::IBluetoothHci;
-using ::android::hardware::bluetooth::V1_0::sim::BluetoothHci;
+using ::android::hardware::bluetooth::V1_1::IBluetoothHci;
+using ::android::hardware::bluetooth::V1_1::sim::BluetoothHci;
 
 int main(int /* argc */, char** /* argv */) {
   sp<IBluetoothHci> bluetooth = new BluetoothHci;
diff --git a/tools/hci/main.c b/tools/hci/main.c
index c167886..093af6b 100644
--- a/tools/hci/main.c
+++ b/tools/hci/main.c
@@ -15,6 +15,7 @@
   HCI_PACKET_ACL_DATA = 2,
   HCI_PACKET_SCO_DATA = 3,
   HCI_PACKET_EVENT = 4,
+  HCI_PACKET_ISO = 5,
 } hci_packet_t;
 
 typedef struct {
diff --git a/tools/scripts/btsnoop_live.py b/tools/scripts/btsnoop_live.py
new file mode 100644
index 0000000..4e145ff
--- /dev/null
+++ b/tools/scripts/btsnoop_live.py
@@ -0,0 +1,297 @@
+#!/usr/bin/env python3
+###############################################################################################################
+#
+#  Copyright (C) 2019 Motorola Mobility LLC
+#
+#  Redistribution and use in source and binary forms, with or without modification, are permitted provided that
+#  the following conditions are met:
+#
+#  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the
+#     following disclaimer.
+#
+#  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
+#     the following disclaimer in the documentation and/or other materials provided with the distribution.
+#
+#  3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or
+#     promote products derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+#  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+#  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+#  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+#  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+#  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+#  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+###############################################################################################################
+###############################################################################################################
+#
+#                             Bluetooth Virtual Sniffing for Android
+#
+#  This script supports Bluetooth Virtual Sniffing via Live Import Feature of Frontline Bluetooth Sniffer(FTS)
+#
+#  It extracts the HCI packets from Snoop logs and redirect it to FTS for live HCI sniffing.
+#
+#  It uses liveimport.ini and LiveImportAPI.dll from FTS path to communicate with FTS sniffer software.
+#
+#  It works on both Windows and Ubuntu. For Ubuntu, both FTS and Python should be installed on Wine.
+#
+#  FTS_INI_PATH & FTS_DLL_PATH should be set to absolute path of liveimport.ini and LiveImportAPI.dll of FTS
+#
+#  Example below - This may change per machine per FTS version in Windows vs Ubuntu (Wine), set accordingly.
+#  For Windows
+#    FTS_INI_PATH = 'C:\\Program Files (x86)\\Frontline Test System II\\Frontline 13.2\\'
+#    FTS_DLL_PATH = 'C:\\Program Files (x86)\\Frontline Test System II\\Frontline 13.2\\Executables\\Core\\'
+#
+#  For Ubuntu - FTS path recognized by Wine (not Ubuntu path)
+#    FTS_INI_PATH = 'C:\\Program Files\\Frontline Test System II\\Frontline 13.2\\'
+#    FTS_DLL_PATH = 'C:\\Program Files\\Frontline Test System II\\Frontline 13.2\\Executables\\Core\\'
+#
+###############################################################################################################
+
+import os
+import platform
+import socket
+import struct
+import subprocess
+import sys
+import time
+if sys.version_info[0] >= 3:
+    import configparser
+else:
+    import ConfigParser as configparser
+
+from calendar import timegm
+from ctypes import byref, c_bool, c_longlong, CDLL
+from _ctypes import FreeLibrary
+from datetime import datetime
+
+
+# Update below to right path corresponding to your machine, FTS version and OS used.
+FTS_INI_PATH = 'C:\\Program Files (x86)\\Frontline Test System II\\Frontline 15.12\\'
+FTS_DLL_PATH = 'C:\\Program Files (x86)\\Frontline Test System II\\Frontline 15.12\\Executables\\Core\\'
+
+
+iniName = 'liveimport.ini'
+if (platform.architecture()[0] == '32bit'):
+    dllName = 'LiveImportAPI.dll'
+else:
+    dllName = 'LiveImportAPI_x64.dll'
+
+launchFtsCmd = '\"'+FTS_DLL_PATH + 'fts.exe\"' + ' \"/ComProbe Protocol Analysis System=Generic\"' + ' \"/oemkey=Virtual\"'
+
+# Unix Epoch delta since 01/01/1970
+FILETIME_EPOCH_DELTA = 116444736000000000
+HUNDREDS_OF_NANOSECONDS = 10000000
+
+HOST = 'localhost'
+PORT = 8872
+SNOOP_ID = 16
+SNOOP_HDR = 24
+
+
+def get_file_time():
+    """
+    Obtain current time in file time format for display
+    """
+    date_time = datetime.now()
+    file_time = FILETIME_EPOCH_DELTA + (timegm(date_time.timetuple()) * HUNDREDS_OF_NANOSECONDS)
+    file_time = file_time + (date_time.microsecond * 10)
+    return file_time
+
+
+def get_connection_string():
+    """
+    Read ConnectionString from liveimport.ini
+    """
+    config = configparser.ConfigParser()
+    config.read(FTS_INI_PATH + iniName)
+    try:
+        conn_str = config.get('General', 'ConnectionString')
+    except (configparser.NoSectionError, configparser.NoOptionError):
+        return None
+
+    return conn_str
+
+
+def get_configuration_string():
+    """
+    Read Configuration string from liveimport.ini
+    """
+    config_str = ''
+    config = configparser.ConfigParser()
+    config.read(FTS_INI_PATH + iniName)
+    try:
+        config_items = config.items('Configuration')
+    except (configparser.NoSectionError, configparser.NoOptionError):
+        return None
+
+    if config_items is not None:
+        for item in config_items:
+            key, value = item
+            config_str += ("%s=%s\n" % (key, value))
+        return config_str
+    else:
+        return None
+
+def check_live_import_connection(live_import):
+    """
+    Launch FTS app in Virtual Sniffing Mode
+    Check if FTS App is ready to start receiving the data.
+    If not, wait until 1 min and exit if FTS didn't start.
+    """
+    is_connection_running = c_bool()
+    count = 0
+
+    status = live_import.IsAppReady(byref(is_connection_running))
+    if (is_connection_running.value == True):
+        print ("FTS is already launched, Start capture if not already started")
+        return True
+
+    print("Launching FTS Virtual Sniffing")
+    try:
+        ftsProcess = subprocess.Popen((launchFtsCmd), stdout=subprocess.PIPE)
+    except:
+        print("Error in Launching FTS.. exiting")
+        return False
+
+    while (is_connection_running.value == False and count < 12):
+        status = live_import.IsAppReady(byref(is_connection_running))
+        if (status < 0):
+            print("Live Import Internal Error %d" %(status))
+            return False
+        if (is_connection_running.value == False):
+            print("Waiting for 5 sec.. Open FTS Virtual Sniffing")
+            time.sleep(5)
+            count += 1
+    if (is_connection_running.value == True):
+        print ("FTS is ready to receive the data, Start capture now")
+        return True
+    else:
+        print("FTS Virtual Sniffing didn't start until 1 min.. exiting")
+        return False
+
+
+def init_live_import(conn_str, config_str):
+    """
+    Load DLL and Initialize the LiveImport module for FTS.
+    """
+    success = c_bool()
+    try:
+        live_import = CDLL(FTS_DLL_PATH + dllName)
+    except:
+        return None
+
+    if live_import is None:
+        print("Error: Path to LiveImportAPI.dll is incorrect.. exiting");
+        return None
+
+    print(dllName + " loaded successfully")
+    result = live_import.InitializeLiveImport(conn_str.encode('ascii', 'ignore'),
+                                              config_str.encode('ascii', 'ignore'),
+                                              byref(success))
+    if (result < 0):
+        print("Live Import Init failed")
+        return None
+    else:
+        print("Live Import Init success")
+        return live_import
+
+
+def release_live_import(live_import):
+    """
+    Cleanup and exit live import module.
+    """
+    if live_import is not None:
+        live_import.ReleaseLiveImport()
+        FreeLibrary(live_import._handle)
+
+
+def main():
+
+    print("Bluetooth Virtual Sniffing for Fluoride")
+    connection_str = get_connection_string()
+    if connection_str is None:
+        print("Error: path to liveimport.ini is incorrect.. exiting")
+        exit(0)
+
+    configuration_str = get_configuration_string()
+    if configuration_str is None:
+        print("Error: path to liveimport.ini is incorrect.. exiting")
+        exit(0)
+
+    live_import = init_live_import(connection_str, configuration_str)
+    if live_import is None:
+        print("Error: Path to LiveImportAPI.dll is incorrect.. exiting")
+        exit(0)
+
+    if (check_live_import_connection(live_import) == False):
+        release_live_import(live_import)
+        exit(0)
+
+    # Wait until the forward socket is ready
+    print("Waiting until adb is ready")
+    os.system('adb wait-for-device forward tcp:8872 tcp:8872')
+
+    btsnoop_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    btsnoop_sock.connect((HOST, PORT))
+    snoop_id = btsnoop_sock.recv(SNOOP_ID)
+    if not snoop_id.startswith(b"btsnoop"):
+        print("Error: Snoop ID wasn't received.. exiting")
+        release_live_import(live_import)
+        exit(0)
+
+    while True:
+        try:
+            snoop_hdr  = btsnoop_sock.recv(SNOOP_HDR)
+            if snoop_hdr is not None:
+                try:
+                    olen, ilen, flags = struct.unpack(">LLL", snoop_hdr[0:12])
+                except struct.error:
+                    print("Error: Invalid data", repr(snoop_hdr))
+                    continue
+
+                file_time = get_file_time()
+                timestamp = c_longlong(file_time)
+
+                snoop_data = b''
+                while (len(snoop_data) < olen):
+                    data_frag = btsnoop_sock.recv(olen - len(snoop_data))
+                    if data_frag is not None:
+                        snoop_data += data_frag
+
+                print ("Bytes received %d Olen %d ilen %d flags %d" % (len(snoop_data), olen, ilen, flags))
+                packet_type = struct.unpack(">B", snoop_data[0:1])[0]
+                if packet_type == 1:
+                    drf = 1
+                    isend = 0
+                elif packet_type == 2:
+                    drf = 2
+                    if (flags & 0x01):
+                        isend = 1
+                    else:
+                        isend = 0
+                elif packet_type == 3:
+                    drf = 4
+                    if (flags & 0x01):
+                        isend = 1
+                    else:
+                        isend = 0
+                elif packet_type == 4:
+                    drf = 8
+                    isend = 1
+
+                result = live_import.SendFrame(olen-1, olen-1, snoop_data[1:olen], drf, isend, timestamp)
+                if (result < 0):
+                    print("Send frame failed")
+        except KeyboardInterrupt:
+            print("Cleanup and exit")
+            release_live_import(live_import)
+            btsnoop_sock.close()
+            exit(0)
+
+
+if __name__ == '__main__':
+    main()
+
diff --git a/vendor_libs/linux/interface/Android.bp b/vendor_libs/linux/interface/Android.bp
index fd4f513..e7d161e 100644
--- a/vendor_libs/linux/interface/Android.bp
+++ b/vendor_libs/linux/interface/Android.bp
@@ -14,7 +14,7 @@
 // limitations under the License.
 
 cc_binary {
-    name: "android.hardware.bluetooth@1.0-service.btlinux",
+    name: "android.hardware.bluetooth@1.1-service.btlinux",
     proprietary: true,
     relative_install_path: "hw",
     srcs: [
@@ -30,7 +30,7 @@
     ],
     header_libs: ["libbluetooth_headers"],
     shared_libs: [
-        "android.hardware.bluetooth@1.0",
+        "android.hardware.bluetooth@1.1",
         "libbase",
         "libcutils",
         "libhidlbase",
@@ -40,5 +40,5 @@
     conlyflags: [
         "-std=c99",
     ],
-    init_rc: ["android.hardware.bluetooth@1.0-service.btlinux.rc"],
+    init_rc: ["android.hardware.bluetooth@1.1-service.btlinux.rc"],
 }
diff --git a/vendor_libs/linux/interface/android.hardware.bluetooth@1.0-service.btlinux.rc b/vendor_libs/linux/interface/android.hardware.bluetooth@1.0-service.btlinux.rc
deleted file mode 100644
index 36fbc2c..0000000
--- a/vendor_libs/linux/interface/android.hardware.bluetooth@1.0-service.btlinux.rc
+++ /dev/null
@@ -1,5 +0,0 @@
-service btlinux-1.0 /vendor/bin/hw/android.hardware.bluetooth@1.0-service.btlinux
-    class hal
-    user bluetooth
-    group bluetooth net_admin net_bt_admin
-    capabilities NET_ADMIN
diff --git a/vendor_libs/linux/interface/android.hardware.bluetooth@1.1-service.btlinux.rc b/vendor_libs/linux/interface/android.hardware.bluetooth@1.1-service.btlinux.rc
new file mode 100644
index 0000000..cb03de2
--- /dev/null
+++ b/vendor_libs/linux/interface/android.hardware.bluetooth@1.1-service.btlinux.rc
@@ -0,0 +1,5 @@
+service btlinux-1.1 /vendor/bin/hw/android.hardware.bluetooth@1.1-service.btlinux
+    class hal
+    user bluetooth
+    group bluetooth net_admin net_bt_admin
+    capabilities NET_ADMIN
diff --git a/vendor_libs/linux/interface/bluetooth_hci.cc b/vendor_libs/linux/interface/bluetooth_hci.cc
index 5ed2ff6..bd987d8 100644
--- a/vendor_libs/linux/interface/bluetooth_hci.cc
+++ b/vendor_libs/linux/interface/bluetooth_hci.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#define LOG_TAG "android.hardware.bluetooth@1.0-btlinux"
+#define LOG_TAG "android.hardware.bluetooth@1.1-btlinux"
 #include <errno.h>
 #include <fcntl.h>
 #include <poll.h>
@@ -66,7 +66,7 @@
 namespace android {
 namespace hardware {
 namespace bluetooth {
-namespace V1_0 {
+namespace V1_1 {
 namespace btlinux {
 
 int BluetoothHci::openBtHci() {
@@ -264,7 +264,18 @@
     : death_recipient_(new BluetoothDeathRecipient(this)) {}
 
 Return<void> BluetoothHci::initialize(
-    const ::android::sp<IBluetoothHciCallbacks>& cb) {
+    const ::android::sp<V1_0::IBluetoothHciCallbacks>& cb) {
+  return initialize_impl(cb, nullptr);
+}
+
+Return<void> BluetoothHci::initialize_1_1(
+    const ::android::sp<V1_1::IBluetoothHciCallbacks>& cb) {
+  return initialize_impl(cb, cb);
+}
+
+Return<void> BluetoothHci::initialize_impl(
+    const ::android::sp<V1_0::IBluetoothHciCallbacks>& cb,
+    const ::android::sp<V1_1::IBluetoothHciCallbacks>& cb_1_1) {
   ALOGI("BluetoothHci::initialize()");
   if (cb == nullptr) {
     ALOGE("cb == nullptr! -> Unable to call initializationComplete(ERR)");
@@ -275,7 +286,7 @@
   cb->linkToDeath(death_recipient_, 0);
   int hci_fd = openBtHci();
   auto hidl_status = cb->initializationComplete(
-          hci_fd > 0 ? Status::SUCCESS : Status::INITIALIZATION_ERROR);
+      hci_fd > 0 ? V1_0::Status::SUCCESS : V1_0::Status::INITIALIZATION_ERROR);
   if (!hidl_status.isOk()) {
       ALOGE("VendorInterface -> Unable to call initializationComplete(ERR)");
   }
@@ -283,7 +294,10 @@
       hci_fd,
       [cb](const hidl_vec<uint8_t>& packet) { cb->hciEventReceived(packet); },
       [cb](const hidl_vec<uint8_t>& packet) { cb->aclDataReceived(packet); },
-      [cb](const hidl_vec<uint8_t>& packet) { cb->scoDataReceived(packet); });
+      [cb](const hidl_vec<uint8_t>& packet) { cb->scoDataReceived(packet); },
+      [cb_1_1](const hidl_vec<uint8_t>& packet) {
+        cb_1_1->isoDataReceived(packet);
+      });
 
   fd_watcher_.WatchFdForNonBlockingReads(
           hci_fd, [h4_hci](int fd) { h4_hci->OnDataReady(fd); });
@@ -327,6 +341,11 @@
   return Void();
 }
 
+Return<void> BluetoothHci::sendIsoData(const hidl_vec<uint8_t>& data) {
+  sendDataToController(HCI_DATA_TYPE_ISO, data);
+  return Void();
+}
+
 void BluetoothHci::sendDataToController(const uint8_t type,
                                         const hidl_vec<uint8_t>& data) {
   hci_handle_->Send(type, data.data(), data.size());
@@ -337,7 +356,7 @@
 }
 
 }  // namespace btlinux
-}  // namespace V1_0
+}  // namespace V1_1
 }  // namespace bluetooth
 }  // namespace hardware
 }  // namespace android
diff --git a/vendor_libs/linux/interface/bluetooth_hci.h b/vendor_libs/linux/interface/bluetooth_hci.h
index 5dc1c0a..095c10c 100644
--- a/vendor_libs/linux/interface/bluetooth_hci.h
+++ b/vendor_libs/linux/interface/bluetooth_hci.h
@@ -14,10 +14,11 @@
 // limitations under the License.
 //
 
-#ifndef HIDL_GENERATED_android_hardware_bluetooth_V1_0_BluetoothHci_H_
-#define HIDL_GENERATED_android_hardware_bluetooth_V1_0_BluetoothHci_H_
+#ifndef HIDL_GENERATED_android_hardware_bluetooth_V1_1_BluetoothHci_H_
+#define HIDL_GENERATED_android_hardware_bluetooth_V1_1_BluetoothHci_H_
 
 #include <android/hardware/bluetooth/1.0/IBluetoothHci.h>
+#include <android/hardware/bluetooth/1.1/IBluetoothHci.h>
 
 #include <hidl/MQDescriptor.h>
 
@@ -28,7 +29,7 @@
 namespace android {
 namespace hardware {
 namespace bluetooth {
-namespace V1_0 {
+namespace V1_1 {
 namespace btlinux {
 
 using ::android::hardware::Return;
@@ -40,13 +41,20 @@
  public:
   BluetoothHci();
   Return<void> initialize(
-      const ::android::sp<IBluetoothHciCallbacks>& cb) override;
+      const ::android::sp<V1_0::IBluetoothHciCallbacks>& cb) override;
+  Return<void> initialize_1_1(
+      const ::android::sp<V1_1::IBluetoothHciCallbacks>& cb) override;
   Return<void> sendHciCommand(const hidl_vec<uint8_t>& packet) override;
   Return<void> sendAclData(const hidl_vec<uint8_t>& data) override;
   Return<void> sendScoData(const hidl_vec<uint8_t>& data) override;
+  Return<void> sendIsoData(const hidl_vec<uint8_t>& data) override;
   Return<void> close() override;
 
  private:
+  Return<void> initialize_impl(
+      const ::android::sp<V1_0::IBluetoothHciCallbacks>& cb,
+      const ::android::sp<V1_1::IBluetoothHciCallbacks>& cb_1_1);
+
   async::AsyncFdWatcher fd_watcher_;
   hci::H4Protocol* hci_handle_;
   int bt_soc_fd_;
@@ -55,6 +63,7 @@
   const uint8_t HCI_DATA_TYPE_COMMAND = 1;
   const uint8_t HCI_DATA_TYPE_ACL = 2;
   const uint8_t HCI_DATA_TYPE_SCO = 3;
+  const uint8_t HCI_DATA_TYPE_ISO = 5;
 
   int waitHciDev(int hci_interface);
   int findRfKill(void);
@@ -70,9 +79,9 @@
 extern "C" IBluetoothHci* HIDL_FETCH_IBluetoothHci(const char* name);
 
 }  // namespace btlinux
-}  // namespace V1_0
+}  // namespace V1_1
 }  // namespace bluetooth
 }  // namespace hardware
 }  // namespace android
 
-#endif  // HIDL_GENERATED_android_hardware_bluetooth_V1_0_BluetoothHci_H_
+#endif  // HIDL_GENERATED_android_hardware_bluetooth_V1_1_BluetoothHci_H_
diff --git a/vendor_libs/linux/interface/h4_protocol.cc b/vendor_libs/linux/interface/h4_protocol.cc
index e7acff4..e7c0f62 100644
--- a/vendor_libs/linux/interface/h4_protocol.cc
+++ b/vendor_libs/linux/interface/h4_protocol.cc
@@ -67,6 +67,9 @@
     case HCI_PACKET_TYPE_SCO_DATA:
       sco_cb_(hci_packetizer_.GetPacket());
       break;
+    case HCI_PACKET_TYPE_ISO_DATA:
+      iso_cb_(hci_packetizer_.GetPacket());
+      break;
     default: {
       bool bad_packet_type = true;
       CHECK(!bad_packet_type);
diff --git a/vendor_libs/linux/interface/h4_protocol.h b/vendor_libs/linux/interface/h4_protocol.h
index 612b0db..92d6698 100644
--- a/vendor_libs/linux/interface/h4_protocol.h
+++ b/vendor_libs/linux/interface/h4_protocol.h
@@ -33,11 +33,12 @@
 class H4Protocol {
  public:
   H4Protocol(int fd, PacketReadCallback event_cb, PacketReadCallback acl_cb,
-             PacketReadCallback sco_cb)
+             PacketReadCallback sco_cb, PacketReadCallback iso_cb)
       : uart_fd_(fd),
         event_cb_(event_cb),
         acl_cb_(acl_cb),
         sco_cb_(sco_cb),
+        iso_cb_(iso_cb),
         hci_packetizer_([this]() { OnPacketReady(); }) {}
 
   size_t Send(uint8_t type, const uint8_t* data, size_t length);
@@ -52,6 +53,7 @@
   PacketReadCallback event_cb_;
   PacketReadCallback acl_cb_;
   PacketReadCallback sco_cb_;
+  PacketReadCallback iso_cb_;
 
   HciPacketType hci_packet_type_{HCI_PACKET_TYPE_UNKNOWN};
   HciPacketizer hci_packetizer_;
diff --git a/vendor_libs/linux/interface/hci_internals.h b/vendor_libs/linux/interface/hci_internals.h
index 1e1f300..24e944f 100644
--- a/vendor_libs/linux/interface/hci_internals.h
+++ b/vendor_libs/linux/interface/hci_internals.h
@@ -24,7 +24,8 @@
   HCI_PACKET_TYPE_COMMAND = 1,
   HCI_PACKET_TYPE_ACL_DATA = 2,
   HCI_PACKET_TYPE_SCO_DATA = 3,
-  HCI_PACKET_TYPE_EVENT = 4
+  HCI_PACKET_TYPE_EVENT = 4,
+  HCI_PACKET_TYPE_ISO_DATA = 5,
 };
 
 // 2 bytes for opcode, 1 byte for parameter length (Volume 2, Part E, 5.4.1)
diff --git a/vendor_libs/linux/interface/service.cc b/vendor_libs/linux/interface/service.cc
index fac9ce0..4efd5e7 100644
--- a/vendor_libs/linux/interface/service.cc
+++ b/vendor_libs/linux/interface/service.cc
@@ -14,20 +14,20 @@
 // limitations under the License.
 //
 
-#define LOG_TAG "android.hardware.bluetooth@1.0-service.btlinux"
+#define LOG_TAG "android.hardware.bluetooth@1.1-service.btlinux"
 
-#include <android/hardware/bluetooth/1.0/IBluetoothHci.h>
+#include <android/hardware/bluetooth/1.1/IBluetoothHci.h>
 #include <hidl/HidlSupport.h>
 #include <hidl/HidlTransportSupport.h>
 #include <utils/Log.h>
 
 #include "bluetooth_hci.h"
 
-using ::android::hardware::configureRpcThreadpool;
-using ::android::hardware::bluetooth::V1_0::IBluetoothHci;
-using ::android::hardware::bluetooth::V1_0::btlinux::BluetoothHci;
-using ::android::hardware::joinRpcThreadpool;
 using ::android::sp;
+using ::android::hardware::configureRpcThreadpool;
+using ::android::hardware::joinRpcThreadpool;
+using ::android::hardware::bluetooth::V1_1::IBluetoothHci;
+using ::android::hardware::bluetooth::V1_1::btlinux::BluetoothHci;
 
 int main(int /* argc */, char** /* argv */) {
   sp<IBluetoothHci> bluetooth = new BluetoothHci;
diff --git a/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.cc b/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.cc
index 587536a..d4974a0 100644
--- a/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.cc
+++ b/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.cc
@@ -269,6 +269,11 @@
   }
 }
 
+void DualModeController::HandleIso(
+    std::shared_ptr<std::vector<uint8_t>> /* packet */) {
+  // TODO: implement handling similar to HandleSco
+}
+
 void DualModeController::HandleCommand(std::shared_ptr<std::vector<uint8_t>> packet) {
   auto command_packet = packets::CommandPacketView::Create(packet);
   uint16_t opcode = command_packet.GetOpcode();
@@ -319,6 +324,13 @@
   send_sco_ = callback;
 }
 
+void DualModeController::RegisterIsoChannel(
+    const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>&
+        callback) {
+  link_layer_controller_.RegisterIsoChannel(callback);
+  send_iso_ = callback;
+}
+
 void DualModeController::HciReset(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 0, "%s  size=%zu", __func__, args.size());
   link_layer_controller_.Reset();
diff --git a/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.h b/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.h
index e695848..b2ec70a 100644
--- a/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.h
+++ b/vendor_libs/test_vendor_lib/model/controller/dual_mode_controller.h
@@ -72,6 +72,7 @@
   void HandleAcl(std::shared_ptr<std::vector<uint8_t>> acl_packet);
   void HandleCommand(std::shared_ptr<std::vector<uint8_t>> command_packet);
   void HandleSco(std::shared_ptr<std::vector<uint8_t>> sco_packet);
+  void HandleIso(std::shared_ptr<std::vector<uint8_t>> iso_packet);
 
   // Set the callbacks for scheduling tasks.
   void RegisterTaskScheduler(std::function<AsyncTaskId(std::chrono::milliseconds, const TaskCallback&)> evtScheduler);
@@ -91,6 +92,10 @@
 
   void RegisterScoChannel(const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>& send_sco);
 
+  void RegisterIsoChannel(
+      const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>&
+          send_iso);
+
   // Set the device's address.
   void SetAddress(Address address) override;
 
@@ -434,6 +439,7 @@
   std::function<void(std::shared_ptr<bluetooth::hci::EventPacketBuilder>)>
       send_event_;
   std::function<void(std::shared_ptr<std::vector<uint8_t>>)> send_sco_;
+  std::function<void(std::shared_ptr<std::vector<uint8_t>>)> send_iso_;
 
   // Maintains the commands to be registered and used in the HciHandler object.
   // Keys are command opcodes and values are the callbacks to handle each
diff --git a/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.cc b/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.cc
index b81be35..b77f2ce 100644
--- a/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.cc
+++ b/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.cc
@@ -898,6 +898,12 @@
   send_sco_ = callback;
 }
 
+void LinkLayerController::RegisterIsoChannel(
+    const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>&
+        callback) {
+  send_iso_ = callback;
+}
+
 void LinkLayerController::RegisterRemoteChannel(
     const std::function<void(
         std::shared_ptr<model::packets::LinkLayerPacketBuilder>, Phy::Type)>&
diff --git a/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.h b/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.h
index 36aa671..ffc9044 100644
--- a/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.h
+++ b/vendor_libs/test_vendor_lib/model/controller/link_layer_controller.h
@@ -112,6 +112,10 @@
 
   void RegisterScoChannel(const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>& send_sco);
 
+  void RegisterIsoChannel(
+      const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>&
+          send_iso);
+
   void RegisterRemoteChannel(
       const std::function<void(
           std::shared_ptr<model::packets::LinkLayerPacketBuilder>, Phy::Type)>&
@@ -308,6 +312,7 @@
   std::function<void(std::shared_ptr<bluetooth::hci::EventPacketBuilder>)>
       send_event_;
   std::function<void(std::shared_ptr<std::vector<uint8_t>>)> send_sco_;
+  std::function<void(std::shared_ptr<std::vector<uint8_t>>)> send_iso_;
 
   // Callback to send packets to remote devices.
   std::function<void(std::shared_ptr<model::packets::LinkLayerPacketBuilder>,
diff --git a/vendor_libs/test_vendor_lib/packets/link_layer_packets.pdl b/vendor_libs/test_vendor_lib/packets/link_layer_packets.pdl
index a7e6614..0c27d97 100644
--- a/vendor_libs/test_vendor_lib/packets/link_layer_packets.pdl
+++ b/vendor_libs/test_vendor_lib/packets/link_layer_packets.pdl
@@ -6,26 +6,38 @@
 enum PacketType : 8 {
     UNKNOWN = 0x00,
     ACL = 0x01,
-    COMMAND = 0x02,
-    DISCONNECT = 0x03,
-    ENCRYPT_CONNECTION = 0x04,
-    ENCRYPT_CONNECTION_RESPONSE = 0x05,
-    EVENT = 0x06,
-    INQUIRY = 0x07,
-    INQUIRY_RESPONSE = 0x08,
-    IO_CAPABILITY_REQUEST = 0x09,
-    IO_CAPABILITY_RESPONSE = 0x0A,
-    IO_CAPABILITY_NEGATIVE_RESPONSE = 0x0B,
-    LE_ADVERTISEMENT = 0x0C,
-    LE_CONNECT = 0x0D,
-    LE_CONNECT_COMPLETE = 0x0E,
-    LE_SCAN = 0x0F,
-    LE_SCAN_RESPONSE = 0x10,
-    PAGE = 0x11,
-    PAGE_RESPONSE = 0x12,
-    PAGE_REJECT = 0x13,
-    RESPONSE = 0x14,
-    SCO = 0x15,
+    DISCONNECT = 0x02,
+    ENCRYPT_CONNECTION = 0x03,
+    ENCRYPT_CONNECTION_RESPONSE = 0x04,
+    EVENT = 0x05,
+    INQUIRY = 0x06,
+    INQUIRY_RESPONSE = 0x07,
+    IO_CAPABILITY_REQUEST = 0x08,
+    IO_CAPABILITY_RESPONSE = 0x09,
+    IO_CAPABILITY_NEGATIVE_RESPONSE = 0x0A,
+    LE_ADVERTISEMENT = 0x0B,
+    LE_CONNECT = 0x0C,
+    LE_CONNECT_COMPLETE = 0x0D,
+    LE_SCAN = 0x0E,
+    LE_SCAN_RESPONSE = 0x0F,
+    PAGE = 0x10,
+    PAGE_RESPONSE = 0x11,
+    PAGE_REJECT = 0x12,
+    READ_CLOCK_OFFSET = 0x13,
+    READ_CLOCK_OFFSET_RESPONSE = 0x14,
+    READ_REMOTE_SUPPORTED_FEATURES = 0x15,
+    READ_REMOTE_SUPPORTED_FEATURES_RESPONSE = 0x16,
+    READ_REMOTE_LMP_FEATURES = 0x17,
+    READ_REMOTE_LMP_FEATURES_RESPONSE = 0x18,
+    READ_REMOTE_EXTENDED_FEATURES = 0x19,
+    READ_REMOTE_EXTENDED_FEATURES_RESPONSE = 0x1A,
+    READ_REMOTE_VERSION_INFORMATION = 0x1B,
+    READ_REMOTE_VERSION_INFORMATION_RESPONSE = 0x1C,
+    REMOTE_NAME_REQUEST = 0x1D,
+    REMOTE_NAME_REQUEST_RESPONSE = 0x1E,
+    SCO = 0x1F,
+    COMMAND = 0x23, // Remove
+    RESPONSE = 0x24, // Remove
 }
 
 packet LinkLayerPacket {
@@ -35,11 +47,16 @@
   _body_,
 }
 
-packet AclPacket : LinkLayerPacket (type = ACL) {
+packet Command : LinkLayerPacket (type = COMMAND) {
   _payload_,
 }
 
-packet Command : LinkLayerPacket (type = COMMAND) {
+packet Response : LinkLayerPacket (type = RESPONSE) {
+  opcode : 16,
+  _payload_,
+}
+
+packet AclPacket : LinkLayerPacket (type = ACL) {
   _payload_,
 }
 
@@ -165,7 +182,56 @@
   reason : 8,
 }
 
-packet Response : LinkLayerPacket (type = RESPONSE) {
-  opcode : 16,
+packet ReadClockOffset : LinkLayerPacket (type = READ_CLOCK_OFFSET) {
+}
+
+packet ReadClockOffsetResponse : LinkLayerPacket (type = READ_CLOCK_OFFSET_RESPONSE) {
+  offset : 16,
+}
+
+packet ReadRemoteSupportedFeatures : LinkLayerPacket (type = READ_REMOTE_SUPPORTED_FEATURES) {
+}
+
+packet ReadRemoteSupportedFeaturesResponse : LinkLayerPacket (type = READ_REMOTE_SUPPORTED_FEATURES_RESPONSE) {
+  features : 64,
+}
+
+packet ReadRemoteLmpFeatures : LinkLayerPacket (type = READ_REMOTE_LMP_FEATURES) {
+}
+
+packet ReadRemoteLmpFeaturesResponse : LinkLayerPacket (type = READ_REMOTE_LMP_FEATURES_RESPONSE) {
+  features : 64,
+}
+
+packet ReadRemoteExtendedFeatures : LinkLayerPacket (type = READ_REMOTE_EXTENDED_FEATURES) {
+  page_number : 8,
+}
+
+packet ReadRemoteExtendedFeaturesResponse : LinkLayerPacket (type = READ_REMOTE_EXTENDED_FEATURES_RESPONSE) {
+  status : 8,
+  page_number : 8,
+  max_page_number : 8,
+  features : 64,
+}
+
+packet ReadRemoteVersionInformation : LinkLayerPacket (type = READ_REMOTE_VERSION_INFORMATION) {
+}
+
+packet ReadRemoteVersionInformationResponse : LinkLayerPacket (type = READ_REMOTE_VERSION_INFORMATION_RESPONSE) {
+  lmp_version : 8,
+  lmp_subversion : 8,
+  manufacturer_name : 16,
+}
+
+packet RemoteNameRequest : LinkLayerPacket (type = REMOTE_NAME_REQUEST) {
+}
+
+packet RemoteNameRequestResponse : LinkLayerPacket (type = REMOTE_NAME_REQUEST_RESPONSE) {
+  _size_(name) : 8,
+  name : 8[],
+}
+
+packet ScoPacket : LinkLayerPacket (type = SCO) {
   _payload_,
 }
+