Snap for 6029552 from 58f29a3694432f641b98dc111221c9d02ee35d21 to rvc-release

Change-Id: Ie1823c8c9422ca6b15839929e1a5ea1a69b35451
diff --git a/btif/Android.bp b/btif/Android.bp
index 6be1d81..8e55de6 100644
--- a/btif/Android.bp
+++ b/btif/Android.bp
@@ -24,8 +24,8 @@
     "system/bt/utils/include",
     "system/bt/include",
     "system/libhwbinder/include",
-    "system/security/keystore/include",
-    "hardware/interfaces/keymaster/4.0/support/include",
+    //"system/security/keystore/include",
+    //"hardware/interfaces/keymaster/4.0/support/include",
 ]
 
 // libbtif static library for target
@@ -73,7 +73,7 @@
         "src/btif_hf_client.cc",
         "src/btif_hh.cc",
         "src/btif_hd.cc",
-        "src/btif_keystore.cc",
+        //"src/btif_keystore.cc",
         "src/btif_mce.cc",
         "src/btif_pan.cc",
         "src/btif_profile_queue.cc",
@@ -107,12 +107,12 @@
         "libhidlbase",
         "libutils",
         "libcrypto",
-        "android.hardware.keymaster@4.0",
-        "android.hardware.keymaster@3.0",
-        "libkeymaster4support",
-        "libkeystore_aidl",
-        "libkeystore_binder",
-        "libkeystore_parcelables",
+        //"android.hardware.keymaster@4.0",
+        //"android.hardware.keymaster@3.0",
+        //"libkeymaster4support",
+        //"libkeystore_aidl",
+        //"libkeystore_binder",
+        //"libkeystore_parcelables",
     ],
     whole_static_libs: [
         "avrcp-target-service",
@@ -135,7 +135,7 @@
     include_dirs: btifCommonIncludes,
     srcs: [
         "test/btif_storage_test.cc",
-        "test/btif_keystore_test.cc"
+        //"test/btif_keystore_test.cc"
     ],
     header_libs: ["libbluetooth_headers"],
     shared_libs: [
@@ -150,13 +150,13 @@
         "libprocessgroup",
         "libutils",
         "libcrypto",
-        "android.hardware.keymaster@4.0",
-        "android.hardware.keymaster@3.0",
-        "libkeymaster4support",
-        "libkeystore_aidl",
-        "libkeystore_binder",
-        "libkeystore_parcelables",
-        "libbinder",
+        //"android.hardware.keymaster@4.0",
+        //"android.hardware.keymaster@3.0",
+        //"libkeymaster4support",
+        //"libkeystore_aidl",
+        //"libkeystore_binder",
+        //"libkeystore_parcelables",
+        //"libbinder",
     ],
     static_libs: [
         "libbt-bta",
diff --git a/btif/co/bta_av_co.cc b/btif/co/bta_av_co.cc
index 0621bc8..f3d39e2 100644
--- a/btif/co/bta_av_co.cc
+++ b/btif/co/bta_av_co.cc
@@ -1005,6 +1005,11 @@
     memcpy(p_codec_info, p_peer->codec_config, AVDT_CODEC_SIZE);
   }
 
+  // report this peer selectable codecs after retrieved all its capabilities.
+  LOG(INFO) << __func__ << ": retrieved " << +p_peer->num_rx_sinks
+            << " capabilities from peer " << p_peer->addr;
+  ReportSourceCodecState(p_peer);
+
   return A2DP_SUCCESS;
 }
 
@@ -1402,8 +1407,7 @@
 }
 
 bool BtaAvCo::SetActivePeer(const RawAddress& peer_address) {
-  APPL_TRACE_DEBUG("%s: peer_address=%s", __func__,
-                   peer_address.ToString().c_str());
+  VLOG(1) << __func__ << ": peer_address=" << peer_address;
 
   std::lock_guard<std::recursive_mutex> lock(codec_lock_);
 
@@ -1422,8 +1426,8 @@
 
   active_peer_ = p_peer;
   memcpy(codec_config_, active_peer_->codec_config, AVDT_CODEC_SIZE);
-  APPL_TRACE_DEBUG("%s: codec = %s", __func__,
-                   A2DP_CodecInfoString(codec_config_).c_str());
+  LOG(INFO) << __func__ << ": codec = " << A2DP_CodecInfoString(codec_config_);
+  // report the selected codec configuration of this new active peer.
   ReportSourceCodecState(active_peer_);
   return true;
 }
@@ -1476,12 +1480,12 @@
   bool config_updated = false;
   bool success = true;
 
-  VLOG(1) << __func__ << ": peer_address=" << peer_address.ToString()
-          << " codec_user_config=" << codec_user_config.ToString();
+  VLOG(1) << __func__ << ": peer_address=" << peer_address
+          << " codec_user_config={" << codec_user_config.ToString() << "}";
 
   BtaAvCoPeer* p_peer = FindPeer(peer_address);
   if (p_peer == nullptr) {
-    LOG(ERROR) << __func__ << ": cannot find peer " << peer_address.ToString()
+    LOG(ERROR) << __func__ << ": cannot find peer " << peer_address
                << " to configure";
     success = false;
     goto done;
@@ -1490,7 +1494,7 @@
   // Don't call BTA_AvReconfig() prior to retrieving all peer's capabilities
   if ((p_peer->num_rx_sinks != p_peer->num_sinks) &&
       (p_peer->num_sup_sinks != BTA_AV_CO_NUM_ELEMENTS(p_peer->sinks))) {
-    LOG(WARNING) << __func__ << ": peer " << p_peer->addr.ToString()
+    LOG(WARNING) << __func__ << ": peer " << p_peer->addr
                  << " : not all peer's capabilities have been retrieved";
     success = false;
     goto done;
@@ -1504,7 +1508,7 @@
     p_sink = p_peer->p_sink;
   }
   if (p_sink == nullptr) {
-    LOG(ERROR) << __func__ << ": peer " << p_peer->addr.ToString()
+    LOG(ERROR) << __func__ << ": peer " << p_peer->addr
                << " : cannot find peer SEP to configure for codec type "
                << codec_user_config.codec_type;
     success = false;
@@ -1529,7 +1533,7 @@
 
     p_sink = SelectSourceCodec(p_peer);
     if (p_sink == nullptr) {
-      LOG(ERROR) << __func__ << ": peer " << p_peer->addr.ToString()
+      LOG(ERROR) << __func__ << ": peer " << p_peer->addr
                  << " : cannot set up codec for the peer SINK";
       success = false;
       goto done;
@@ -1543,12 +1547,16 @@
   }
 
 done:
-  // NOTE: We unconditionally send the upcall even if there is no change
-  // or the user config failed. Thus, the caller would always know whether the
-  // request succeeded or failed.
+  // We send the upcall if there is no change or the user config failed for
+  // current active peer, so the caller would know it failed. If there is no
+  // error, the new selected codec configuration would be sent after we are
+  // ready to start a new session with the audio HAL.
+  // For none active peer, we unconditionally send the upcall, so the caller
+  // would always know the result.
   // NOTE: Currently, the input is restarted by sending an upcall
   // and informing the Media Framework about the change.
-  if (p_peer != nullptr) {
+  if (p_peer != nullptr &&
+      (!restart_output || !success || p_peer != active_peer_)) {
     return ReportSourceCodecState(p_peer);
   }
 
@@ -1574,7 +1582,7 @@
   // Don't call BTA_AvReconfig() prior to retrieving all peer's capabilities
   if ((p_peer->num_rx_sinks != p_peer->num_sinks) &&
       (p_peer->num_sup_sinks != BTA_AV_CO_NUM_ELEMENTS(p_peer->sinks))) {
-    LOG(WARNING) << __func__ << ": peer " << p_peer->addr.ToString()
+    LOG(WARNING) << __func__ << ": peer " << p_peer->addr
                  << " : not all peer's capabilities have been retrieved";
     return false;
   }
@@ -1582,7 +1590,7 @@
   // Use the current sink codec
   const BtaAvCoSep* p_sink = p_peer->p_sink;
   if (p_sink == nullptr) {
-    LOG(ERROR) << __func__ << ": peer " << p_peer->addr.ToString()
+    LOG(ERROR) << __func__ << ": peer " << p_peer->addr
                << " : cannot find peer SEP to configure";
     return false;
   }
@@ -1613,7 +1621,7 @@
 
   if (config_updated) {
     // NOTE: Currently, the input is restarted by sending an upcall
-    // and informing the Media Framework about the change.
+    // and informing the Media Framework about the change of selected codec.
     return ReportSourceCodecState(p_peer);
   }
 
@@ -1625,22 +1633,19 @@
   std::vector<btav_a2dp_codec_config_t> codecs_local_capabilities;
   std::vector<btav_a2dp_codec_config_t> codecs_selectable_capabilities;
 
-  APPL_TRACE_DEBUG("%s: peer_address=%s", __func__,
-                   p_peer->addr.ToString().c_str());
+  VLOG(1) << __func__ << ": peer_address=" << p_peer->addr;
   A2dpCodecs* codecs = p_peer->GetCodecs();
   CHECK(codecs != nullptr);
   if (!codecs->getCodecConfigAndCapabilities(&codec_config,
                                              &codecs_local_capabilities,
                                              &codecs_selectable_capabilities)) {
-    APPL_TRACE_WARNING(
-        "%s: Peer %s : error reporting audio source codec state: "
-        "cannot get codec config and capabilities",
-        __func__, p_peer->addr.ToString().c_str());
+    LOG(WARNING) << __func__ << ": Peer " << p_peer->addr
+                 << " : error reporting audio source codec state: cannot get "
+                    "codec config and capabilities";
     return false;
   }
-  APPL_TRACE_DEBUG("%s: peer %s codec_config=%s", __func__,
-                   p_peer->addr.ToString().c_str(),
-                   codec_config.ToString().c_str());
+  LOG(INFO) << __func__ << ": peer " << p_peer->addr << " codec_config={"
+            << codec_config.ToString() << "}";
   btif_av_report_source_codec_state(p_peer->addr, codec_config,
                                     codecs_local_capabilities,
                                     codecs_selectable_capabilities);
@@ -1747,19 +1752,14 @@
 
   // Select the codec
   for (const auto& iter : p_peer->GetCodecs()->orderedSourceCodecs()) {
-    APPL_TRACE_DEBUG("%s: trying codec %s", __func__, iter->name().c_str());
+    VLOG(1) << __func__ << ": trying codec " << iter->name();
     p_sink = AttemptSourceCodecSelection(*iter, p_peer);
     if (p_sink != nullptr) {
-      APPL_TRACE_DEBUG("%s: selected codec %s", __func__, iter->name().c_str());
+      VLOG(1) << __func__ << ": selected codec " << iter->name();
       break;
     }
-    APPL_TRACE_DEBUG("%s: cannot use codec %s", __func__, iter->name().c_str());
+    VLOG(1) << __func__ << ": cannot use codec " << iter->name();
   }
-
-  // NOTE: Unconditionally dispatch the event to make sure a callback with
-  // the most recent codec info is generated.
-  ReportSourceCodecState(p_peer);
-
   return p_sink;
 }
 
@@ -1999,10 +1999,8 @@
   bool restart_output = false;
   bool config_updated = false;
 
-  APPL_TRACE_DEBUG("%s: peer_address=%s", __func__,
-                   p_peer->addr.ToString().c_str());
-  APPL_TRACE_DEBUG("%s: codec: %s", __func__,
-                   A2DP_CodecInfoString(p_ota_codec_config).c_str());
+  LOG(INFO) << __func__ << ": peer_address=" << p_peer->addr
+            << ", codec: " << A2DP_CodecInfoString(p_ota_codec_config);
 
   *p_restart_output = false;
 
@@ -2013,8 +2011,8 @@
     // There are no peer SEPs if we didn't do the discovery procedure yet.
     // We have all the information we need from the peer, so we can
     // proceed with the OTA codec configuration.
-    APPL_TRACE_ERROR("%s: peer %s : cannot find peer SEP to configure",
-                     __func__, p_peer->addr.ToString().c_str());
+    LOG(ERROR) << __func__ << ": peer " << p_peer->addr
+               << " : cannot find peer SEP to configure";
     return false;
   }
 
@@ -2023,15 +2021,14 @@
   if (!p_peer->GetCodecs()->setCodecOtaConfig(
           p_ota_codec_config, &peer_params, result_codec_config, &restart_input,
           &restart_output, &config_updated)) {
-    APPL_TRACE_ERROR("%s: peer %s : cannot set OTA config", __func__,
-                     p_peer->addr.ToString().c_str());
+    LOG(ERROR) << __func__ << ": peer " << p_peer->addr
+               << " : cannot set OTA config";
     return false;
   }
 
   if (restart_output) {
-    APPL_TRACE_DEBUG("%s: restart output", __func__);
-    APPL_TRACE_DEBUG("%s: codec: %s", __func__,
-                     A2DP_CodecInfoString(result_codec_config).c_str());
+    VLOG(1) << __func__ << ": restart output for codec: "
+            << A2DP_CodecInfoString(result_codec_config);
 
     *p_restart_output = true;
     p_peer->p_sink = p_sink;
@@ -2041,7 +2038,7 @@
 
   if (restart_input || config_updated) {
     // NOTE: Currently, the input is restarted by sending an upcall
-    // and informing the Media Framework about the change.
+    // and informing the Media Framework about the change of selected codec.
     ReportSourceCodecState(p_peer);
   }
 
diff --git a/btif/src/btif_config.cc b/btif/src/btif_config.cc
index be006ab..b1af0ba 100644
--- a/btif/src/btif_config.cc
+++ b/btif/src/btif_config.cc
@@ -38,7 +38,7 @@
 #include "btif_api.h"
 #include "btif_common.h"
 #include "btif_config_transcode.h"
-#include "btif_keystore.h"
+//#include "btif_keystore.h"
 #include "btif_util.h"
 #include "common/address_obfuscator.h"
 #include "osi/include/alarm.h"
@@ -58,15 +58,15 @@
 #define DISABLED "disabled"
 static const char* TIME_STRING_FORMAT = "%Y-%m-%d %H:%M:%S";
 
-constexpr int kBufferSize = 400 * 10;  // initial file is ~400B
+// constexpr int kBufferSize = 400 * 10;  // initial file is ~400B
 
-static bool use_key_attestation() {
+/*static bool use_key_attestation() {
   return getuid() == AID_BLUETOOTH && is_single_user_mode();
-}
+}*/
 
 #define BT_CONFIG_METRICS_SECTION "Metrics"
 #define BT_CONFIG_METRICS_SALT_256BIT "Salt256Bit"
-using bluetooth::BtifKeystore;
+// using bluetooth::BtifKeystore;
 using bluetooth::common::AddressObfuscator;
 
 // TODO(armansito): Find a better way than searching by a hardcoded path.
@@ -93,9 +93,10 @@
 static std::unique_ptr<config_t> btif_config_open(const char* filename, const char* checksum_filename);
 
 // Key attestation
-static std::string hash_file(const char* filename);
-static std::string read_checksum_file(const char* filename);
-static void write_checksum_file(const char* filename, const std::string& hash);
+// static std::string hash_file(const char* filename);
+// static std::string read_checksum_file(const char* filename);
+// static void write_checksum_file(const char* filename, const std::string&
+// hash);
 
 static enum ConfigSource {
   NOT_LOADED,
@@ -176,7 +177,7 @@
 static std::unique_ptr<config_t> config;
 static alarm_t* config_timer;
 
-static BtifKeystore btif_keystore(new keystore::KeystoreClientImpl);
+// static BtifKeystore btif_keystore(new keystore::KeystoreClientImpl);
 
 // Module lifecycle functions
 
@@ -184,6 +185,9 @@
   std::unique_lock<std::recursive_mutex> lock(config_lock);
 
   if (is_factory_reset()) delete_config_files();
+  /*if (is_factory_reset() ||
+      (use_key_attestation() && !btif_keystore.DoesKeyExist()))
+    delete_config_files();*/
 
   std::string file_source;
 
@@ -262,7 +266,7 @@
 }
 
 static std::unique_ptr<config_t> btif_config_open(const char* filename, const char* checksum_filename) {
-  // START KEY ATTESTATION
+  /*// START KEY ATTESTATION
   // Get hash of current file
   std::string current_hash = hash_file(filename);
   // Get stored hash
@@ -278,7 +282,7 @@
   if (current_hash != stored_hash) {
     return nullptr;
   }
-  // END KEY ATTESTATION
+  // END KEY ATTESTATION*/
 
   std::unique_ptr<config_t> config = config_new(filename);
   if (!config) return nullptr;
@@ -512,11 +516,11 @@
   bool ret = config_save(*config, CONFIG_FILE_PATH);
   btif_config_source = RESET;
 
-  // Save encrypted hash
+  /*// Save encrypted hash
   std::string current_hash = hash_file(CONFIG_FILE_PATH);
   if (!current_hash.empty()) {
     write_checksum_file(CONFIG_FILE_CHECKSUM_PATH, current_hash);
-  }
+  }*/
 
   return ret;
 }
@@ -539,11 +543,11 @@
   std::unique_ptr<config_t> config_paired = config_new_clone(*config);
   btif_config_remove_unpaired(config_paired.get());
   config_save(*config_paired, CONFIG_FILE_PATH);
-  // Save hash
+  /*// Save hash
   std::string current_hash = hash_file(CONFIG_FILE_PATH);
   if (!current_hash.empty()) {
     write_checksum_file(CONFIG_FILE_CHECKSUM_PATH, current_hash);
-  }
+  }*/
 }
 
 static void btif_config_remove_unpaired(config_t* conf) {
@@ -635,12 +639,12 @@
 static void delete_config_files(void) {
   remove(CONFIG_FILE_PATH);
   remove(CONFIG_BACKUP_PATH);
-  remove(CONFIG_FILE_CHECKSUM_PATH);
-  remove(CONFIG_BACKUP_CHECKSUM_PATH);
+  // remove(CONFIG_FILE_CHECKSUM_PATH);
+  // remove(CONFIG_BACKUP_CHECKSUM_PATH);
   osi_property_set("persist.bluetooth.factoryreset", "false");
 }
 
-static std::string hash_file(const char* filename) {
+/*static std::string hash_file(const char* filename) {
   if (!use_key_attestation()) {
     LOG(INFO) << __func__ << ": Disabled for multi-user";
     return DISABLED;
@@ -696,4 +700,4 @@
       << __func__ << ": Failed encrypting checksum";
   CHECK(checksum_save(encrypted_checksum, checksum_filename))
       << __func__ << ": Failed to save checksum!";
-}
+}*/
diff --git a/gd/Android.bp b/gd/Android.bp
index 19eaec3..46108c6 100644
--- a/gd/Android.bp
+++ b/gd/Android.bp
@@ -363,7 +363,7 @@
     tools: [
         "bluetooth_packetgen",
     ],
-    cmd: "$(location bluetooth_packetgen) --include=system/bt/gd --out=$(genDir) $(in)",
+    cmd: "$(location bluetooth_packetgen) --include=system/bt/gd --out=$(genDir) --num_shards=5 $(in)",
     srcs: [
         "hci/hci_packets.pdl",
         "l2cap/l2cap_packets.pdl",
@@ -371,8 +371,23 @@
     ],
     out: [
         "hci/hci_packets_python3.cc",
+        "hci/hci_packets_python3_shard_0.cc",
+        "hci/hci_packets_python3_shard_1.cc",
+        "hci/hci_packets_python3_shard_2.cc",
+        "hci/hci_packets_python3_shard_3.cc",
+        "hci/hci_packets_python3_shard_4.cc",
         "l2cap/l2cap_packets_python3.cc",
+        "l2cap/l2cap_packets_python3_shard_0.cc",
+        "l2cap/l2cap_packets_python3_shard_1.cc",
+        "l2cap/l2cap_packets_python3_shard_2.cc",
+        "l2cap/l2cap_packets_python3_shard_3.cc",
+        "l2cap/l2cap_packets_python3_shard_4.cc",
         "security/smp_packets_python3.cc",
+        "security/smp_packets_python3_shard_0.cc",
+        "security/smp_packets_python3_shard_1.cc",
+        "security/smp_packets_python3_shard_2.cc",
+        "security/smp_packets_python3_shard_3.cc",
+        "security/smp_packets_python3_shard_4.cc",
     ],
 }
 
@@ -590,7 +605,11 @@
       linux_glibc_x86_64: {
           include_dirs: ["external/python/cpython3/android/linux_x86_64/pyconfig"],
           cflags: ["-DSOABI=\"cpython-38android-x86_64-linux-gnu\""],
-          suffix: ".cpython-38android-x86_64-linux-gnu",
+          // Commenting out the Linux suffix so that cpython-38-x86_64-linux-gnu
+          // Python 3.8 can also import the untagged .so library per PEP 3149
+          // Keep this change until Android py3-cmd can run ACTS, gRPC and can
+          // Export Python native symbols such as PyType_Type
+          // suffix: ".cpython-38android-x86_64-linux-gnu",
       },
       windows: {
           enabled: false,
diff --git a/gd/cert/bluetooth_packets_python3_setup.py b/gd/cert/bluetooth_packets_python3_setup.py
new file mode 100644
index 0000000..bce14f0
--- /dev/null
+++ b/gd/cert/bluetooth_packets_python3_setup.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python3
+#
+#   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.
+
+
+# Usage:
+# 1. Run envsetup and lunch first in an Android checkout
+# 2. Make target bluetooth_packets_python3 that will generate C++ sources for the
+#    Extension
+# 3. Build only:
+#       python3 bluetooth_packets_python3_setup.py build_ext
+#   Then Find the .so file in build/lib.linux-x86_64-3.X
+# 4. Install:
+#       python3 bluetooth_packets_python3_setup.py install --user
+
+
+import os
+import glob
+from setuptools import setup, Extension
+
+ANDROID_BUILD_TOP = os.getenv("ANDROID_BUILD_TOP")
+PYBIND11_INCLUDE_DIR = os.path.join(ANDROID_BUILD_TOP,
+                                    "external/python/pybind11/include")
+GD_DIR = os.path.join(ANDROID_BUILD_TOP, "system/bt/gd")
+BT_PACKETS_GEN_DIR = os.path.join(ANDROID_BUILD_TOP,
+                                  "out/soong/.intermediates/system/bt/gd/BluetoothGeneratedPackets_h/gen")
+BT_PACKETS_PY3_GEN_DIR = os.path.join(ANDROID_BUILD_TOP,
+                                      "out/soong/.intermediates/system/bt/gd/BluetoothGeneratedPackets_python3_cc/gen")
+
+BT_PACKETS_BASE_SRCS = [
+    os.path.join(GD_DIR, "l2cap/fcs.cc"),
+    os.path.join(GD_DIR, "packet/bit_inserter.cc"),
+    os.path.join(GD_DIR, "packet/byte_inserter.cc"),
+    os.path.join(GD_DIR, "packet/byte_observer.cc"),
+    os.path.join(GD_DIR, "packet/iterator.cc"),
+    os.path.join(GD_DIR, "packet/fragmenting_inserter.cc"),
+    os.path.join(GD_DIR, "packet/packet_view.cc"),
+    os.path.join(GD_DIR, "packet/raw_builder.cc"),
+    os.path.join(GD_DIR, "packet/view.cc"),
+]
+
+BT_PACKETS_PY3_SRCs = \
+  [os.path.join(GD_DIR, "packet/python3_module.cc")] \
+  + glob.glob(os.path.join(BT_PACKETS_PY3_GEN_DIR, "hci", "*.cc")) \
+  + glob.glob(os.path.join(BT_PACKETS_PY3_GEN_DIR, "l2cap", "*.cc")) \
+  + glob.glob(os.path.join(BT_PACKETS_PY3_GEN_DIR, "security", "*.cc"))
+
+bluetooth_packets_python3_module = Extension('bluetooth_packets_python3',
+                                             sources=BT_PACKETS_BASE_SRCS + BT_PACKETS_PY3_SRCs,
+                                             include_dirs=[GD_DIR,
+                                                           BT_PACKETS_GEN_DIR,
+                                                           BT_PACKETS_PY3_GEN_DIR,
+                                                           PYBIND11_INCLUDE_DIR],
+                                             extra_compile_args=['-std=c++17']
+                                             )
+
+setup(name='bluetooth_packets_python3',
+      version='1.0',
+      author="Android Open Source Project",
+      description="""Bluetooth Packet Library""",
+      ext_modules=[bluetooth_packets_python3_module],
+      py_modules=["bluetooth_packets_python3"],
+      )
diff --git a/gd/cert/gd_cert_device.py b/gd/cert/gd_cert_device.py
index b730b50..b579258 100644
--- a/gd/cert/gd_cert_device.py
+++ b/gd/cert/gd_cert_device.py
@@ -69,7 +69,7 @@
         self.hal = hal_cert_pb2_grpc.HciHalCertStub(self.grpc_channel)
         self.controller_read_only_property = cert_rootservice_pb2_grpc.ReadOnlyPropertyStub(self.grpc_channel)
         self.hci = hci_cert_pb2_grpc.AclManagerCertStub(self.grpc_channel)
-        self.l2cap = l2cap_cert_pb2_grpc.L2capModuleCertStub(self.grpc_channel)
+        self.l2cap = l2cap_cert_pb2_grpc.L2capClassicModuleCertStub(self.grpc_channel)
 
         # Event streams
         self.hal.hci_event_stream = EventStream(self.hal.FetchHciEvent)
diff --git a/gd/cert/gd_device.py b/gd/cert/gd_device.py
index 2cd49b7..17454f3 100644
--- a/gd/cert/gd_device.py
+++ b/gd/cert/gd_device.py
@@ -70,7 +70,7 @@
         self.controller_read_only_property = facade_rootservice_pb2_grpc.ReadOnlyPropertyStub(self.grpc_channel)
         self.hci = hci_facade_pb2_grpc.AclManagerFacadeStub(self.grpc_channel)
         self.hci_classic_security = hci_facade_pb2_grpc.ClassicSecurityManagerFacadeStub(self.grpc_channel)
-        self.l2cap = l2cap_facade_pb2_grpc.L2capModuleFacadeStub(self.grpc_channel)
+        self.l2cap = l2cap_facade_pb2_grpc.L2capClassicModuleFacadeStub(self.grpc_channel)
 
         # Event streams
         self.hal.hci_event_stream = EventStream(self.hal.FetchHciEvent)
diff --git a/gd/cert/grpc_root_server.cc b/gd/cert/grpc_root_server.cc
index 7281867..48b2d5a 100644
--- a/gd/cert/grpc_root_server.cc
+++ b/gd/cert/grpc_root_server.cc
@@ -59,7 +59,7 @@
         break;
       case BluetoothModule::L2CAP:
         modules.add<::bluetooth::cert::ReadOnlyPropertyServerModule>();
-        modules.add<::bluetooth::l2cap::classic::cert::L2capModuleCertModule>();
+        modules.add<::bluetooth::l2cap::classic::cert::L2capClassicModuleCertModule>();
         break;
       default:
         return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "invalid module under test");
diff --git a/gd/cert/run_cert.sh b/gd/cert/run_cert.sh
index 81fe91e..a85b994 100755
--- a/gd/cert/run_cert.sh
+++ b/gd/cert/run_cert.sh
@@ -1,3 +1,5 @@
 #! /bin/bash
 
-act.py -c $ANDROID_BUILD_TOP/system/bt/gd/cert/host_only_config.json -tf $ANDROID_BUILD_TOP/system/bt/gd/cert/cert_testcases -tp $ANDROID_BUILD_TOP/system/bt/gd
+# For bluetooth_packets_python3
+export PYTHONPATH=$PYTHONPATH:$ANDROID_BUILD_TOP/out/host/linux-x86/lib64
+python3.8 `which act.py` -c $ANDROID_BUILD_TOP/system/bt/gd/cert/host_only_config.json -tf $ANDROID_BUILD_TOP/system/bt/gd/cert/cert_testcases -tp $ANDROID_BUILD_TOP/system/bt/gd
diff --git a/gd/cert/run_device_cert.sh b/gd/cert/run_device_cert.sh
index 44e5a89..7566f65 100755
--- a/gd/cert/run_device_cert.sh
+++ b/gd/cert/run_device_cert.sh
@@ -1,3 +1,5 @@
 #! /bin/bash
 
-act.py -c $ANDROID_BUILD_TOP/system/bt/gd/cert/android_devices_config.json -tf $ANDROID_BUILD_TOP/system/bt/gd/cert/cert_testcases -tp $ANDROID_BUILD_TOP/system/bt/gd
+# For bluetooth_packets_python3
+export PYTHONPATH=$PYTHONPATH:$ANDROID_BUILD_TOP/out/host/linux-x86/lib64
+python3.8 `which act.py` -c $ANDROID_BUILD_TOP/system/bt/gd/cert/android_devices_config.json -tf $ANDROID_BUILD_TOP/system/bt/gd/cert/cert_testcases -tp $ANDROID_BUILD_TOP/system/bt/gd
diff --git a/gd/cert/set_up_acts.sh b/gd/cert/set_up_acts.sh
index c8fa38b..ad4e775 100755
--- a/gd/cert/set_up_acts.sh
+++ b/gd/cert/set_up_acts.sh
@@ -46,9 +46,15 @@
     popd
 }
 
-function SetupPython3 {
-    echo "Setting up python3"
-    sudo apt-get install python3-dev
+function SetupPython38 {
+    echo "Setting up python3.8"
+    sudo apt-get install python3.8-dev
+}
+
+function CompileBluetoothPacketsPython3 {
+    echo "bluetooth_packets_python3 is not found, compiling"
+    croot
+    make -j bluetooth_packets_python3
 }
 
 if [[ "${BASH_SOURCE[0]}" == "${0}" ]] ; then
@@ -60,16 +66,41 @@
     SetUpAndroidBuild
 fi
 
-## Check python3 is installed properly
-dpkg -l python3-dev > /dev/null 2>&1
+## Check python3.8 is installed properly
+## Need Python 3.8 because bluetooth_packets_python3 is compiled against
+## Python 3.8 headers
+dpkg -l python3.8-dev > /dev/null 2>&1
 if [[ $? -ne 0 ]] ; then
-    SetupPython3
+    SetupPython38
+fi
+
+## Check bluetooth_packets_python3 is compiled succssfully
+export PYTHONPATH=$PYTHONPATH:$ANDROID_BUILD_TOP/out/host/linux-x86/lib64
+python3.8 -c "
+import bluetooth_packets_python3 as bp3
+bp3.BaseStruct
+"
+if [[ $? -ne 0 ]] ; then
+  pushd .
+  CompileBluetoothPacketsPython3
+  popd
+  python3.8 -c "
+import bluetooth_packets_python3 as bp3
+bp3.BaseStruct
+"
+  if [[ $? -ne 0 ]] ; then
+    echo "Setup failed as bluetooth_packets_python3 cannot be found"
+  else
+    echo "Found bluetooth_packets_python3 after compilation"
+  fi
+else
+  echo "Found bluetooth_packets_python3"
 fi
 
 ## All is good now so go ahead with the acts setup
 pushd .
 cd $ANDROID_BUILD_TOP/tools/test/connectivity/acts/framework/
-sudo python3 setup.py develop
+sudo python3.8 setup.py develop
 if [[ $? -eq 0 ]] ; then
     echo "cert setup complete"
 else
diff --git a/gd/facade/grpc_root_server.cc b/gd/facade/grpc_root_server.cc
index 29ccc59..e802dfe 100644
--- a/gd/facade/grpc_root_server.cc
+++ b/gd/facade/grpc_root_server.cc
@@ -60,7 +60,7 @@
         break;
       case BluetoothModule::L2CAP:
         modules.add<::bluetooth::facade::ReadOnlyPropertyServerModule>();
-        modules.add<::bluetooth::l2cap::classic::L2capModuleFacadeModule>();
+        modules.add<::bluetooth::l2cap::classic::L2capClassicModuleFacadeModule>();
         break;
       default:
         return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, "invalid module under test");
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/hci/acl_manager.h b/gd/hci/acl_manager.h
index 27c4fb3..3706aba 100644
--- a/gd/hci/acl_manager.h
+++ b/gd/hci/acl_manager.h
@@ -151,7 +151,7 @@
  private:
   friend AclManager;
   AclConnection(const AclManager* manager, uint16_t handle, Address address)
-      : manager_(manager), handle_(handle), address_(address) {}
+      : manager_(manager), handle_(handle), address_(address), address_type_(AddressType::PUBLIC_DEVICE_ADDRESS) {}
   AclConnection(const AclManager* manager, uint16_t handle, Address address, AddressType address_type, Role role)
       : manager_(manager), handle_(handle), address_(address), address_type_(address_type), role_(role) {}
   const AclManager* manager_;
diff --git a/gd/hci/address.h b/gd/hci/address.h
index a8f515c..3bc507f 100644
--- a/gd/hci/address.h
+++ b/gd/hci/address.h
@@ -18,6 +18,7 @@
 
 #pragma once
 
+#include <cstring>
 #include <string>
 
 namespace bluetooth {
diff --git a/gd/hci/classic_security_manager.cc b/gd/hci/classic_security_manager.cc
index 8425ae6..07aae77 100644
--- a/gd/hci/classic_security_manager.cc
+++ b/gd/hci/classic_security_manager.cc
@@ -46,6 +46,8 @@
                                      Bind(&impl::on_request_event, common::Unretained(this)), handler_);
     hci_layer_->RegisterEventHandler(EventCode::ENCRYPTION_KEY_REFRESH_COMPLETE,
                                      Bind(&impl::on_complete_event, common::Unretained(this)), handler_);
+    hci_layer_->RegisterEventHandler(EventCode::LINK_KEY_NOTIFICATION,
+                                     Bind(&impl::on_link_key_notification, common::Unretained(this)), handler_);
   }
 
   void Stop() {
@@ -224,6 +226,12 @@
     LOG_DEBUG("receive complete event %d", (uint8_t)event_code);
   }
 
+  void on_link_key_notification(EventPacketView packet) {
+    auto view = LinkKeyNotificationView::Create(packet);
+    ASSERT(view.IsValid());
+    LOG_DEBUG("receive link key notification, key type %d", (uint8_t)view.GetKeyType());
+  }
+
   void on_command_complete(CommandCompleteView status) {
     if (client_handler_ != nullptr) {
       client_handler_->Post(common::BindOnce(&ClassicSecurityCommandCallbacks::OnCommandComplete,
diff --git a/gd/hci/hci_packets.pdl b/gd/hci/hci_packets.pdl
index b591ec9..c048c80 100644
--- a/gd/hci/hci_packets.pdl
+++ b/gd/hci/hci_packets.pdl
@@ -1530,7 +1530,7 @@
   authentication_enable : AuthenticationEnable,
 }
 
-packet WriteAuthenticationEnable : CommandPacket (op_code = WRITE_AUTHENTICATION_ENABLE) {
+packet WriteAuthenticationEnable : SecurityCommand (op_code = WRITE_AUTHENTICATION_ENABLE) {
   authentication_enable : AuthenticationEnable,
 }
 
@@ -1911,7 +1911,7 @@
   secure_connections_host_support : Enable,
 }
 
-packet WriteSecureConnectionsHostSupport : CommandPacket (op_code = WRITE_SECURE_CONNECTIONS_HOST_SUPPORT) {
+packet WriteSecureConnectionsHostSupport : SecurityCommand (op_code = WRITE_SECURE_CONNECTIONS_HOST_SUPPORT) {
   secure_connections_host_support : Enable,
 }
 
@@ -2655,12 +2655,17 @@
   _payload_,  // placeholder (unimplemented)
 }
 
-packet LeSetExtendedScanParameters : LeScanningCommand (op_code = LE_SET_EXTENDED_SCAN_PARAMETERS) {
+struct PhyScanParameters {
   le_scan_type : LeScanType,
-  le_scan_interval : 32, // 0x0004-0x00FFFFFF Default 0x10 (10ms)
-  le_scan_window : 32, // 0x004-0xFFFF Default 0x10 (10ms)
+  le_scan_interval : 16, // 0x0004-0xFFFF Default 0x10 (10ms)
+  le_scan_window : 16, // 0x004-0xFFFF Default 0x10 (10ms)
+}
+
+packet LeSetExtendedScanParameters : LeScanningCommand (op_code = LE_SET_EXTENDED_SCAN_PARAMETERS) {
   own_address_type : AddressType,
   scanning_filter_policy : LeSetScanningFilterPolicy,
+  scanning_phys : 8,
+  parameters : PhyScanParameters[],
 }
 
 packet LeSetExtendedScanParametersComplete : CommandComplete (command_op_code = LE_SET_EXTENDED_SCAN_PARAMETERS) {
diff --git a/gd/hci/hci_packets_test.cc b/gd/hci/hci_packets_test.cc
index 729513b..6eb0d13 100644
--- a/gd/hci/hci_packets_test.cc
+++ b/gd/hci/hci_packets_test.cc
@@ -262,5 +262,161 @@
 //  TODO: Revisit reflection tests for EIR
 // DEFINE_AND_INSTANTIATE_WriteExtendedInquiryResponseReflectionTest(pixel_3_xl_write_extended_inquiry_response,
 // pixel_3_xl_write_extended_inquiry_response_no_uuids);
+
+std::vector<uint8_t> le_set_scan_parameters{
+    0x0b, 0x20, 0x07, 0x01, 0x12, 0x00, 0x12, 0x00, 0x01, 0x00,
+};
+TEST(HciPacketsTest, testLeSetScanParameters) {
+  PacketView<kLittleEndian> packet_bytes_view(std::make_shared<std::vector<uint8_t>>(le_set_scan_parameters));
+  auto view =
+      LeSetScanParametersView::Create(LeScanningCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
+
+  ASSERT(view.IsValid());
+  ASSERT_EQ(LeScanType::ACTIVE, view.GetLeScanType());
+  ASSERT_EQ(0x12, view.GetLeScanInterval());
+  ASSERT_EQ(0x12, view.GetLeScanWindow());
+  ASSERT_EQ(AddressType::RANDOM_DEVICE_ADDRESS, view.GetOwnAddressType());
+  ASSERT_EQ(LeSetScanningFilterPolicy::ACCEPT_ALL, view.GetScanningFilterPolicy());
+}
+
+DEFINE_AND_INSTANTIATE_LeSetScanParametersReflectionTest(le_set_scan_parameters);
+
+std::vector<uint8_t> le_set_scan_enable{
+    0x0c, 0x20, 0x02, 0x01, 0x00,
+};
+TEST(HciPacketsTest, testLeSetScanEnable) {
+  PacketView<kLittleEndian> packet_bytes_view(std::make_shared<std::vector<uint8_t>>(le_set_scan_enable));
+  auto view = LeSetScanEnableView::Create(LeScanningCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
+
+  ASSERT(view.IsValid());
+  ASSERT_EQ(Enable::ENABLED, view.GetLeScanEnable());
+  ASSERT_EQ(Enable::DISABLED, view.GetFilterDuplicates());
+}
+
+DEFINE_AND_INSTANTIATE_LeSetScanEnableReflectionTest(le_set_scan_enable);
+
+std::vector<uint8_t> le_get_vendor_capabilities{
+    0x53,
+    0xfd,
+    0x00,
+};
+TEST(HciPacketsTest, testLeGetVendorCapabilities) {
+  PacketView<kLittleEndian> packet_bytes_view(std::make_shared<std::vector<uint8_t>>(le_get_vendor_capabilities));
+  auto view =
+      LeGetVendorCapabilitiesView::Create(VendorCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
+
+  ASSERT(view.IsValid());
+}
+
+DEFINE_AND_INSTANTIATE_LeGetVendorCapabilitiesReflectionTest(le_get_vendor_capabilities);
+
+std::vector<uint8_t> le_get_vendor_capabilities_complete{
+    0x0e, 0x0c, 0x01, 0x53, 0xfd, 0x00, 0x05, 0x01, 0x00, 0x04, 0x80, 0x01, 0x10, 0x01,
+};
+TEST(HciPacketsTest, testLeGetVendorCapabilitiesComplete) {
+  PacketView<kLittleEndian> packet_bytes_view(
+      std::make_shared<std::vector<uint8_t>>(le_get_vendor_capabilities_complete));
+  auto view = LeGetVendorCapabilitiesCompleteView::Create(
+      CommandCompleteView::Create(EventPacketView::Create(packet_bytes_view)));
+
+  ASSERT(view.IsValid());
+  auto base_capabilities = view.GetBaseVendorCapabilities();
+  ASSERT_EQ(5, base_capabilities.max_advt_instances_);
+  ASSERT_EQ(1, base_capabilities.offloaded_resolution_of_private_address_);
+  ASSERT_EQ(1024, base_capabilities.total_scan_results_storage_);
+  ASSERT_EQ(128, base_capabilities.max_irk_list_sz_);
+  ASSERT_EQ(1, base_capabilities.filtering_support_);
+  ASSERT_EQ(16, base_capabilities.max_filter_);
+  ASSERT_EQ(1, base_capabilities.activity_energy_info_support_);
+}
+
+DEFINE_AND_INSTANTIATE_LeGetVendorCapabilitiesCompleteReflectionTest(le_get_vendor_capabilities_complete);
+
+std::vector<uint8_t> le_set_extended_scan_parameters{
+    0x41, 0x20, 0x08, 0x01, 0x00, 0x01, 0x01, 0x12, 0x00, 0x12, 0x00,
+};
+
+TEST(HciPacketsTest, testLeSetExtendedScanParameters) {
+  PacketView<kLittleEndian> packet_bytes_view(std::make_shared<std::vector<uint8_t>>(le_set_extended_scan_parameters));
+  auto view = LeSetExtendedScanParametersView::Create(
+      LeScanningCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
+
+  ASSERT(view.IsValid());
+  ASSERT_EQ(1, view.GetScanningPhys());
+  auto params = view.GetParameters();
+  ASSERT_EQ(1, params.size());
+  ASSERT_EQ(LeScanType::ACTIVE, params[0].le_scan_type_);
+  ASSERT_EQ(18, params[0].le_scan_interval_);
+  ASSERT_EQ(18, params[0].le_scan_window_);
+}
+
+std::vector<uint8_t> le_set_extended_scan_parameters_6553{
+    0x41, 0x20, 0x08, 0x01, 0x00, 0x01, 0x01, 0x99, 0x19, 0x99, 0x19,
+};
+
+TEST(HciPacketsTest, testLeSetExtendedScanParameters_6553) {
+  PacketView<kLittleEndian> packet_bytes_view(
+      std::make_shared<std::vector<uint8_t>>(le_set_extended_scan_parameters_6553));
+  auto view = LeSetExtendedScanParametersView::Create(
+      LeScanningCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
+
+  ASSERT(view.IsValid());
+  ASSERT_EQ(1, view.GetScanningPhys());
+  auto params = view.GetParameters();
+  ASSERT_EQ(1, params.size());
+  ASSERT_EQ(LeScanType::ACTIVE, params[0].le_scan_type_);
+  ASSERT_EQ(6553, params[0].le_scan_interval_);
+  ASSERT_EQ(6553, params[0].le_scan_window_);
+}
+
+DEFINE_AND_INSTANTIATE_LeSetExtendedScanParametersReflectionTest(le_set_extended_scan_parameters,
+                                                                 le_set_extended_scan_parameters_6553);
+
+std::vector<uint8_t> le_set_extended_scan_parameters_complete{
+    0x0e, 0x04, 0x01, 0x41, 0x20, 0x00,
+};
+DEFINE_AND_INSTANTIATE_LeSetExtendedScanParametersCompleteReflectionTest(le_set_extended_scan_parameters_complete);
+
+std::vector<uint8_t> le_set_extended_scan_enable{
+    0x42, 0x20, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+TEST(HciPacketsTest, testLeSetExtendedScanEnable) {
+  PacketView<kLittleEndian> packet_bytes_view(std::make_shared<std::vector<uint8_t>>(le_set_extended_scan_enable));
+  auto view =
+      LeSetExtendedScanEnableView::Create(LeScanningCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
+
+  ASSERT(view.IsValid());
+  ASSERT_EQ(FilterDuplicates::DISABLED, view.GetFilterDuplicates());
+  ASSERT_EQ(Enable::ENABLED, view.GetEnable());
+  ASSERT_EQ(0, view.GetDuration());
+  ASSERT_EQ(0, view.GetPeriod());
+}
+
+std::vector<uint8_t> le_set_extended_scan_enable_disable{
+    0x42, 0x20, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+};
+
+TEST(HciPacketsTest, testLeSetExtendedScanEnableDisable) {
+  PacketView<kLittleEndian> packet_bytes_view(
+      std::make_shared<std::vector<uint8_t>>(le_set_extended_scan_enable_disable));
+  auto view =
+      LeSetExtendedScanEnableView::Create(LeScanningCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
+
+  ASSERT(view.IsValid());
+  ASSERT_EQ(FilterDuplicates::ENABLED, view.GetFilterDuplicates());
+  ASSERT_EQ(Enable::DISABLED, view.GetEnable());
+  ASSERT_EQ(0, view.GetDuration());
+  ASSERT_EQ(0, view.GetPeriod());
+}
+
+DEFINE_AND_INSTANTIATE_LeSetExtendedScanEnableReflectionTest(le_set_extended_scan_enable,
+                                                             le_set_extended_scan_enable_disable);
+
+std::vector<uint8_t> le_set_extended_scan_enable_complete{
+    0x0e, 0x04, 0x01, 0x42, 0x20, 0x00,
+};
+DEFINE_AND_INSTANTIATE_LeSetExtendedScanEnableCompleteReflectionTest(le_set_extended_scan_enable_complete);
+
 }  // namespace hci
 }  // namespace bluetooth
diff --git a/gd/hci/le_scanning_manager.cc b/gd/hci/le_scanning_manager.cc
index 0b1bb62..ad88b44 100644
--- a/gd/hci/le_scanning_manager.cc
+++ b/gd/hci/le_scanning_manager.cc
@@ -107,12 +107,19 @@
   }
 
   void configure_scan() {
+    std::vector<PhyScanParameters> parameter_vector;
+    PhyScanParameters phy_scan_parameters;
+    phy_scan_parameters.le_scan_window_ = 0;
+    phy_scan_parameters.le_scan_interval_ = 0;
+    phy_scan_parameters.le_scan_type_ = LeScanType::ACTIVE;
+    parameter_vector.push_back(phy_scan_parameters);
+    uint8_t phys_in_use = 1;
+
     switch (api_type_) {
       case ScanApiType::LE_5_0:
-        le_scanning_interface_->EnqueueCommand(
-            hci::LeSetExtendedScanParametersBuilder::Create(LeScanType::ACTIVE, interval_ms_, window_ms_,
-                                                            own_address_type_, filter_policy_),
-            common::BindOnce(impl::check_status), module_handler_);
+        le_scanning_interface_->EnqueueCommand(hci::LeSetExtendedScanParametersBuilder::Create(
+                                                   own_address_type_, filter_policy_, phys_in_use, parameter_vector),
+                                               common::BindOnce(impl::check_status), module_handler_);
         break;
       case ScanApiType::ANDROID_HCI:
         le_scanning_interface_->EnqueueCommand(
diff --git a/gd/hci/security_interface.h b/gd/hci/security_interface.h
index efb20d0..ea15aa0 100644
--- a/gd/hci/security_interface.h
+++ b/gd/hci/security_interface.h
@@ -43,6 +43,7 @@
       hci::EventCode::IO_CAPABILITY_REQUEST,     hci::EventCode::IO_CAPABILITY_RESPONSE,
       hci::EventCode::REMOTE_OOB_DATA_REQUEST,   hci::EventCode::SIMPLE_PAIRING_COMPLETE,
       hci::EventCode::USER_PASSKEY_NOTIFICATION, hci::EventCode::KEYPRESS_NOTIFICATION,
+      hci::EventCode::USER_CONFIRMATION_REQUEST, hci::EventCode::USER_PASSKEY_REQUEST,
   };
 };
 }  // namespace hci
diff --git a/gd/l2cap/Android.bp b/gd/l2cap/Android.bp
index 649f618..60175e2 100644
--- a/gd/l2cap/Android.bp
+++ b/gd/l2cap/Android.bp
@@ -19,6 +19,7 @@
         "classic/l2cap_classic_module.cc",
         "internal/basic_mode_channel_data_controller.cc",
         "internal/enhanced_retransmission_mode_channel_data_controller.cc",
+        "internal/le_credit_based_channel_data_controller.cc",
         "internal/receiver.cc",
         "internal/scheduler_fifo.cc",
         "internal/sender.cc",
@@ -42,7 +43,10 @@
         "classic/internal/fixed_channel_service_manager_test.cc",
         "classic/internal/link_manager_test.cc",
         "classic/internal/signalling_manager_test.cc",
+        "internal/basic_mode_channel_data_controller_test.cc",
+        "internal/enhanced_retransmission_mode_channel_data_controller_test.cc",
         "internal/fixed_channel_allocator_test.cc",
+        "internal/le_credit_based_channel_data_controller_test.cc",
         "internal/receiver_test.cc",
         "internal/scheduler_fifo_test.cc",
         "internal/sender_test.cc",
diff --git a/gd/l2cap/cid.h b/gd/l2cap/cid.h
index 9a21abb..729272c 100644
--- a/gd/l2cap/cid.h
+++ b/gd/l2cap/cid.h
@@ -36,5 +36,7 @@
 constexpr Cid kSmpCid = 6;
 constexpr Cid kSmpBrCid = 7;
 
+constexpr Cid kClassicPairingTriggerCid = kLastFixedChannel - 1;
+
 }  // namespace l2cap
 }  // namespace bluetooth
diff --git a/gd/l2cap/classic/cert/api.proto b/gd/l2cap/classic/cert/api.proto
index 8492323..a80e648 100644
--- a/gd/l2cap/classic/cert/api.proto
+++ b/gd/l2cap/classic/cert/api.proto
@@ -5,8 +5,10 @@
 import "google/protobuf/empty.proto";
 import "facade/common.proto";
 
-service L2capModuleCert {
+service L2capClassicModuleCert {
   rpc SendL2capPacket(L2capPacket) returns (google.protobuf.Empty) {}
+  rpc SendIFrame(IFrame) returns (SendIFrameResult) {}
+  rpc SendSFrame(SFrame) returns (SendSFrameResult) {}
 
   rpc SetupLink(SetupLinkRequest) returns (SetupLinkResponse) {}
   rpc DisconnectLink(DisconnectLinkRequest) returns (google.protobuf.Empty) {}
@@ -24,6 +26,7 @@
   rpc SendInformationResponse(InformationResponse) returns (SendInformationResponseResult) {}
 
   rpc FetchL2capLog(FetchL2capLogRequest) returns (stream FetchL2capLogResponse) {}
+  rpc StopFetchingL2capLog(StopFetchingL2capLogRequest) returns (StopFetchingL2capLogResponse) {}
 }
 
 message L2capPacket {
@@ -32,6 +35,30 @@
   bytes payload = 3;
 }
 
+message IFrame {
+  facade.BluetoothAddress remote = 1;
+  uint32 channel = 2;
+  uint32 sar = 3;
+  uint32 tx_seq = 4;
+  uint32 req_seq = 5;
+  uint32 f = 6;
+  uint32 sdu_size = 7;
+  bytes information = 8;
+}
+
+message SendIFrameResult {}
+
+message SFrame {
+  facade.BluetoothAddress remote = 1;
+  uint32 channel = 2;
+  uint32 req_seq = 3;
+  uint32 f = 4;
+  uint32 p = 5;
+  uint32 s = 6;
+}
+
+message SendSFrameResult {}
+
 message DisconnectLinkRequest {
   facade.BluetoothAddress remote = 1;
 }
@@ -58,10 +85,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 +113,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 +153,7 @@
   InformationRequestType type = 1;
   uint32 data = 2;
   uint32 signal_id = 3;
+  uint32 information_value = 4;
 }
 
 message SendInformationResponseResult {}
@@ -153,3 +200,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..abb3561 100644
--- a/gd/l2cap/classic/cert/cert.cc
+++ b/gd/l2cap/classic/cert/cert.cc
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#include "l2cap/classic/cert/cert.h"
-
 #include <condition_variable>
 #include <cstdint>
 #include <memory>
@@ -29,6 +27,7 @@
 #include "hci/cert/cert.h"
 #include "hci/hci_packets.h"
 #include "l2cap/classic/cert/api.grpc.pb.h"
+#include "l2cap/classic/cert/cert.h"
 #include "l2cap/classic/l2cap_classic_module.h"
 #include "l2cap/l2cap_packets.h"
 #include "os/log.h"
@@ -52,9 +51,9 @@
 
 constexpr auto kEventTimeout = std::chrono::seconds(1);
 
-class L2capModuleCertService : public L2capModuleCert::Service {
+class L2capClassicModuleCertService : public L2capClassicModuleCert::Service {
  public:
-  L2capModuleCertService(hci::AclManager* acl_manager, os::Handler* facade_handler)
+  L2capClassicModuleCertService(hci::AclManager* acl_manager, os::Handler* facade_handler)
       : handler_(facade_handler), acl_manager_(acl_manager) {
     ASSERT(handler_ != nullptr);
     acl_manager_->RegisterCallbacks(&acl_callbacks, handler_);
@@ -81,6 +80,37 @@
     return ::grpc::Status::OK;
   }
 
+  ::grpc::Status SendIFrame(::grpc::ServerContext* context, const ::bluetooth::l2cap::classic::cert::IFrame* request,
+                            ::bluetooth::l2cap::classic::cert::SendIFrameResult* response) override {
+    std::unique_ptr<RawBuilder> packet = std::make_unique<RawBuilder>();
+    auto req_string = request->information();
+    packet->AddOctets(std::vector<uint8_t>(req_string.begin(), req_string.end()));
+    std::unique_ptr<BasePacketBuilder> l2cap_builder;
+    auto f = static_cast<Final>(request->f());
+    if (request->sar() == static_cast<int>(SegmentationAndReassembly::START)) {
+      l2cap_builder = EnhancedInformationStartFrameBuilder::Create(
+          request->channel(), request->tx_seq(), f, request->req_seq(), request->sdu_size(), std::move(packet));
+    } else {
+      l2cap_builder = EnhancedInformationFrameBuilder::Create(
+          request->channel(), request->tx_seq(), f, request->req_seq(),
+          static_cast<SegmentationAndReassembly>(request->sar()), std::move(packet));
+    }
+    outgoing_packet_queue_.push(std::move(l2cap_builder));
+    send_packet_from_queue();
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status SendSFrame(::grpc::ServerContext* context, const ::bluetooth::l2cap::classic::cert::SFrame* request,
+                            ::bluetooth::l2cap::classic::cert::SendSFrameResult* response) override {
+    auto f = static_cast<Final>(request->f());
+    auto p = static_cast<Poll>(request->p());
+    auto s = static_cast<SupervisoryFunction>(request->s());
+    auto builder = EnhancedSupervisoryFrameBuilder::Create(request->channel(), s, p, f, request->req_seq());
+    outgoing_packet_queue_.push(std::move(builder));
+    send_packet_from_queue();
+    return ::grpc::Status::OK;
+  }
+
   ::grpc::Status SendConnectionRequest(::grpc::ServerContext* context, const cert::ConnectionRequest* request,
                                        ::google::protobuf::Empty* response) override {
     auto builder = ConnectionRequestBuilder::Create(request->signal_id(), request->psm(), request->scid());
@@ -105,7 +135,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 +160,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 +239,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 +247,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 +280,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 +298,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_;
@@ -286,7 +353,7 @@
   void send_packet_from_queue() {
     if (outgoing_packet_queue_.size() == 1) {
       acl_connection_->GetAclQueueEnd()->RegisterEnqueue(
-          handler_, common::Bind(&L2capModuleCertService::enqueue_packet_to_acl, common::Unretained(this)));
+          handler_, common::Bind(&L2capClassicModuleCertService::enqueue_packet_to_acl, common::Unretained(this)));
     }
   }
 
@@ -393,15 +460,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;
           }
         }
@@ -420,12 +503,13 @@
 
   class AclCallbacks : public hci::ConnectionCallbacks {
    public:
-    AclCallbacks(L2capModuleCertService* module) : module_(module) {}
+    AclCallbacks(L2capClassicModuleCertService* module) : module_(module) {}
     void OnConnectSuccess(std::unique_ptr<hci::AclConnection> connection) override {
       module_->acl_connection_ = std::move(connection);
       module_->acl_connection_->RegisterDisconnectCallback(common::BindOnce([](hci::ErrorCode) {}), module_->handler_);
       module_->acl_connection_->GetAclQueueEnd()->RegisterDequeue(
-          module_->handler_, common::Bind(&L2capModuleCertService::on_incoming_packet, common::Unretained(module_)));
+          module_->handler_,
+          common::Bind(&L2capClassicModuleCertService::on_incoming_packet, common::Unretained(module_)));
       dequeue_registered_ = true;
       FetchL2capLogResponse response;
       response.mutable_link_up()->mutable_remote()->set_address(module_->acl_connection_->GetAddress().ToString());
@@ -441,36 +525,36 @@
 
     bool dequeue_registered_ = false;
 
-    L2capModuleCertService* module_;
+    L2capClassicModuleCertService* module_;
   } acl_callbacks{this};
 
   std::mutex mutex_;
 };
 
-void L2capModuleCertModule::ListDependencies(ModuleList* list) {
+void L2capClassicModuleCertModule::ListDependencies(ModuleList* list) {
   ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
   list->add<hci::AclManager>();
   list->add<hci::HciLayer>();
 }
 
-void L2capModuleCertModule::Start() {
+void L2capClassicModuleCertModule::Start() {
   ::bluetooth::grpc::GrpcFacadeModule::Start();
   GetDependency<hci::HciLayer>()->EnqueueCommand(hci::WriteScanEnableBuilder::Create(hci::ScanEnable::PAGE_SCAN_ONLY),
                                                  common::BindOnce([](hci::CommandCompleteView) {}), GetHandler());
-  service_ = new L2capModuleCertService(GetDependency<hci::AclManager>(), GetHandler());
+  service_ = new L2capClassicModuleCertService(GetDependency<hci::AclManager>(), GetHandler());
 }
 
-void L2capModuleCertModule::Stop() {
+void L2capClassicModuleCertModule::Stop() {
   delete service_;
   ::bluetooth::grpc::GrpcFacadeModule::Stop();
 }
 
-::grpc::Service* L2capModuleCertModule::GetService() const {
+::grpc::Service* L2capClassicModuleCertModule::GetService() const {
   return service_;
 }
 
-const ModuleFactory L2capModuleCertModule::Factory =
-    ::bluetooth::ModuleFactory([]() { return new L2capModuleCertModule(); });
+const ModuleFactory L2capClassicModuleCertModule::Factory =
+    ::bluetooth::ModuleFactory([]() { return new L2capClassicModuleCertModule(); });
 
 }  // namespace cert
 }  // namespace classic
diff --git a/gd/l2cap/classic/cert/cert.h b/gd/l2cap/classic/cert/cert.h
index c8015d6..d3108cd 100644
--- a/gd/l2cap/classic/cert/cert.h
+++ b/gd/l2cap/classic/cert/cert.h
@@ -9,9 +9,9 @@
 namespace classic {
 namespace cert {
 
-class L2capModuleCertService;
+class L2capClassicModuleCertService;
 
-class L2capModuleCertModule : public ::bluetooth::grpc::GrpcFacadeModule {
+class L2capClassicModuleCertModule : public ::bluetooth::grpc::GrpcFacadeModule {
  public:
   static const ModuleFactory Factory;
 
@@ -22,7 +22,7 @@
   ::grpc::Service* GetService() const override;
 
  private:
-  L2capModuleCertService* service_;
+  L2capClassicModuleCertService* service_;
 };
 
 }  // namespace cert
diff --git a/gd/l2cap/classic/cert/simple_l2cap_test.py b/gd/l2cap/classic/cert/simple_l2cap_test.py
index d0fafb3..607cfb2 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,41 @@
     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)
+                self.cert_device.l2cap.SendSFrame(l2cap_cert_pb2.SFrame(channel=self.scid_dcid_map[scid], req_seq=1, s=0))
+            data_received.append((log.channel, log.payload))
+        event_handler.on(lambda log : log.HasField("data_packet"), on_data_received)
+        logs = self.cert_device.l2cap.FetchL2capLog(l2cap_cert_pb2.FetchL2capLogRequest())
+        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))
+
+        self.cert_device.l2cap.SendIFrame(l2cap_cert_pb2.IFrame(channel=self.scid_dcid_map[scid], req_seq=1, tx_seq=0, sar=0, information=b"abcd"))
+
+        logs = self.cert_device.l2cap.FetchL2capLog(l2cap_cert_pb2.FetchL2capLogRequest())
+        event_handler.execute(logs)
+        assert (scid, b"abc"*34) in data_received
 
     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 +248,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 +272,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 +310,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 +335,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 +354,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 +373,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..64f3810 100644
--- a/gd/l2cap/classic/facade.cc
+++ b/gd/l2cap/classic/facade.cc
@@ -39,9 +39,9 @@
 namespace l2cap {
 namespace classic {
 
-class L2capModuleFacadeService : public L2capModuleFacade::Service {
+class L2capClassicModuleFacadeService : public L2capClassicModuleFacade::Service {
  public:
-  L2capModuleFacadeService(L2capClassicModule* l2cap_layer, os::Handler* facade_handler)
+  L2capClassicModuleFacadeService(L2capClassicModule* l2cap_layer, os::Handler* facade_handler)
       : l2cap_layer_(l2cap_layer), facade_handler_(facade_handler) {
     ASSERT(l2cap_layer_ != nullptr);
     ASSERT(facade_handler_ != nullptr);
@@ -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);
@@ -125,8 +126,8 @@
 
   class L2capFixedChannelHelper {
    public:
-    L2capFixedChannelHelper(L2capModuleFacadeService* service, L2capClassicModule* l2cap_layer, os::Handler* handler,
-                            Cid cid)
+    L2capFixedChannelHelper(L2capClassicModuleFacadeService* service, L2capClassicModule* l2cap_layer,
+                            os::Handler* handler, Cid cid)
         : facade_service_(service), l2cap_layer_(l2cap_layer), handler_(handler), cid_(cid) {
       fixed_channel_manager_ = l2cap_layer_->GetFixedChannelManager();
       fixed_channel_manager_->RegisterService(
@@ -172,7 +173,7 @@
       return packet_one;
     };
 
-    L2capModuleFacadeService* facade_service_;
+    L2capClassicModuleFacadeService* facade_service_;
     L2capClassicModule* l2cap_layer_;
     os::Handler* handler_;
     std::unique_ptr<FixedChannelManager> fixed_channel_manager_;
@@ -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)
+    L2capDynamicChannelHelper(L2capClassicModuleFacadeService* service, L2capClassicModule* l2cap_layer,
+                              os::Handler* handler, 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,13 +249,13 @@
     }
 
     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;
     };
 
-    L2capModuleFacadeService* facade_service_;
+    L2capClassicModuleFacadeService* facade_service_;
     L2capClassicModule* l2cap_layer_;
     os::Handler* handler_;
     std::unique_ptr<DynamicChannelManager> dynamic_channel_manager_;
@@ -260,7 +271,7 @@
 
   class L2capStreamCallback : public ::bluetooth::grpc::GrpcEventStreamCallback<L2capPacket, L2capPacket> {
    public:
-    L2capStreamCallback(L2capModuleFacadeService* service) : service_(service) {}
+    L2capStreamCallback(L2capClassicModuleFacadeService* service) : service_(service) {}
 
     ~L2capStreamCallback() {
       for (const auto& connection : service_->fixed_channel_helper_map_) {
@@ -318,7 +329,7 @@
       response->CopyFrom(event);
     }
 
-    L2capModuleFacadeService* service_;
+    L2capClassicModuleFacadeService* service_;
     std::map<Cid, bool> subscribed_fixed_channel_;
     std::map<Psm, bool> subscribed_dynamic_channel_;
 
@@ -328,30 +339,30 @@
   std::mutex mutex_;
 };
 
-void L2capModuleFacadeModule::ListDependencies(ModuleList* list) {
+void L2capClassicModuleFacadeModule::ListDependencies(ModuleList* list) {
   ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
   list->add<l2cap::classic::L2capClassicModule>();
   list->add<hci::HciLayer>();
 }
 
-void L2capModuleFacadeModule::Start() {
+void L2capClassicModuleFacadeModule::Start() {
   ::bluetooth::grpc::GrpcFacadeModule::Start();
   GetDependency<hci::HciLayer>()->EnqueueCommand(hci::WriteScanEnableBuilder::Create(hci::ScanEnable::PAGE_SCAN_ONLY),
                                                  common::BindOnce([](hci::CommandCompleteView) {}), GetHandler());
-  service_ = new L2capModuleFacadeService(GetDependency<l2cap::classic::L2capClassicModule>(), GetHandler());
+  service_ = new L2capClassicModuleFacadeService(GetDependency<l2cap::classic::L2capClassicModule>(), GetHandler());
 }
 
-void L2capModuleFacadeModule::Stop() {
+void L2capClassicModuleFacadeModule::Stop() {
   delete service_;
   ::bluetooth::grpc::GrpcFacadeModule::Stop();
 }
 
-::grpc::Service* L2capModuleFacadeModule::GetService() const {
+::grpc::Service* L2capClassicModuleFacadeModule::GetService() const {
   return service_;
 }
 
-const ModuleFactory L2capModuleFacadeModule::Factory =
-    ::bluetooth::ModuleFactory([]() { return new L2capModuleFacadeModule(); });
+const ModuleFactory L2capClassicModuleFacadeModule::Factory =
+    ::bluetooth::ModuleFactory([]() { return new L2capClassicModuleFacadeModule(); });
 
 }  // namespace classic
 }  // namespace l2cap
diff --git a/gd/l2cap/classic/facade.h b/gd/l2cap/classic/facade.h
index 425940a..ebaee0d 100644
--- a/gd/l2cap/classic/facade.h
+++ b/gd/l2cap/classic/facade.h
@@ -16,15 +16,16 @@
 #pragma once
 
 #include <grpc++/grpc++.h>
+
 #include "grpc/grpc_module.h"
 
 namespace bluetooth {
 namespace l2cap {
 namespace classic {
 
-class L2capModuleFacadeService;
+class L2capClassicModuleFacadeService;
 
-class L2capModuleFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule {
+class L2capClassicModuleFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule {
  public:
   static const ModuleFactory Factory;
 
@@ -35,7 +36,7 @@
   ::grpc::Service* GetService() const override;
 
  private:
-  L2capModuleFacadeService* service_;
+  L2capClassicModuleFacadeService* service_;
 };
 
 }  // namespace classic
diff --git a/gd/l2cap/classic/facade.proto b/gd/l2cap/classic/facade.proto
index 21add63..ab2a911 100644
--- a/gd/l2cap/classic/facade.proto
+++ b/gd/l2cap/classic/facade.proto
@@ -5,7 +5,7 @@
 import "google/protobuf/empty.proto";
 import "facade/common.proto";
 
-service L2capModuleFacade {
+service L2capClassicModuleFacade {
   rpc RegisterChannel(RegisterChannelRequest) returns (google.protobuf.Empty) {
     // Testing Android Bluetooth stack only. Optional for other stack.
   }
@@ -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/fixed_channel_manager.h b/gd/l2cap/classic/fixed_channel_manager.h
index 69812df..5e8f6ab 100644
--- a/gd/l2cap/classic/fixed_channel_manager.h
+++ b/gd/l2cap/classic/fixed_channel_manager.h
@@ -31,6 +31,10 @@
 
 class L2capClassicModule;
 
+namespace testing {
+class MockFixedChannelManager;
+}
+
 namespace internal {
 class LinkManager;
 class FixedChannelServiceManagerImpl;
@@ -102,7 +106,7 @@
    *
    * Returns: true if connection was able to be initiated, false otherwise.
    */
-  bool ConnectServices(hci::Address device, OnConnectionFailureCallback on_fail_callback, os::Handler* handler);
+  virtual bool ConnectServices(hci::Address device, OnConnectionFailureCallback on_fail_callback, os::Handler* handler);
 
   /**
    * Register a service to receive incoming connections bound to a specific channel.
@@ -124,11 +128,14 @@
    * @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.
    */
-  bool RegisterService(Cid cid, const SecurityPolicy& security_policy,
-                       OnRegistrationCompleteCallback on_registration_complete,
-                       OnConnectionOpenCallback on_connection_open, os::Handler* handler);
+  virtual bool RegisterService(Cid cid, const SecurityPolicy& security_policy,
+                               OnRegistrationCompleteCallback on_registration_complete,
+                               OnConnectionOpenCallback on_connection_open, os::Handler* handler);
+
+  virtual ~FixedChannelManager() = default;
 
   friend class L2capClassicModule;
+  friend class testing::MockFixedChannelManager;
 
  private:
   // The constructor is not to be used by user code
diff --git a/gd/l2cap/classic/fixed_channel_manager_mock.h b/gd/l2cap/classic/fixed_channel_manager_mock.h
new file mode 100644
index 0000000..5ead957
--- /dev/null
+++ b/gd/l2cap/classic/fixed_channel_manager_mock.h
@@ -0,0 +1,42 @@
+/*
+ * 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/classic/fixed_channel_manager.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace testing {
+
+class MockFixedChannelManager : public FixedChannelManager {
+ public:
+  MockFixedChannelManager() : FixedChannelManager(nullptr, nullptr, nullptr){};
+  MOCK_METHOD(bool, ConnectServices,
+              (hci::Address device, OnConnectionFailureCallback on_fail_callback, os::Handler* handler), (override));
+  MOCK_METHOD(bool, RegisterService,
+              (Cid cid, const SecurityPolicy& security_policy, OnRegistrationCompleteCallback on_registration_complete,
+               OnConnectionOpenCallback on_connection_open, os::Handler* handler),
+              (override));
+};
+
+}  // namespace testing
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/internal/dynamic_channel_impl.cc b/gd/l2cap/classic/internal/dynamic_channel_impl.cc
index bd56187..c0614f9 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,28 +98,21 @@
   incoming_configuration_status_ = status;
 }
 
-Mtu DynamicChannelImpl::GetIncomingMtu() const {
-  return incoming_mtu_;
+void DynamicChannelImpl::SetSender(l2cap::internal::Sender* sender) {
+  sender_ = sender;
 }
 
 void DynamicChannelImpl::SetIncomingMtu(Mtu mtu) {
-  incoming_mtu_ = mtu;
+  sender_->SetIncomingMtu(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);
 }
 
 void DynamicChannelImpl::SetFcsType(FcsType fcs_type) {
-  fcs_type_ = fcs_type;
+  sender_->SetFcsType(fcs_type);
 }
 
 }  // namespace internal
diff --git a/gd/l2cap/classic/internal/dynamic_channel_impl.h b/gd/l2cap/classic/internal/dynamic_channel_impl.h
index ba75814..95be5c7 100644
--- a/gd/l2cap/classic/internal/dynamic_channel_impl.h
+++ b/gd/l2cap/classic/internal/dynamic_channel_impl.h
@@ -76,13 +76,16 @@
   virtual ConfigurationStatus GetIncomingConfigurationStatus() const;
   virtual void SetIncomingConfigurationStatus(ConfigurationStatus status);
 
-  virtual Mtu GetIncomingMtu() const;
+  /**
+   * 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 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,10 +112,7 @@
   ConfigurationStatus outgoing_configuration_status_ = ConfigurationStatus::NOT_CONFIGURED;
   ConfigurationStatus incoming_configuration_status_ = ConfigurationStatus::NOT_CONFIGURED;
 
-  Mtu incoming_mtu_ = kDefaultClassicMtu;
-  RetransmissionAndFlowControlModeOption mode_ = RetransmissionAndFlowControlModeOption::L2CAP_BASIC;
-  // TODO: Add all RetransmissionAndFlowControlConfigurationOptions
-  FcsType fcs_type_ = FcsType::DEFAULT;
+  l2cap::internal::Sender* sender_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(DynamicChannelImpl);
 };
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..09d7eb9 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,18 @@
   virtual void NotifyChannelCreation(Cid cid, std::unique_ptr<DynamicChannel> channel);
   virtual void NotifyChannelFail(Cid cid);
 
+  // Information received from signaling 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;
+
+  virtual std::string ToString() {
+    return GetDevice().ToString();
+  }
+
  private:
   os::Handler* l2cap_handler_;
   l2cap::internal::FixedChannelAllocator<FixedChannelImpl, Link> fixed_channel_allocator_{this, l2cap_handler_};
@@ -110,6 +124,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..7398c17 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,7 @@
     handle_send_next_command();
     return;
   }
+
   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 +391,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 +412,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 +424,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/classic/l2cap_classic_module.cc b/gd/l2cap/classic/l2cap_classic_module.cc
index bc30c86..ca5a0d5 100644
--- a/gd/l2cap/classic/l2cap_classic_module.cc
+++ b/gd/l2cap/classic/l2cap_classic_module.cc
@@ -50,6 +50,10 @@
                                       &dynamic_channel_service_manager_impl_, &parameter_provider_};
 };
 
+L2capClassicModule::L2capClassicModule() {}
+
+L2capClassicModule::~L2capClassicModule() {}
+
 void L2capClassicModule::ListDependencies(ModuleList* list) {
   list->add<hci::AclManager>();
 }
@@ -78,4 +82,4 @@
 
 }  // namespace classic
 }  // namespace l2cap
-}  // namespace bluetooth
\ No newline at end of file
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/l2cap_classic_module.h b/gd/l2cap/classic/l2cap_classic_module.h
index 7255bc3..388cae2 100644
--- a/gd/l2cap/classic/l2cap_classic_module.h
+++ b/gd/l2cap/classic/l2cap_classic_module.h
@@ -27,18 +27,18 @@
 
 class L2capClassicModule : public bluetooth::Module {
  public:
-  L2capClassicModule() = default;
-  ~L2capClassicModule() = default;
+  L2capClassicModule();
+  virtual ~L2capClassicModule();
 
   /**
    * Get the api to the classic fixed channel l2cap module
    */
-  std::unique_ptr<FixedChannelManager> GetFixedChannelManager();
+  virtual std::unique_ptr<FixedChannelManager> GetFixedChannelManager();
 
   /**
    * Get the api to the classic dynamic channel l2cap module
    */
-  std::unique_ptr<DynamicChannelManager> GetDynamicChannelManager();
+  virtual std::unique_ptr<DynamicChannelManager> GetDynamicChannelManager();
 
   static const ModuleFactory Factory;
 
@@ -59,4 +59,4 @@
 
 }  // namespace classic
 }  // namespace l2cap
-}  // namespace bluetooth
\ No newline at end of file
+}  // namespace bluetooth
diff --git a/gd/l2cap/classic/l2cap_classic_module_mock.h b/gd/l2cap/classic/l2cap_classic_module_mock.h
new file mode 100644
index 0000000..1252854
--- /dev/null
+++ b/gd/l2cap/classic/l2cap_classic_module_mock.h
@@ -0,0 +1,37 @@
+/*
+ * 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/classic/l2cap_classic_module.h"
+
+#include <gmock/gmock.h>
+
+// Unit test interfaces
+namespace bluetooth {
+namespace l2cap {
+namespace classic {
+namespace testing {
+
+class MockL2capClassicModule : public L2capClassicModule {
+ public:
+  MOCK_METHOD(std::unique_ptr<FixedChannelManager>, GetFixedChannelManager, (), (override));
+  MOCK_METHOD(std::unique_ptr<DynamicChannelManager>, GetDynamicChannelManager, (), (override));
+};
+
+}  // namespace testing
+}  // namespace classic
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/basic_mode_channel_data_controller.cc b/gd/l2cap/internal/basic_mode_channel_data_controller.cc
index 1a049aa..3575e25 100644
--- a/gd/l2cap/internal/basic_mode_channel_data_controller.cc
+++ b/gd/l2cap/internal/basic_mode_channel_data_controller.cc
@@ -16,6 +16,8 @@
 
 #include "l2cap/internal/basic_mode_channel_data_controller.h"
 
+#include "l2cap/l2cap_packets.h"
+
 namespace bluetooth {
 namespace l2cap {
 namespace internal {
@@ -31,11 +33,15 @@
   scheduler_->OnPacketsReady(cid_, 1);
 }
 
-void BasicModeDataController::OnPdu(BasicFrameView pdu) {
-  enqueue_buffer_.Enqueue(std::make_unique<PacketView<kLittleEndian>>(pdu.GetPayload()), handler_);
+void BasicModeDataController::OnPdu(packet::PacketView<true> pdu) {
+  auto basic_frame_view = BasicFrameView::Create(pdu);
+  if (!basic_frame_view.IsValid()) {
+    return;
+  }
+  enqueue_buffer_.Enqueue(std::make_unique<PacketView<kLittleEndian>>(basic_frame_view.GetPayload()), handler_);
 }
 
-std::unique_ptr<BasicFrameBuilder> BasicModeDataController::GetNextPacket() {
+std::unique_ptr<packet::BasePacketBuilder> BasicModeDataController::GetNextPacket() {
   auto next = std::move(pdu_queue_.front());
   pdu_queue_.pop();
   return next;
diff --git a/gd/l2cap/internal/basic_mode_channel_data_controller.h b/gd/l2cap/internal/basic_mode_channel_data_controller.h
index c19fb93..8e40402 100644
--- a/gd/l2cap/internal/basic_mode_channel_data_controller.h
+++ b/gd/l2cap/internal/basic_mode_channel_data_controller.h
@@ -46,16 +46,19 @@
 
   void OnSdu(std::unique_ptr<packet::BasePacketBuilder> sdu) override;
 
-  void OnPdu(BasicFrameView pdu) override;
+  void OnPdu(packet::PacketView<true> pdu) override;
 
-  std::unique_ptr<BasicFrameBuilder> GetNextPacket() override;
+  std::unique_ptr<packet::BasePacketBuilder> GetNextPacket() override;
+
+  void EnableFcs(bool enabled) override {}
+  void SetRetransmissionAndFlowControlOptions(const RetransmissionAndFlowControlConfigurationOption& option) override {}
 
  private:
   Cid cid_;
   Cid remote_cid_;
   os::EnqueueBuffer<UpperEnqueue> enqueue_buffer_;
   os::Handler* handler_;
-  std::queue<std::unique_ptr<BasicFrameBuilder>> pdu_queue_;
+  std::queue<std::unique_ptr<packet::BasePacketBuilder>> pdu_queue_;
   Scheduler* scheduler_;
 };
 
diff --git a/gd/l2cap/internal/basic_mode_channel_data_controller_test.cc b/gd/l2cap/internal/basic_mode_channel_data_controller_test.cc
new file mode 100644
index 0000000..c4cc56d
--- /dev/null
+++ b/gd/l2cap/internal/basic_mode_channel_data_controller_test.cc
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/internal/basic_mode_channel_data_controller.h"
+
+#include <gtest/gtest.h>
+
+#include "l2cap/internal/scheduler_mock.h"
+#include "packet/raw_builder.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+namespace {
+
+std::unique_ptr<packet::BasePacketBuilder> CreateSdu(std::vector<uint8_t> payload) {
+  auto raw_builder = std::make_unique<packet::RawBuilder>();
+  raw_builder->AddOctets(payload);
+  return raw_builder;
+}
+
+PacketView<kLittleEndian> GetPacketView(std::unique_ptr<packet::BasePacketBuilder> packet) {
+  auto bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter i(*bytes);
+  bytes->reserve(packet->size());
+  packet->Serialize(i);
+  return packet::PacketView<packet::kLittleEndian>(bytes);
+}
+
+void sync_handler(os::Handler* handler) {
+  std::promise<void> promise;
+  auto future = promise.get_future();
+  handler->Post(common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)));
+  auto status = future.wait_for(std::chrono::milliseconds(300));
+  EXPECT_EQ(status, std::future_status::ready);
+}
+
+class BasicModeDataControllerTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
+    user_handler_ = new os::Handler(thread_);
+    queue_handler_ = new os::Handler(thread_);
+  }
+
+  void TearDown() override {
+    queue_handler_->Clear();
+    user_handler_->Clear();
+    delete queue_handler_;
+    delete user_handler_;
+    delete thread_;
+  }
+
+  os::Thread* thread_ = nullptr;
+  os::Handler* user_handler_ = nullptr;
+  os::Handler* queue_handler_ = nullptr;
+};
+
+TEST_F(BasicModeDataControllerTest, transmit) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  BasicModeDataController controller{1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  EXPECT_CALL(scheduler, OnPacketsReady(1, 1));
+  controller.OnSdu(CreateSdu({'a', 'b', 'c', 'd'}));
+  auto next_packet = controller.GetNextPacket();
+  EXPECT_NE(next_packet, nullptr);
+  auto view = GetPacketView(std::move(next_packet));
+  auto pdu_view = BasicFrameView::Create(view);
+  EXPECT_TRUE(pdu_view.IsValid());
+  auto payload = pdu_view.GetPayload();
+  std::string data = std::string(payload.begin(), payload.end());
+  EXPECT_EQ(data, "abcd");
+}
+
+TEST_F(BasicModeDataControllerTest, receive) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  BasicModeDataController controller{1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  auto base_view = GetPacketView(BasicFrameBuilder::Create(1, CreateSdu({'a', 'b', 'c', 'd'})));
+  controller.OnPdu(base_view);
+  sync_handler(queue_handler_);
+  auto packet_view = channel_queue.GetUpEnd()->TryDequeue();
+  EXPECT_NE(packet_view, nullptr);
+  std::string data = std::string(packet_view->begin(), packet_view->end());
+  EXPECT_EQ(data, "abcd");
+}
+
+}  // namespace
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
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/data_controller.h b/gd/l2cap/internal/data_controller.h
index 18cff2a..bc7c2e5 100644
--- a/gd/l2cap/internal/data_controller.h
+++ b/gd/l2cap/internal/data_controller.h
@@ -34,10 +34,18 @@
   virtual void OnSdu(std::unique_ptr<packet::BasePacketBuilder> sdu) = 0;
 
   // PDUs -> SDU and enqueue to channel queue end
-  virtual void OnPdu(BasicFrameView pdu) = 0;
+  virtual void OnPdu(packet::PacketView<true> pdu) = 0;
 
   // Used by Scheduler to get next PDU
-  virtual std::unique_ptr<BasicFrameBuilder> GetNextPacket() = 0;
+  virtual std::unique_ptr<packet::BasePacketBuilder> GetNextPacket() = 0;
+
+  // Set FCS mode. This only applies to some modes (ERTM).
+  virtual void EnableFcs(bool enabled) = 0;
+
+  // Set retransmission and flow control. Ignore the mode option because each DataController only handles one mode.
+  // This only applies to some modes (ERTM).
+  virtual void SetRetransmissionAndFlowControlOptions(
+      const RetransmissionAndFlowControlConfigurationOption& option) = 0;
 };
 
 }  // namespace internal
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..97f9702 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));
     }
   }
 
@@ -165,20 +168,20 @@
     }
   }
 
-  void recv_i_frame(Final f, uint8_t tx_seq, uint8_t req_seq, SegmentationAndReassembly sar,
+  void recv_i_frame(Final f, uint8_t tx_seq, uint8_t req_seq, SegmentationAndReassembly sar, uint16_t sdu_size,
                     const packet::PacketView<true>& payload) {
     if (rx_state_ == RxState::RECV) {
       if (f == Final::NOT_SET && with_expected_tx_seq(tx_seq) && with_valid_req_seq(req_seq) && with_valid_f_bit(f) &&
           !local_busy()) {
         increment_expected_tx_seq();
         pass_to_tx(req_seq, f);
-        data_indication(sar, payload);
+        data_indication(sar, sdu_size, payload);
         send_ack(Final::NOT_SET);
       } else if (f == Final::POLL_RESPONSE && with_expected_tx_seq(tx_seq) && with_valid_req_seq(req_seq) &&
                  with_valid_f_bit(f) && !local_busy()) {
         increment_expected_tx_seq();
         pass_to_tx(req_seq, f);
-        data_indication(sar, payload);
+        data_indication(sar, sdu_size, payload);
         if (!rej_actioned_) {
           retransmit_i_frames(req_seq);
           send_pending_i_frames();
@@ -213,14 +216,14 @@
       if (f == Final::NOT_SET && with_expected_tx_seq(tx_seq) && with_valid_req_seq(req_seq) && with_valid_f_bit(f)) {
         increment_expected_tx_seq();
         pass_to_tx(req_seq, f);
-        data_indication(sar, payload);
+        data_indication(sar, sdu_size, payload);
         send_ack(Final::NOT_SET);
         rx_state_ = RxState::RECV;
       } else if (f == Final::POLL_RESPONSE && with_expected_tx_seq(tx_seq) && with_valid_req_seq(req_seq) &&
                  with_valid_f_bit(f)) {
         increment_expected_tx_seq();
         pass_to_tx(req_seq, f);
-        data_indication(sar, payload);
+        data_indication(sar, sdu_size, payload);
         if (!rej_actioned_) {
           retransmit_i_frames(req_seq);
           send_pending_i_frames();
@@ -487,11 +490,11 @@
   }
 
   bool with_valid_req_seq(uint8_t req_seq) {
-    return expected_ack_seq_ <= req_seq && req_seq < next_tx_seq_;
+    return expected_ack_seq_ <= req_seq && req_seq <= next_tx_seq_;
   }
 
   bool with_valid_req_seq_retrans(uint8_t req_seq) {
-    return expected_ack_seq_ <= req_seq && req_seq < next_tx_seq_;
+    return expected_ack_seq_ <= req_seq && req_seq <= next_tx_seq_;
   }
 
   bool with_valid_f_bit(Final f) {
@@ -550,19 +553,38 @@
 
   // 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<packet::BasePacketBuilder> builder;
+    if (sar == SegmentationAndReassembly::START) {
+      if (controller_->fcs_enabled_) {
+        builder = EnhancedInformationStartFrameWithFcsBuilder::Create(controller_->remote_cid_, tx_seq, f, req_seq,
+                                                                      sdu_size, std::move(segment));
+      } else {
+        builder = EnhancedInformationStartFrameBuilder::Create(controller_->remote_cid_, tx_seq, f, req_seq, sdu_size,
+                                                               std::move(segment));
+      }
+    } else {
+      if (controller_->fcs_enabled_) {
+        builder = EnhancedInformationFrameWithFcsBuilder::Create(controller_->remote_cid_, tx_seq, f, req_seq, sar,
+                                                                 std::move(segment));
+      } else {
+        builder = EnhancedInformationFrameBuilder::Create(controller_->remote_cid_, tx_seq, f, req_seq, sar,
+                                                          std::move(segment));
+      }
+    }
     controller_->send_pdu(std::move(builder));
   }
 
-  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);
-    // TODO hsz fix me
+                          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);
     unacked_frames_++;
     frames_sent_++;
     retry_i_frames_[next_tx_seq_] = 1;
@@ -570,8 +592,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) {
@@ -586,7 +608,12 @@
   }
 
   void _send_s_frame(SupervisoryFunction s, uint8_t req_seq, Poll p, Final f) {
-    auto builder = EnhancedSupervisoryFrameBuilder::Create(controller_->remote_cid_, s, p, f, req_seq);
+    std::unique_ptr<packet::BasePacketBuilder> builder;
+    if (controller_->fcs_enabled_) {
+      builder = EnhancedSupervisoryFrameWithFcsBuilder::Create(controller_->remote_cid_, s, p, f, req_seq);
+    } else {
+      builder = EnhancedSupervisoryFrameBuilder::Create(controller_->remote_cid_, s, p, f, req_seq);
+    }
     controller_->send_pdu(std::move(builder));
   }
 
@@ -655,8 +682,8 @@
     recv_f_bit(f);
   }
 
-  void data_indication(SegmentationAndReassembly sar, const packet::PacketView<true>& segment) {
-    controller_->stage_for_reassembly(sar, segment);
+  void data_indication(SegmentationAndReassembly sar, uint16_t sdu_size, const packet::PacketView<true>& segment) {
+    controller_->stage_for_reassembly(sar, sdu_size, segment);
     buffer_seq_ = (buffer_seq_ + 1) % kMaxTxWin;
   }
 
@@ -702,8 +729,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 +749,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 +763,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,29 +784,36 @@
 
 // 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 =
-      (remote_mps_ - 4 /* basic L2CAP header */ - 2 /* SDU length */ - 2 /* Extended control */ - 2 /* FCS */);
+  auto sdu_size = sdu->size();
   std::vector<std::unique_ptr<packet::RawBuilder>> segments;
-  packet::FragmentingInserter fragmenting_inserter(size_each_packet, std::back_insert_iterator(segments));
+  packet::FragmentingInserter fragmenting_inserter(size_each_packet_, std::back_insert_iterator(segments));
   sdu->Serialize(fragmenting_inserter);
+  fragmenting_inserter.finalize();
   if (segments.size() == 1) {
-    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) {
-  auto standard_frame_view = StandardFrameView::Create(pdu);
+void ErtmController::OnPdu(packet::PacketView<true> pdu) {
+  if (fcs_enabled_) {
+    on_pdu_fcs(pdu);
+  } else {
+    on_pdu_no_fcs(pdu);
+  }
+}
+
+void ErtmController::on_pdu_no_fcs(const packet::PacketView<true>& pdu) {
+  auto basic_frame_view = BasicFrameView::Create(pdu);
+  if (!basic_frame_view.IsValid()) {
+    return;
+  }
+  auto standard_frame_view = StandardFrameView::Create(basic_frame_view);
   if (!standard_frame_view.IsValid()) {
     LOG_WARN("Received invalid frame");
     return;
@@ -787,8 +825,21 @@
       LOG_WARN("Received invalid frame");
       return;
     }
-    pimpl_->recv_i_frame(i_frame_view.GetF(), i_frame_view.GetTxSeq(), i_frame_view.GetReqSeq(), i_frame_view.GetSar(),
-                         i_frame_view.GetPayload());
+    Final f = i_frame_view.GetF();
+    uint8_t tx_seq = i_frame_view.GetTxSeq();
+    uint8_t req_seq = i_frame_view.GetReqSeq();
+    auto sar = i_frame_view.GetSar();
+    if (sar == SegmentationAndReassembly::START) {
+      auto i_frame_start_view = EnhancedInformationStartFrameView::Create(i_frame_view);
+      if (!i_frame_start_view.IsValid()) {
+        LOG_WARN("Received invalid I-Frame START");
+        return;
+      }
+      pimpl_->recv_i_frame(f, tx_seq, req_seq, sar, i_frame_start_view.GetL2capSduLength(),
+                           i_frame_start_view.GetPayload());
+    } else {
+      pimpl_->recv_i_frame(f, tx_seq, req_seq, sar, 0, i_frame_view.GetPayload());
+    }
   } else if (type == FrameType::S_FRAME) {
     auto s_frame_view = EnhancedSupervisoryFrameView::Create(standard_frame_view);
     if (!s_frame_view.IsValid()) {
@@ -817,16 +868,82 @@
   }
 }
 
-std::unique_ptr<BasicFrameBuilder> ErtmController::GetNextPacket() {
+void ErtmController::on_pdu_fcs(const packet::PacketView<true>& pdu) {
+  auto basic_frame_view = BasicFrameWithFcsView::Create(pdu);
+  if (!basic_frame_view.IsValid()) {
+    return;
+  }
+  auto standard_frame_view = StandardFrameWithFcsView::Create(basic_frame_view);
+  if (!standard_frame_view.IsValid()) {
+    LOG_WARN("Received invalid frame");
+    return;
+  }
+  auto type = standard_frame_view.GetFrameType();
+  if (type == FrameType::I_FRAME) {
+    auto i_frame_view = EnhancedInformationFrameWithFcsView::Create(standard_frame_view);
+    if (!i_frame_view.IsValid()) {
+      LOG_WARN("Received invalid frame");
+      return;
+    }
+    Final f = i_frame_view.GetF();
+    uint8_t tx_seq = i_frame_view.GetTxSeq();
+    uint8_t req_seq = i_frame_view.GetReqSeq();
+    auto sar = i_frame_view.GetSar();
+    if (sar == SegmentationAndReassembly::START) {
+      auto i_frame_start_view = EnhancedInformationStartFrameWithFcsView::Create(i_frame_view);
+      if (!i_frame_start_view.IsValid()) {
+        LOG_WARN("Received invalid I-Frame START");
+        return;
+      }
+      pimpl_->recv_i_frame(f, tx_seq, req_seq, sar, i_frame_start_view.GetL2capSduLength(),
+                           i_frame_start_view.GetPayload());
+    } else {
+      pimpl_->recv_i_frame(f, tx_seq, req_seq, sar, 0, i_frame_view.GetPayload());
+    }
+  } else if (type == FrameType::S_FRAME) {
+    auto s_frame_view = EnhancedSupervisoryFrameWithFcsView::Create(standard_frame_view);
+    if (!s_frame_view.IsValid()) {
+      LOG_WARN("Received invalid frame");
+      return;
+    }
+    auto req_seq = s_frame_view.GetReqSeq();
+    auto f = s_frame_view.GetF();
+    auto p = s_frame_view.GetP();
+    switch (s_frame_view.GetS()) {
+      case SupervisoryFunction::RECEIVER_READY:
+        pimpl_->recv_rr(req_seq, p, f);
+        break;
+      case SupervisoryFunction::RECEIVER_NOT_READY:
+        pimpl_->recv_rnr(req_seq, p, f);
+        break;
+      case SupervisoryFunction::REJECT:
+        pimpl_->recv_rej(req_seq, p, f);
+        break;
+      case SupervisoryFunction::SELECT_REJECT:
+        pimpl_->recv_srej(req_seq, p, f);
+        break;
+    }
+  } else {
+    LOG_WARN("Received invalid frame");
+  }
+}
+
+std::unique_ptr<packet::BasePacketBuilder> ErtmController::GetNextPacket() {
   auto next = std::move(pdu_queue_.front());
   pdu_queue_.pop();
   return next;
 }
 
-void ErtmController::stage_for_reassembly(SegmentationAndReassembly sar,
+void ErtmController::stage_for_reassembly(SegmentationAndReassembly sar, uint16_t sdu_size,
                                           const packet::PacketView<kLittleEndian>& payload) {
   switch (sar) {
     case SegmentationAndReassembly::UNSEGMENTED:
+      if (sar_state_ != SegmentationAndReassembly::END) {
+        LOG_WARN("Received invalid SAR");
+        close_channel();
+        return;
+      }
+      // TODO: Enforce MTU
       enqueue_buffer_.Enqueue(std::make_unique<packet::PacketView<kLittleEndian>>(payload), handler_);
       break;
     case SegmentationAndReassembly::START:
@@ -835,8 +952,10 @@
         close_channel();
         return;
       }
+      // TODO: Enforce MTU
       sar_state_ = SegmentationAndReassembly::START;
       reassembly_stage_ = payload;
+      remaining_sdu_continuation_packet_size_ = sdu_size - payload.size();
       break;
     case SegmentationAndReassembly::CONTINUATION:
       if (sar_state_ == SegmentationAndReassembly::END) {
@@ -845,6 +964,7 @@
         return;
       }
       reassembly_stage_.AppendPacketView(payload);
+      remaining_sdu_continuation_packet_size_ -= payload.size();
       break;
     case SegmentationAndReassembly::END:
       if (sar_state_ == SegmentationAndReassembly::END) {
@@ -852,18 +972,38 @@
         close_channel();
         return;
       }
+      sar_state_ = SegmentationAndReassembly::END;
+      remaining_sdu_continuation_packet_size_ -= payload.size();
+      if (remaining_sdu_continuation_packet_size_ != 0) {
+        LOG_WARN("Received invalid END I-Frame");
+        reassembly_stage_ = PacketViewForReassembly(std::make_shared<std::vector<uint8_t>>());
+        remaining_sdu_continuation_packet_size_ = 0;
+        close_channel();
+        return;
+      }
       reassembly_stage_.AppendPacketView(payload);
       enqueue_buffer_.Enqueue(std::make_unique<packet::PacketView<kLittleEndian>>(reassembly_stage_), handler_);
-      sar_state_ = SegmentationAndReassembly::END;
       break;
   }
 }
 
-void ErtmController::send_pdu(std::unique_ptr<BasicFrameBuilder> pdu) {
+void ErtmController::EnableFcs(bool enabled) {
+  fcs_enabled_ = enabled;
+}
+
+void ErtmController::send_pdu(std::unique_ptr<packet::BasePacketBuilder> pdu) {
   pdu_queue_.emplace(std::move(pdu));
   scheduler_->OnPacketsReady(cid_, 1);
 }
 
+void ErtmController::SetRetransmissionAndFlowControlOptions(
+    const RetransmissionAndFlowControlConfigurationOption& option) {
+  local_tx_window_ = option.tx_window_size_;
+  local_max_transmit_ = option.max_transmit_;
+  local_retransmit_timeout_ms_ = option.retransmission_time_out_;
+  local_monitor_timeout_ms_ = option.monitor_time_out_;
+}
+
 void ErtmController::close_channel() {
   // TODO: Get a reference to signalling manager
 }
@@ -876,10 +1016,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..dd1ca64 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 {
@@ -46,23 +47,35 @@
   ~ErtmController();
   // Segmentation is handled here
   void OnSdu(std::unique_ptr<packet::BasePacketBuilder> sdu) override;
-  void OnPdu(BasicFrameView pdu) override;
-  std::unique_ptr<BasicFrameBuilder> GetNextPacket() override;
+  void OnPdu(packet::PacketView<true> pdu) override;
+  std::unique_ptr<packet::BasePacketBuilder> GetNextPacket() override;
+  void EnableFcs(bool enabled) override;
+  void SetRetransmissionAndFlowControlOptions(const RetransmissionAndFlowControlConfigurationOption& option) override;
 
  private:
-  [[maybe_unused]] Cid cid_;
-  [[maybe_unused]] Cid remote_cid_;
-  [[maybe_unused]] os::EnqueueBuffer<UpperEnqueue> enqueue_buffer_;
-  [[maybe_unused]] os::Handler* handler_;
-  std::queue<std::unique_ptr<BasicFrameBuilder>> pdu_queue_;
-  [[maybe_unused]] Scheduler* scheduler_;
-  // TODO: Support FCS
-  [[maybe_unused]] FcsType fcs_type_ = FcsType::NO_FCS;
+  Cid cid_;
+  Cid remote_cid_;
+  os::EnqueueBuffer<UpperEnqueue> enqueue_buffer_;
+  os::Handler* handler_;
+  std::queue<std::unique_ptr<packet::BasePacketBuilder>> pdu_queue_;
+  Scheduler* scheduler_;
+
+  // Configuration options
+  bool fcs_enabled_ = false;
+  uint16_t local_tx_window_ = 10;
+  uint16_t local_max_transmit_ = 20;
+  uint16_t local_retransmit_timeout_ms_ = 2000;
+  uint16_t local_monitor_timeout_ms_ = 12000;
+
+  uint16_t remote_tx_window_ = 10;
+  uint16_t remote_mps_ = 1010;
+
+  uint16_t size_each_packet_ =
+      (remote_mps_ - 4 /* basic L2CAP header */ - 2 /* SDU length */ - 2 /* Extended control */ - 2 /* FCS */);
 
   class PacketViewForReassembly : public packet::PacketView<kLittleEndian> {
    public:
     PacketViewForReassembly(const PacketView& packetView) : PacketView(packetView) {}
-    PacketViewForReassembly(nullptr_t) : PacketView(nullptr) {}
     void AppendPacketView(packet::PacketView<kLittleEndian> to_append) {
       Append(to_append);
     }
@@ -70,39 +83,28 @@
 
   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;
+  uint16_t remaining_sdu_continuation_packet_size_ = 0;
 
-  void stage_for_reassembly(SegmentationAndReassembly sar, const packet::PacketView<kLittleEndian>& payload);
-  void send_pdu(std::unique_ptr<BasicFrameBuilder> pdu);
+  void stage_for_reassembly(SegmentationAndReassembly sar, uint16_t sdu_size,
+                            const packet::PacketView<kLittleEndian>& payload);
+  void send_pdu(std::unique_ptr<packet::BasePacketBuilder> pdu);
 
   void close_channel();
 
-  // 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;
-
-  [[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;
+  void on_pdu_no_fcs(const packet::PacketView<true>& pdu);
+  void on_pdu_fcs(const packet::PacketView<true>& pdu);
 
   struct impl;
   std::unique_ptr<impl> pimpl_;
diff --git a/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller_test.cc b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller_test.cc
new file mode 100644
index 0000000..e1b5818
--- /dev/null
+++ b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller_test.cc
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/internal/enhanced_retransmission_mode_channel_data_controller.h"
+
+#include <gtest/gtest.h>
+
+#include "l2cap/internal/scheduler_mock.h"
+#include "l2cap/l2cap_packets.h"
+#include "packet/raw_builder.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+namespace {
+
+std::unique_ptr<packet::BasePacketBuilder> CreateSdu(std::vector<uint8_t> payload) {
+  auto raw_builder = std::make_unique<packet::RawBuilder>();
+  raw_builder->AddOctets(payload);
+  return raw_builder;
+}
+
+PacketView<kLittleEndian> GetPacketView(std::unique_ptr<packet::BasePacketBuilder> packet) {
+  auto bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter i(*bytes);
+  bytes->reserve(packet->size());
+  packet->Serialize(i);
+  return packet::PacketView<packet::kLittleEndian>(bytes);
+}
+
+void sync_handler(os::Handler* handler) {
+  std::promise<void> promise;
+  auto future = promise.get_future();
+  handler->Post(common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)));
+  auto status = future.wait_for(std::chrono::milliseconds(300));
+  EXPECT_EQ(status, std::future_status::ready);
+}
+
+class ErtmDataControllerTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
+    user_handler_ = new os::Handler(thread_);
+    queue_handler_ = new os::Handler(thread_);
+  }
+
+  void TearDown() override {
+    queue_handler_->Clear();
+    user_handler_->Clear();
+    delete queue_handler_;
+    delete user_handler_;
+    delete thread_;
+  }
+
+  os::Thread* thread_ = nullptr;
+  os::Handler* user_handler_ = nullptr;
+  os::Handler* queue_handler_ = nullptr;
+};
+
+TEST_F(ErtmDataControllerTest, transmit_no_fcs) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  ErtmController controller{1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  EXPECT_CALL(scheduler, OnPacketsReady(1, 1));
+  controller.OnSdu(CreateSdu({'a', 'b', 'c', 'd'}));
+  auto next_packet = controller.GetNextPacket();
+  EXPECT_NE(next_packet, nullptr);
+  auto view = GetPacketView(std::move(next_packet));
+  auto pdu_view = BasicFrameView::Create(view);
+  EXPECT_TRUE(pdu_view.IsValid());
+  auto standard_view = StandardFrameView::Create(pdu_view);
+  EXPECT_TRUE(standard_view.IsValid());
+  auto i_frame_view = EnhancedInformationFrameView::Create(standard_view);
+  EXPECT_TRUE(i_frame_view.IsValid());
+  auto payload = i_frame_view.GetPayload();
+  std::string data = std::string(payload.begin(), payload.end());
+  EXPECT_EQ(data, "abcd");
+  EXPECT_EQ(i_frame_view.GetTxSeq(), 0);
+  EXPECT_EQ(i_frame_view.GetReqSeq(), 0);
+}
+
+TEST_F(ErtmDataControllerTest, receive_no_fcs) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  ErtmController controller{1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  auto segment = CreateSdu({'a', 'b', 'c', 'd'});
+  auto builder = EnhancedInformationFrameBuilder::Create(1, 0, Final::NOT_SET, 0,
+                                                         SegmentationAndReassembly::UNSEGMENTED, std::move(segment));
+  auto base_view = GetPacketView(std::move(builder));
+  controller.OnPdu(base_view);
+  sync_handler(queue_handler_);
+  auto payload = channel_queue.GetUpEnd()->TryDequeue();
+  EXPECT_NE(payload, nullptr);
+  std::string data = std::string(payload->begin(), payload->end());
+  EXPECT_EQ(data, "abcd");
+}
+
+TEST_F(ErtmDataControllerTest, reassemble_valid_sdu) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  ErtmController controller{1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  auto segment1 = CreateSdu({'a'});
+  auto segment2 = CreateSdu({'b', 'c'});
+  auto segment3 = CreateSdu({'d', 'e', 'f'});
+  auto builder1 = EnhancedInformationStartFrameBuilder::Create(1, 0, Final::NOT_SET, 0, 6, std::move(segment1));
+  auto base_view = GetPacketView(std::move(builder1));
+  controller.OnPdu(base_view);
+  auto builder2 = EnhancedInformationFrameBuilder::Create(1, 1, Final::NOT_SET, 0,
+                                                          SegmentationAndReassembly::CONTINUATION, std::move(segment2));
+  base_view = GetPacketView(std::move(builder2));
+  controller.OnPdu(base_view);
+  auto builder3 = EnhancedInformationFrameBuilder::Create(1, 2, Final::NOT_SET, 0, SegmentationAndReassembly::END,
+                                                          std::move(segment3));
+  base_view = GetPacketView(std::move(builder3));
+  controller.OnPdu(base_view);
+  sync_handler(queue_handler_);
+  auto payload = channel_queue.GetUpEnd()->TryDequeue();
+  EXPECT_NE(payload, nullptr);
+  std::string data = std::string(payload->begin(), payload->end());
+  EXPECT_EQ(data, "abcdef");
+}
+
+TEST_F(ErtmDataControllerTest, reassemble_invalid_sdu_size_in_start_frame) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  ErtmController controller{1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  auto segment1 = CreateSdu({'a'});
+  auto segment2 = CreateSdu({'b', 'c'});
+  auto segment3 = CreateSdu({'d', 'e', 'f'});
+  auto builder1 = EnhancedInformationStartFrameBuilder::Create(1, 0, Final::NOT_SET, 0, 10, std::move(segment1));
+  auto base_view = GetPacketView(std::move(builder1));
+  controller.OnPdu(base_view);
+  auto builder2 = EnhancedInformationFrameBuilder::Create(1, 1, Final::NOT_SET, 0,
+                                                          SegmentationAndReassembly::CONTINUATION, std::move(segment2));
+  base_view = GetPacketView(std::move(builder2));
+  controller.OnPdu(base_view);
+  auto builder3 = EnhancedInformationFrameBuilder::Create(1, 2, Final::NOT_SET, 0, SegmentationAndReassembly::END,
+                                                          std::move(segment3));
+  base_view = GetPacketView(std::move(builder3));
+  controller.OnPdu(base_view);
+  sync_handler(queue_handler_);
+  auto payload = channel_queue.GetUpEnd()->TryDequeue();
+  EXPECT_EQ(payload, nullptr);
+}
+
+TEST_F(ErtmDataControllerTest, transmit_with_fcs) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  ErtmController controller{1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  controller.EnableFcs(true);
+  EXPECT_CALL(scheduler, OnPacketsReady(1, 1));
+  controller.OnSdu(CreateSdu({'a', 'b', 'c', 'd'}));
+  auto next_packet = controller.GetNextPacket();
+  EXPECT_NE(next_packet, nullptr);
+  auto view = GetPacketView(std::move(next_packet));
+  auto pdu_view = BasicFrameWithFcsView::Create(view);
+  EXPECT_TRUE(pdu_view.IsValid());
+  auto standard_view = StandardFrameWithFcsView::Create(pdu_view);
+  EXPECT_TRUE(standard_view.IsValid());
+  auto i_frame_view = EnhancedInformationFrameWithFcsView::Create(standard_view);
+  EXPECT_TRUE(i_frame_view.IsValid());
+  auto payload = i_frame_view.GetPayload();
+  std::string data = std::string(payload.begin(), payload.end());
+  EXPECT_EQ(data, "abcd");
+  EXPECT_EQ(i_frame_view.GetTxSeq(), 0);
+  EXPECT_EQ(i_frame_view.GetReqSeq(), 0);
+}
+
+TEST_F(ErtmDataControllerTest, receive_packet_with_fcs) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  ErtmController controller{1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  controller.EnableFcs(true);
+  auto segment = CreateSdu({'a', 'b', 'c', 'd'});
+  auto builder = EnhancedInformationFrameWithFcsBuilder::Create(
+      1, 0, Final::NOT_SET, 0, SegmentationAndReassembly::UNSEGMENTED, std::move(segment));
+  auto base_view = GetPacketView(std::move(builder));
+  controller.OnPdu(base_view);
+  sync_handler(queue_handler_);
+  auto payload = channel_queue.GetUpEnd()->TryDequeue();
+  EXPECT_NE(payload, nullptr);
+  std::string data = std::string(payload->begin(), payload->end());
+  EXPECT_EQ(data, "abcd");
+}
+
+}  // namespace
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/fixed_channel_allocator.h b/gd/l2cap/internal/fixed_channel_allocator.h
index a421c91..b821cb1 100644
--- a/gd/l2cap/internal/fixed_channel_allocator.h
+++ b/gd/l2cap/internal/fixed_channel_allocator.h
@@ -44,20 +44,17 @@
   // Allocates a channel. If cid is used, return nullptr. NOTE: The returned BaseFixedChannelImpl object is still
   // owned by the channel allocator, NOT the client.
   virtual std::shared_ptr<FixedChannelImplType> AllocateChannel(Cid cid, SecurityPolicy security_policy) {
-    ASSERT_LOG(!IsChannelAllocated((cid)), "Cid 0x%x for device %s is already in use", cid,
-               link_->GetDevice().ToString().c_str());
+    ASSERT_LOG(!IsChannelAllocated((cid)), "Cid 0x%x for link %s is already in use", cid, link_->ToString().c_str());
     ASSERT_LOG(cid >= kFirstFixedChannel && cid <= kLastFixedChannel, "Cid %d out of bound", cid);
     auto elem = channels_.try_emplace(cid, std::make_shared<FixedChannelImplType>(cid, link_, l2cap_handler_));
-    ASSERT_LOG(elem.second, "Failed to create channel for cid 0x%x device %s", cid,
-               link_->GetDevice().ToString().c_str());
+    ASSERT_LOG(elem.second, "Failed to create channel for cid 0x%x link %s", cid, link_->ToString().c_str());
     ASSERT(elem.first->second != nullptr);
     return elem.first->second;
   }
 
   // Frees a channel. If cid doesn't exist, it will crash
   virtual void FreeChannel(Cid cid) {
-    ASSERT_LOG(IsChannelAllocated(cid), "Channel is not in use: cid %d, device %s", cid,
-               link_->GetDevice().ToString().c_str());
+    ASSERT_LOG(IsChannelAllocated(cid), "Channel is not in use: cid %d, link %s", cid, link_->ToString().c_str());
     channels_.erase(cid);
   }
 
@@ -66,8 +63,7 @@
   }
 
   virtual std::shared_ptr<FixedChannelImplType> FindChannel(Cid cid) {
-    ASSERT_LOG(IsChannelAllocated(cid), "Channel is not in use: cid %d, device %s", cid,
-               link_->GetDevice().ToString().c_str());
+    ASSERT_LOG(IsChannelAllocated(cid), "Channel is not in use: cid %d, link %s", cid, link_->ToString().c_str());
     return channels_.find(cid)->second;
   }
 
diff --git a/gd/l2cap/internal/le_credit_based_channel_data_controller.cc b/gd/l2cap/internal/le_credit_based_channel_data_controller.cc
new file mode 100644
index 0000000..eb510c1
--- /dev/null
+++ b/gd/l2cap/internal/le_credit_based_channel_data_controller.cc
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/internal/le_credit_based_channel_data_controller.h"
+
+#include "l2cap/l2cap_packets.h"
+#include "packet/fragmenting_inserter.h"
+#include "packet/raw_builder.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+
+LeCreditBasedDataController::LeCreditBasedDataController(Cid cid, Cid remote_cid, UpperQueueDownEnd* channel_queue_end,
+                                                         os::Handler* handler, Scheduler* scheduler)
+    : cid_(cid), remote_cid_(remote_cid), enqueue_buffer_(channel_queue_end), handler_(handler), scheduler_(scheduler) {
+}
+
+void LeCreditBasedDataController::OnSdu(std::unique_ptr<packet::BasePacketBuilder> sdu) {
+  auto sdu_size = sdu->size();
+  if (sdu_size == 0) {
+    LOG_WARN("Received empty SDU");
+    return;
+  }
+  if (sdu_size > mtu_) {
+    LOG_WARN("Received sdu_size %d > mtu %d", static_cast<int>(sdu_size), mtu_);
+  }
+  std::vector<std::unique_ptr<packet::RawBuilder>> segments;
+  // TODO: We don't need to waste 2 bytes for continuation segment.
+  packet::FragmentingInserter fragmenting_inserter(mps_ - 2, std::back_insert_iterator(segments));
+  sdu->Serialize(fragmenting_inserter);
+  fragmenting_inserter.finalize();
+  std::unique_ptr<BasicFrameBuilder> builder;
+  builder = FirstLeInformationFrameBuilder::Create(remote_cid_, sdu_size, std::move(segments[0]));
+  pdu_queue_.emplace(std::move(builder));
+  for (auto i = 1; i < segments.size(); i++) {
+    builder = BasicFrameBuilder::Create(remote_cid_, std::move(segments[i]));
+    pdu_queue_.emplace(std::move(builder));
+  }
+  scheduler_->OnPacketsReady(cid_, segments.size());
+}
+
+void LeCreditBasedDataController::OnPdu(packet::PacketView<true> pdu) {
+  auto basic_frame_view = BasicFrameView::Create(pdu);
+  if (!basic_frame_view.IsValid()) {
+    LOG_WARN("Received invalid frame");
+    return;
+  }
+  if (basic_frame_view.size() > mps_) {
+    LOG_WARN("Received frame size %d > mps %d, dropping the packet", static_cast<int>(basic_frame_view.size()), mps_);
+    return;
+  }
+  if (remaining_sdu_continuation_packet_size_ == 0) {
+    auto start_frame_view = FirstLeInformationFrameView::Create(basic_frame_view);
+    if (!start_frame_view.IsValid()) {
+      LOG_WARN("Received invalid frame");
+      return;
+    }
+    auto payload = start_frame_view.GetPayload();
+    auto sdu_size = start_frame_view.GetL2capSduLength();
+    remaining_sdu_continuation_packet_size_ = sdu_size - payload.size();
+    reassembly_stage_ = payload;
+  } else {
+    auto payload = basic_frame_view.GetPayload();
+    remaining_sdu_continuation_packet_size_ -= payload.size();
+    reassembly_stage_.AppendPacketView(payload);
+  }
+  if (remaining_sdu_continuation_packet_size_ == 0) {
+    enqueue_buffer_.Enqueue(std::make_unique<PacketView<kLittleEndian>>(reassembly_stage_), handler_);
+  } else if (remaining_sdu_continuation_packet_size_ < 0 || reassembly_stage_.size() > mtu_) {
+    LOG_WARN("Received larger SDU size than expected");
+    reassembly_stage_ = PacketViewForReassembly(std::make_shared<std::vector<uint8_t>>());
+    remaining_sdu_continuation_packet_size_ = 0;
+    // TODO: Close channel
+  }
+}
+
+std::unique_ptr<packet::BasePacketBuilder> LeCreditBasedDataController::GetNextPacket() {
+  auto next = std::move(pdu_queue_.front());
+  pdu_queue_.pop();
+  return next;
+}
+
+void LeCreditBasedDataController::SetMtu(Mtu mtu) {
+  mtu_ = mtu;
+}
+
+void LeCreditBasedDataController::SetMps(uint16_t mps) {
+  mps_ = mps;
+}
+
+void LeCreditBasedDataController::OnCredit(uint16_t credits) {
+  int total_credits = credits_ + credits;
+  credits_ = total_credits > 0xffff ? 0xffff : total_credits;
+}
+
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/le_credit_based_channel_data_controller.h b/gd/l2cap/internal/le_credit_based_channel_data_controller.h
new file mode 100644
index 0000000..9374cd6
--- /dev/null
+++ b/gd/l2cap/internal/le_credit_based_channel_data_controller.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+#include <unordered_map>
+#include <utility>
+
+#include "common/bidi_queue.h"
+#include "l2cap/cid.h"
+#include "l2cap/internal/channel_impl.h"
+#include "l2cap/internal/data_controller.h"
+#include "l2cap/internal/scheduler.h"
+#include "l2cap/l2cap_packets.h"
+#include "l2cap/mtu.h"
+#include "os/handler.h"
+#include "os/queue.h"
+#include "packet/base_packet_builder.h"
+#include "packet/packet_view.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+
+class LeCreditBasedDataController : public DataController {
+ public:
+  using UpperEnqueue = packet::PacketView<packet::kLittleEndian>;
+  using UpperDequeue = packet::BasePacketBuilder;
+  using UpperQueueDownEnd = common::BidiQueueEnd<UpperEnqueue, UpperDequeue>;
+  LeCreditBasedDataController(Cid cid, Cid remote_cid, UpperQueueDownEnd* channel_queue_end, os::Handler* handler,
+                              Scheduler* scheduler);
+
+  void OnSdu(std::unique_ptr<packet::BasePacketBuilder> sdu) override;
+  void OnPdu(packet::PacketView<true> pdu) override;
+  std::unique_ptr<packet::BasePacketBuilder> GetNextPacket() override;
+
+  void EnableFcs(bool enabled) override {}
+  void SetRetransmissionAndFlowControlOptions(const RetransmissionAndFlowControlConfigurationOption& option) override {}
+
+  // TODO: Set MTU and MPS from signalling channel
+  void SetMtu(Mtu mtu);
+  void SetMps(uint16_t mps);
+  // TODO: Handle credits
+  void OnCredit(uint16_t credits);
+
+ private:
+  Cid cid_;
+  Cid remote_cid_;
+  os::EnqueueBuffer<UpperEnqueue> enqueue_buffer_;
+  os::Handler* handler_;
+  std::queue<std::unique_ptr<packet::BasePacketBuilder>> pdu_queue_;
+  Scheduler* scheduler_;
+  Mtu mtu_ = 512;
+  uint16_t mps_ = 251;
+  uint16_t credits_ = 0;
+
+  class PacketViewForReassembly : public packet::PacketView<kLittleEndian> {
+   public:
+    PacketViewForReassembly(const PacketView& packetView) : PacketView(packetView) {}
+    void AppendPacketView(packet::PacketView<kLittleEndian> to_append) {
+      Append(to_append);
+    }
+  };
+  PacketViewForReassembly reassembly_stage_{std::make_shared<std::vector<uint8_t>>()};
+  uint16_t remaining_sdu_continuation_packet_size_ = 0;
+};
+
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/le_credit_based_channel_data_controller_test.cc b/gd/l2cap/internal/le_credit_based_channel_data_controller_test.cc
new file mode 100644
index 0000000..071e976
--- /dev/null
+++ b/gd/l2cap/internal/le_credit_based_channel_data_controller_test.cc
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "l2cap/internal/le_credit_based_channel_data_controller.h"
+
+#include <gtest/gtest.h>
+
+#include "l2cap/internal/scheduler_mock.h"
+#include "l2cap/l2cap_packets.h"
+#include "packet/raw_builder.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+namespace {
+
+std::unique_ptr<packet::BasePacketBuilder> CreateSdu(std::vector<uint8_t> payload) {
+  auto raw_builder = std::make_unique<packet::RawBuilder>();
+  raw_builder->AddOctets(payload);
+  return raw_builder;
+}
+
+PacketView<kLittleEndian> GetPacketView(std::unique_ptr<packet::BasePacketBuilder> packet) {
+  auto bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter i(*bytes);
+  bytes->reserve(packet->size());
+  packet->Serialize(i);
+  return packet::PacketView<packet::kLittleEndian>(bytes);
+}
+
+void sync_handler(os::Handler* handler) {
+  std::promise<void> promise;
+  auto future = promise.get_future();
+  handler->Post(common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)));
+  auto status = future.wait_for(std::chrono::milliseconds(300));
+  EXPECT_EQ(status, std::future_status::ready);
+}
+
+class LeCreditBasedDataControllerTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
+    user_handler_ = new os::Handler(thread_);
+    queue_handler_ = new os::Handler(thread_);
+  }
+
+  void TearDown() override {
+    queue_handler_->Clear();
+    user_handler_->Clear();
+    delete queue_handler_;
+    delete user_handler_;
+    delete thread_;
+  }
+
+  os::Thread* thread_ = nullptr;
+  os::Handler* user_handler_ = nullptr;
+  os::Handler* queue_handler_ = nullptr;
+};
+
+TEST_F(LeCreditBasedDataControllerTest, transmit_unsegmented) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  LeCreditBasedDataController controller{0x41, 0x41, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  EXPECT_CALL(scheduler, OnPacketsReady(0x41, 1));
+  controller.OnSdu(CreateSdu({'a', 'b', 'c', 'd'}));
+  auto next_packet = controller.GetNextPacket();
+  EXPECT_NE(next_packet, nullptr);
+  auto view = GetPacketView(std::move(next_packet));
+  auto pdu_view = BasicFrameView::Create(view);
+  EXPECT_TRUE(pdu_view.IsValid());
+  auto first_le_info_view = FirstLeInformationFrameView::Create(pdu_view);
+  EXPECT_TRUE(first_le_info_view.IsValid());
+  auto payload = first_le_info_view.GetPayload();
+  std::string data = std::string(payload.begin(), payload.end());
+  EXPECT_EQ(data, "abcd");
+}
+
+TEST_F(LeCreditBasedDataControllerTest, transmit_segmented) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  LeCreditBasedDataController controller{0x41, 0x41, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  controller.SetMps(4);
+  EXPECT_CALL(scheduler, OnPacketsReady(0x41, 2));
+  // Should be divided into 'ab', and 'cd'
+  controller.OnSdu(CreateSdu({'a', 'b', 'c', 'd'}));
+  auto next_packet = controller.GetNextPacket();
+  EXPECT_NE(next_packet, nullptr);
+  auto view = GetPacketView(std::move(next_packet));
+  auto pdu_view = BasicFrameView::Create(view);
+  EXPECT_TRUE(pdu_view.IsValid());
+  auto first_le_info_view = FirstLeInformationFrameView::Create(pdu_view);
+  EXPECT_TRUE(first_le_info_view.IsValid());
+  auto payload = first_le_info_view.GetPayload();
+  std::string data = std::string(payload.begin(), payload.end());
+  EXPECT_EQ(data, "ab");
+  EXPECT_EQ(first_le_info_view.GetL2capSduLength(), 4);
+
+  next_packet = controller.GetNextPacket();
+  EXPECT_NE(next_packet, nullptr);
+  view = GetPacketView(std::move(next_packet));
+  pdu_view = BasicFrameView::Create(view);
+  EXPECT_TRUE(pdu_view.IsValid());
+  payload = pdu_view.GetPayload();
+  data = std::string(payload.begin(), payload.end());
+  EXPECT_EQ(data, "cd");
+}
+
+TEST_F(LeCreditBasedDataControllerTest, receive_unsegmented) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  LeCreditBasedDataController controller{0x41, 0x41, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  auto segment = CreateSdu({'a', 'b', 'c', 'd'});
+  auto builder = FirstLeInformationFrameBuilder::Create(0x41, 4, std::move(segment));
+  auto base_view = GetPacketView(std::move(builder));
+  controller.OnPdu(base_view);
+  sync_handler(queue_handler_);
+  auto payload = channel_queue.GetUpEnd()->TryDequeue();
+  EXPECT_NE(payload, nullptr);
+  std::string data = std::string(payload->begin(), payload->end());
+  EXPECT_EQ(data, "abcd");
+}
+
+TEST_F(LeCreditBasedDataControllerTest, receive_segmented) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  LeCreditBasedDataController controller{0x41, 0x41, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  auto segment1 = CreateSdu({'a', 'b', 'c', 'd'});
+  auto builder1 = FirstLeInformationFrameBuilder::Create(0x41, 7, std::move(segment1));
+  auto base_view = GetPacketView(std::move(builder1));
+  controller.OnPdu(base_view);
+  auto segment2 = CreateSdu({'e', 'f', 'g'});
+  auto builder2 = BasicFrameBuilder::Create(0x41, std::move(segment2));
+  base_view = GetPacketView(std::move(builder2));
+  controller.OnPdu(base_view);
+  sync_handler(queue_handler_);
+  auto payload = channel_queue.GetUpEnd()->TryDequeue();
+  EXPECT_NE(payload, nullptr);
+  std::string data = std::string(payload->begin(), payload->end());
+  EXPECT_EQ(data, "abcdefg");
+}
+
+TEST_F(LeCreditBasedDataControllerTest, receive_segmented_with_wrong_sdu_length) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  LeCreditBasedDataController controller{0x41, 0x41, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  auto segment1 = CreateSdu({'a', 'b', 'c', 'd'});
+  auto builder1 = FirstLeInformationFrameBuilder::Create(0x41, 5, std::move(segment1));
+  auto base_view = GetPacketView(std::move(builder1));
+  controller.OnPdu(base_view);
+  auto segment2 = CreateSdu({'e', 'f', 'g'});
+  auto builder2 = BasicFrameBuilder::Create(0x41, std::move(segment2));
+  base_view = GetPacketView(std::move(builder2));
+  controller.OnPdu(base_view);
+  sync_handler(queue_handler_);
+  auto payload = channel_queue.GetUpEnd()->TryDequeue();
+  EXPECT_EQ(payload, nullptr);
+}
+
+}  // namespace
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/receiver.cc b/gd/l2cap/internal/receiver.cc
index 437064e..e92de0b 100644
--- a/gd/l2cap/internal/receiver.cc
+++ b/gd/l2cap/internal/receiver.cc
@@ -48,7 +48,7 @@
     LOG_WARN("Received a packet with invalid cid: %d", cid);
     return;
   }
-  data_controller->OnPdu(basic_frame_view);
+  data_controller->OnPdu(*packet);
 }
 
 }  // namespace internal
diff --git a/gd/l2cap/internal/scheduler.h b/gd/l2cap/internal/scheduler.h
index b7bd237..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.
@@ -70,11 +72,6 @@
   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 cd14f6f..7454cbe 100644
--- a/gd/l2cap/internal/scheduler_fifo.cc
+++ b/gd/l2cap/internal/scheduler_fifo.cc
@@ -40,6 +40,9 @@
   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) {
@@ -79,11 +82,6 @@
   link_queue_enqueue_registered_ = true;
 }
 
-void Fifo::SetChannelRetransmissionFlowControlMode(Cid cid, RetransmissionAndFlowControlModeOption mode) {
-  ASSERT(sender_map_.find(cid) != sender_map_.end());
-  sender_map_.find(cid)->second.SetChannelRetransmissionFlowControlMode(mode);
-}
-
 DataController* Fifo::GetDataController(Cid cid) {
   if (sender_map_.find(cid) == sender_map_.end()) {
     return nullptr;
diff --git a/gd/l2cap/internal/scheduler_fifo.h b/gd/l2cap/internal/scheduler_fifo.h
index ffbd086..b20ea4f 100644
--- a/gd/l2cap/internal/scheduler_fifo.h
+++ b/gd/l2cap/internal/scheduler_fifo.h
@@ -39,7 +39,6 @@
   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:
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..7fe1dd7 100644
--- a/gd/l2cap/internal/sender.cc
+++ b/gd/l2cap/internal/sender.cc
@@ -55,22 +55,32 @@
   return data_controller_->GetNextPacket();
 }
 
-void Sender::SetChannelRetransmissionFlowControlMode(RetransmissionAndFlowControlModeOption mode) {
-  if (mode_ == mode) {
+void Sender::SetChannelRetransmissionFlowControlMode(const RetransmissionAndFlowControlConfigurationOption& option) {
+  if (mode_ == option.mode_) {
     return;
   }
-  if (mode_ == RetransmissionAndFlowControlModeOption::L2CAP_BASIC) {
+  if (option.mode_ == RetransmissionAndFlowControlModeOption::L2CAP_BASIC) {
     data_controller_ =
         std::make_unique<BasicModeDataController>(channel_id_, remote_channel_id_, queue_end_, handler_, scheduler_);
     return;
   }
-  if (mode == RetransmissionAndFlowControlModeOption::ENHANCED_RETRANSMISSION) {
+  if (option.mode_ == RetransmissionAndFlowControlModeOption::ENHANCED_RETRANSMISSION) {
     data_controller_ =
         std::make_unique<ErtmController>(channel_id_, remote_channel_id_, queue_end_, handler_, scheduler_);
+    data_controller_->SetRetransmissionAndFlowControlOptions(option);
     return;
   }
 }
 
+void Sender::SetFcsType(FcsType fcs_type) {
+  // TODO: FCS is enabled when "not both side explicitly disable it".
+  data_controller_->EnableFcs(fcs_type == FcsType::DEFAULT);
+}
+
+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 8d3a72e..850a88d 100644
--- a/gd/l2cap/internal/sender.h
+++ b/gd/l2cap/internal/sender.h
@@ -25,6 +25,8 @@
 #include "l2cap/cid.h"
 #include "l2cap/internal/channel_impl.h"
 #include "l2cap/internal/data_controller.h"
+#include "l2cap/l2cap_packets.h"
+#include "l2cap/mtu.h"
 #include "os/handler.h"
 #include "os/queue.h"
 #include "packet/base_packet_builder.h"
@@ -59,7 +61,9 @@
    */
   std::unique_ptr<UpperDequeue> GetNextPacket();
 
-  void SetChannelRetransmissionFlowControlMode(RetransmissionAndFlowControlModeOption mode);
+  void SetChannelRetransmissionFlowControlMode(const RetransmissionAndFlowControlConfigurationOption& option);
+  void SetFcsType(FcsType fcs_type);
+  void SetIncomingMtu(Mtu mtu);
 
   DataController* GetDataController();
 
diff --git a/gd/l2cap/internal/sender_test.cc b/gd/l2cap/internal/sender_test.cc
index c3a913d..cb16245 100644
--- a/gd/l2cap/internal/sender_test.cc
+++ b/gd/l2cap/internal/sender_test.cc
@@ -67,8 +67,6 @@
     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));
     sender_ = new Sender(queue_handler_, &scheduler_, mock_channel_);
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/fixed_channel.cc b/gd/l2cap/le/fixed_channel.cc
index 1e6a163..d02dad6 100644
--- a/gd/l2cap/le/fixed_channel.cc
+++ b/gd/l2cap/le/fixed_channel.cc
@@ -49,4 +49,4 @@
 }
 }  // namespace le
 }  // namespace l2cap
-}  // namespace bluetooth
\ No newline at end of file
+}  // namespace bluetooth
diff --git a/gd/l2cap/le/fixed_channel_manager.h b/gd/l2cap/le/fixed_channel_manager.h
index ea1a346..4583310 100644
--- a/gd/l2cap/le/fixed_channel_manager.h
+++ b/gd/l2cap/le/fixed_channel_manager.h
@@ -50,9 +50,9 @@
     hci::ErrorCode hci_error = hci::ErrorCode::SUCCESS;
   };
   /**
-   * OnConnectionFailureCallback(std::string failure_reason);
+   * OnConnectionFailureCallback(ConnectionResult failure_reason);
    */
-  using OnConnectionFailureCallback = common::OnceCallback<void(ConnectionResult result)>;
+  using OnConnectionFailureCallback = common::OnceCallback<void(ConnectionResult)>;
 
   /**
    * OnConnectionOpenCallback(FixedChannel channel);
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/l2cap/le/internal/fixed_channel_impl_test.cc b/gd/l2cap/le/internal/fixed_channel_impl_test.cc
index 9874936..a4270b4 100644
--- a/gd/l2cap/le/internal/fixed_channel_impl_test.cc
+++ b/gd/l2cap/le/internal/fixed_channel_impl_test.cc
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 #include "l2cap/le/internal/fixed_channel_impl.h"
-
 #include "common/testing/bind_test_util.h"
+#include "hci/address_with_type.h"
 #include "l2cap/cid.h"
 #include "l2cap/internal/parameter_provider_mock.h"
 #include "l2cap/le/internal/link_mock.h"
diff --git a/gd/l2cap/le/internal/link.h b/gd/l2cap/le/internal/link.h
index 5dc8ae4..783e03b 100644
--- a/gd/l2cap/le/internal/link.h
+++ b/gd/l2cap/le/internal/link.h
@@ -90,6 +90,10 @@
     }
   }
 
+  virtual std::string ToString() {
+    return GetDevice().ToString();
+  }
+
  private:
   os::Handler* l2cap_handler_;
   l2cap::internal::FixedChannelAllocator<FixedChannelImpl, Link> fixed_channel_allocator_{this, l2cap_handler_};
diff --git a/gd/l2cap/le/internal/link_mock.h b/gd/l2cap/le/internal/link_mock.h
index 3411c95..08ea629 100644
--- a/gd/l2cap/le/internal/link_mock.h
+++ b/gd/l2cap/le/internal/link_mock.h
@@ -16,7 +16,7 @@
 #pragma once
 
 #include "hci/acl_manager_mock.h"
-#include "hci/address.h"
+#include "hci/address_with_type.h"
 #include "l2cap/internal/scheduler_mock.h"
 #include "l2cap/le/internal/link.h"
 
@@ -50,4 +50,4 @@
 }  // namespace internal
 }  // namespace le
 }  // namespace l2cap
-}  // namespace bluetooth
\ No newline at end of file
+}  // namespace bluetooth
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/packet/fragmenting_inserter_unittest.cc b/gd/packet/fragmenting_inserter_unittest.cc
index 6179959..c2f1124 100644
--- a/gd/packet/fragmenting_inserter_unittest.cc
+++ b/gd/packet/fragmenting_inserter_unittest.cc
@@ -91,6 +91,36 @@
   ASSERT_EQ(checksum, observer.GetValue());
 }
 
+TEST(FragmentingInserterTest, testMtuBoundaries) {
+  constexpr size_t kPacketSize = 1024;
+  auto counts = RawBuilder();
+  for (size_t i = 0; i < kPacketSize; i++) {
+    counts.AddOctets1(static_cast<uint8_t>(i));
+  }
+
+  std::vector<std::unique_ptr<RawBuilder>> fragments_mtu_is_kPacketSize;
+  FragmentingInserter it(kPacketSize, std::back_insert_iterator(fragments_mtu_is_kPacketSize));
+  counts.Serialize(it);
+  it.finalize();
+  ASSERT_EQ(1, fragments_mtu_is_kPacketSize.size());
+  ASSERT_EQ(kPacketSize, fragments_mtu_is_kPacketSize[0]->size());
+
+  std::vector<std::unique_ptr<RawBuilder>> fragments_mtu_is_less;
+  FragmentingInserter it_less(kPacketSize - 1, std::back_insert_iterator(fragments_mtu_is_less));
+  counts.Serialize(it_less);
+  it_less.finalize();
+  ASSERT_EQ(2, fragments_mtu_is_less.size());
+  ASSERT_EQ(kPacketSize - 1, fragments_mtu_is_less[0]->size());
+  ASSERT_EQ(1, fragments_mtu_is_less[1]->size());
+
+  std::vector<std::unique_ptr<RawBuilder>> fragments_mtu_is_more;
+  FragmentingInserter it_more(kPacketSize + 1, std::back_insert_iterator(fragments_mtu_is_more));
+  counts.Serialize(it_more);
+  it_more.finalize();
+  ASSERT_EQ(1, fragments_mtu_is_more.size());
+  ASSERT_EQ(kPacketSize, fragments_mtu_is_more[0]->size());
+}
+
 constexpr size_t kPacketSize = 128;
 class FragmentingTest : public ::testing::TestWithParam<size_t> {
  public:
diff --git a/gd/packet/iterator.h b/gd/packet/iterator.h
index f15e561..8d927a0 100644
--- a/gd/packet/iterator.h
+++ b/gd/packet/iterator.h
@@ -18,6 +18,7 @@
 
 #include <cstdint>
 #include <forward_list>
+#include <memory>
 
 #include "packet/view.h"
 
diff --git a/gd/packet/parser/fields/custom_field_fixed_size.cc b/gd/packet/parser/fields/custom_field_fixed_size.cc
index 029f0aa..687d48d 100644
--- a/gd/packet/parser/fields/custom_field_fixed_size.cc
+++ b/gd/packet/parser/fields/custom_field_fixed_size.cc
@@ -55,6 +55,10 @@
   // Do nothing.
 }
 
+void CustomFieldFixedSize::GenInserter(std::ostream& s) const {
+  s << "insert(" << GetName() << "_, i);";
+}
+
 void CustomFieldFixedSize::GenValidator(std::ostream&) const {
   // Do nothing.
 }
diff --git a/gd/packet/parser/fields/custom_field_fixed_size.h b/gd/packet/parser/fields/custom_field_fixed_size.h
index bd19eb0..97acff9 100644
--- a/gd/packet/parser/fields/custom_field_fixed_size.h
+++ b/gd/packet/parser/fields/custom_field_fixed_size.h
@@ -37,6 +37,8 @@
 
   virtual void GenParameterValidator(std::ostream&) const override;
 
+  virtual void GenInserter(std::ostream& s) const override;
+
   virtual void GenValidator(std::ostream&) const override;
 
   std::string type_name_;
diff --git a/gd/packet/parser/fields/scalar_field.cc b/gd/packet/parser/fields/scalar_field.cc
index c6d2ecf..320534a 100644
--- a/gd/packet/parser/fields/scalar_field.cc
+++ b/gd/packet/parser/fields/scalar_field.cc
@@ -121,8 +121,6 @@
 void ScalarField::GenInserter(std::ostream& s) const {
   if (GetSize().bits() == 8) {
     s << "i.insert_byte(" << GetName() << "_);";
-  } else if (GetSize().bits() % 8 == 0) {
-    s << "insert(" << GetName() << "_, i);";
   } else {
     s << "insert(" << GetName() << "_, i," << GetSize().bits() << ");";
   }
diff --git a/gd/packet/parser/main.cc b/gd/packet/parser/main.cc
index 05475b6..757523d 100644
--- a/gd/packet/parser/main.cc
+++ b/gd/packet/parser/main.cc
@@ -230,9 +230,16 @@
   return true;
 }
 
+// Get the out_file shard at a symbol_count
+std::ofstream& get_out_file(size_t symbol_count, size_t symbol_total, std::vector<std::ofstream>* out_files) {
+  auto symbols_per_shard = symbol_total / out_files->size();
+  auto file_index = std::min(symbol_count / symbols_per_shard, out_files->size() - 1);
+  return out_files->at(file_index);
+}
+
 bool generate_pybind11_sources_one_file(const Declarations& decls, const std::filesystem::path& input_file,
                                         const std::filesystem::path& include_dir, const std::filesystem::path& out_dir,
-                                        const std::string& root_namespace) {
+                                        const std::string& root_namespace, size_t num_shards) {
   auto gen_relative_path = input_file.lexically_relative(include_dir).parent_path();
 
   auto input_filename = input_file.filename().string().substr(0, input_file.filename().string().find(".pdl"));
@@ -241,85 +248,132 @@
   std::filesystem::create_directories(gen_path);
 
   auto gen_relative_header = gen_relative_path / (input_filename + ".h");
-  auto gen_file = gen_path / (input_filename + "_python3.cc");
-
-  std::ofstream out_file;
-  out_file.open(gen_file);
-  if (!out_file.is_open()) {
-    std::cerr << "can't open " << gen_file << std::endl;
-    return false;
-  }
-
-  out_file << "#include <pybind11/pybind11.h>\n";
-  out_file << "#include <pybind11/stl.h>\n";
-  out_file << "\n\n";
-  out_file << "#include " << gen_relative_header << "\n";
-  out_file << "\n\n";
 
   std::vector<std::string> namespace_list;
   parse_namespace(root_namespace, gen_relative_path, &namespace_list);
-  generate_namespace_open(namespace_list, out_file);
-  out_file << "\n\n";
 
-  for (const auto& c : decls.type_defs_queue_) {
-    if (c.second->GetDefinitionType() == TypeDef::Type::CUSTOM ||
-        c.second->GetDefinitionType() == TypeDef::Type::CHECKSUM) {
-      const auto* custom_def = dynamic_cast<const CustomFieldDef*>(c.second);
-      custom_def->GenUsing(out_file);
+  std::vector<std::ofstream> out_file_shards(num_shards);
+  for (size_t i = 0; i < out_file_shards.size(); i++) {
+    auto filename = gen_path / (input_filename + "_python3_shard_" + std::to_string(i) + ".cc");
+    auto& out_file = out_file_shards[i];
+    out_file.open(filename);
+    if (!out_file.is_open()) {
+      std::cerr << "can't open " << filename << std::endl;
+      return false;
+    }
+    out_file << "#include <pybind11/pybind11.h>\n";
+    out_file << "#include <pybind11/stl.h>\n";
+    out_file << "\n\n";
+    out_file << "#include " << gen_relative_header << "\n";
+    out_file << "\n\n";
+
+    generate_namespace_open(namespace_list, out_file);
+    out_file << "\n\n";
+
+    for (const auto& c : decls.type_defs_queue_) {
+      if (c.second->GetDefinitionType() == TypeDef::Type::CUSTOM ||
+          c.second->GetDefinitionType() == TypeDef::Type::CHECKSUM) {
+        const auto* custom_def = dynamic_cast<const CustomFieldDef*>(c.second);
+        custom_def->GenUsing(out_file);
+      }
+    }
+    out_file << "\n\n";
+
+    out_file << "using ::bluetooth::packet::BasePacketBuilder;";
+    out_file << "using ::bluetooth::packet::BitInserter;";
+    out_file << "using ::bluetooth::packet::CustomTypeChecker;";
+    out_file << "using ::bluetooth::packet::Iterator;";
+    out_file << "using ::bluetooth::packet::kLittleEndian;";
+    out_file << "using ::bluetooth::packet::PacketBuilder;";
+    out_file << "using ::bluetooth::packet::BaseStruct;";
+    out_file << "using ::bluetooth::packet::PacketStruct;";
+    out_file << "using ::bluetooth::packet::PacketView;";
+    out_file << "using ::bluetooth::packet::parser::ChecksumTypeChecker;";
+    out_file << "\n\n";
+
+    out_file << "namespace py = pybind11;\n\n";
+
+    out_file << "void define_" << input_filename << "_submodule_shard_" << std::to_string(i) << "(py::module& m) {\n\n";
+  }
+  size_t symbol_total = 0;
+  // Only count types that will be generated
+  for (const auto& e : decls.type_defs_queue_) {
+    if (e.second->GetDefinitionType() == TypeDef::Type::ENUM) {
+      symbol_total++;
+    } else if (e.second->GetDefinitionType() == TypeDef::Type::STRUCT) {
+      symbol_total++;
     }
   }
-  out_file << "\n\n";
-
-  out_file << "using ::bluetooth::packet::BasePacketBuilder;";
-  out_file << "using ::bluetooth::packet::BitInserter;";
-  out_file << "using ::bluetooth::packet::CustomTypeChecker;";
-  out_file << "using ::bluetooth::packet::Iterator;";
-  out_file << "using ::bluetooth::packet::kLittleEndian;";
-  out_file << "using ::bluetooth::packet::PacketBuilder;";
-  out_file << "using ::bluetooth::packet::BaseStruct;";
-  out_file << "using ::bluetooth::packet::PacketStruct;";
-  out_file << "using ::bluetooth::packet::PacketView;";
-  out_file << "using ::bluetooth::packet::parser::ChecksumTypeChecker;";
-  out_file << "\n\n";
-
-  out_file << "namespace py = pybind11;\n\n";
-
-  out_file << "void define_" << input_filename << "_submodule(py::module& parent) {\n\n";
-  out_file << "py::module m = parent.def_submodule(\"" << input_filename << "\", \"A submodule of " << input_filename
-           << "\");\n\n";
+  // View and builder are counted separately
+  symbol_total += decls.packet_defs_queue_.size() * 2;
+  size_t symbol_count = 0;
 
   for (const auto& e : decls.type_defs_queue_) {
     if (e.second->GetDefinitionType() == TypeDef::Type::ENUM) {
       const auto* enum_def = dynamic_cast<const EnumDef*>(e.second);
       EnumGen gen(*enum_def);
+      auto& out_file = get_out_file(symbol_count, symbol_total, &out_file_shards);
       gen.GenDefinitionPybind11(out_file);
       out_file << "\n\n";
+      symbol_count++;
     }
   }
 
   for (const auto& s : decls.type_defs_queue_) {
     if (s.second->GetDefinitionType() == TypeDef::Type::STRUCT) {
       const auto* struct_def = dynamic_cast<const StructDef*>(s.second);
+      auto& out_file = get_out_file(symbol_count, symbol_total, &out_file_shards);
       struct_def->GenDefinitionPybind11(out_file);
       out_file << "\n";
+      symbol_count++;
     }
   }
 
   for (const auto& packet_def : decls.packet_defs_queue_) {
+    auto& out_file = get_out_file(symbol_count, symbol_total, &out_file_shards);
     packet_def.second.GenParserDefinitionPybind11(out_file);
     out_file << "\n\n";
+    symbol_count++;
   }
 
   for (const auto& p : decls.packet_defs_queue_) {
+    auto& out_file = get_out_file(symbol_count, symbol_total, &out_file_shards);
     p.second.GenBuilderDefinitionPybind11(out_file);
     out_file << "\n\n";
+    symbol_count++;
   }
 
-  out_file << "}\n\n";
+  for (auto& out_file : out_file_shards) {
+    out_file << "}\n\n";
+    generate_namespace_close(namespace_list, out_file);
+  }
 
-  generate_namespace_close(namespace_list, out_file);
+  auto gen_file_main = gen_path / (input_filename + "_python3.cc");
+  std::ofstream out_file_main;
+  out_file_main.open(gen_file_main);
+  if (!out_file_main.is_open()) {
+    std::cerr << "can't open " << gen_file_main << std::endl;
+    return false;
+  }
+  out_file_main << "#include <pybind11/pybind11.h>\n";
+  generate_namespace_open(namespace_list, out_file_main);
 
-  out_file.close();
+  out_file_main << "namespace py = pybind11;\n\n";
+
+  for (size_t i = 0; i < out_file_shards.size(); i++) {
+    out_file_main << "void define_" << input_filename << "_submodule_shard_" << std::to_string(i)
+                  << "(py::module& m);\n";
+  }
+
+  out_file_main << "void define_" << input_filename << "_submodule(py::module& parent) {\n\n";
+  out_file_main << "py::module m = parent.def_submodule(\"" << input_filename << "\", \"A submodule of "
+                << input_filename << "\");\n\n";
+  for (size_t i = 0; i < out_file_shards.size(); i++) {
+    out_file_main << "define_" << input_filename << "_submodule_shard_" << std::to_string(i) << "(m);\n";
+  }
+  out_file_main << "}\n\n";
+
+  generate_namespace_close(namespace_list, out_file_main);
 
   return true;
 }
@@ -335,10 +389,13 @@
   std::filesystem::path out_dir;
   std::filesystem::path include_dir;
   std::string root_namespace = "bluetooth";
+  // Number of shards per output pybind11 cc file
+  size_t num_shards = 1;
   std::queue<std::filesystem::path> input_files;
   const std::string arg_out = "--out=";
   const std::string arg_include = "--include=";
   const std::string arg_namespace = "--root_namespace=";
+  const std::string arg_num_shards = "--num_shards=";
 
   for (int i = 1; i < argc; i++) {
     std::string arg = argv[i];
@@ -348,12 +405,15 @@
       include_dir = std::filesystem::current_path() / std::filesystem::path(arg.substr(arg_include.size()));
     } else if (arg.find(arg_namespace) == 0) {
       root_namespace = arg.substr(arg_namespace.size());
+    } else if (arg.find(arg_num_shards) == 0) {
+      num_shards = std::stoul(arg.substr(arg_num_shards.size()));
     } else {
       input_files.emplace(std::filesystem::current_path() / std::filesystem::path(arg));
     }
   }
-  if (out_dir == std::filesystem::path() || include_dir == std::filesystem::path()) {
-    std::cerr << "Usage: bt-packetgen --out=OUT --include=INCLUDE --root=NAMESPACE input_files..." << std::endl;
+  if (out_dir == std::filesystem::path() || include_dir == std::filesystem::path() || num_shards == 0) {
+    std::cerr << "Usage: bt-packetgen --out=OUT --include=INCLUDE --root_namespace=NAMESPACE --num_shards=NUM_SHARDS "
+              << "input_files..." << std::endl;
     return 1;
   }
 
@@ -367,7 +427,8 @@
       std::cerr << "Didn't generate cpp headers for " << input_files.front() << std::endl;
       return 3;
     }
-    if (!generate_pybind11_sources_one_file(declarations, input_files.front(), include_dir, out_dir, root_namespace)) {
+    if (!generate_pybind11_sources_one_file(declarations, input_files.front(), include_dir, out_dir, root_namespace,
+                                            num_shards)) {
       std::cerr << "Didn't generate pybind11 sources for " << input_files.front() << std::endl;
       return 4;
     }
diff --git a/gd/packet/parser/test/generated_packet_test.cc b/gd/packet/parser/test/generated_packet_test.cc
index 06f60b5..f8454a2 100644
--- a/gd/packet/parser/test/generated_packet_test.cc
+++ b/gd/packet/parser/test/generated_packet_test.cc
@@ -1825,6 +1825,58 @@
     ASSERT_EQ(ltv_vector[i].value_, an_array[i].value_);
   }
 }
+
+vector<uint8_t> byte_sized{
+    0x11,                                            // 1
+    0x21, 0x22,                                      // 2
+    0x31, 0x32, 0x33,                                // 3
+    0x41, 0x42, 0x43, 0x44,                          // 4
+    0x51, 0x52, 0x53, 0x54, 0x55,                    // 5
+    0x61, 0x62, 0x63, 0x64, 0x65, 0x66,              // 6
+    0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,        // 7
+    0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,  // 8
+};
+
+TEST(GeneratedPacketTest, testByteSizedFields) {
+  uint64_t array[9]{
+      0xbadbadbad,
+      0x11,                // 1
+      0x2221,              // 2
+      0x333231,            // 3
+      0x44434241,          // 4
+      0x5554535251,        // 5
+      0x666564636261,      // 6
+      0x77767574737271,    // 7
+      0x8887868584838281,  // 8
+  };
+  auto packet =
+      ByteSizedFieldsBuilder::Create(array[1], array[2], array[3], array[4], array[5], array[6], array[7], array[8]);
+  ASSERT_EQ(byte_sized.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(byte_sized.size(), packet_bytes->size());
+  for (size_t i = 0; i < byte_sized.size(); i++) {
+    ASSERT_EQ(byte_sized[i], packet_bytes->at(i));
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = ByteSizedFieldsView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  ASSERT_EQ(array[1], view.GetOne());
+  ASSERT_EQ(array[2], view.GetTwo());
+  ASSERT_EQ(array[3], view.GetThree());
+  ASSERT_EQ(array[4], view.GetFour());
+  ASSERT_EQ(array[5], view.GetFive());
+  ASSERT_EQ(array[6], view.GetSix());
+  ASSERT_EQ(array[7], view.GetSeven());
+  ASSERT_EQ(array[8], view.GetEight());
+}
+
+DEFINE_AND_INSTANTIATE_ByteSizedFieldsReflectionTest(byte_sized);
+
 }  // namespace parser
 }  // namespace packet
 }  // namespace bluetooth
diff --git a/gd/packet/parser/test/test_packets.pdl b/gd/packet/parser/test/test_packets.pdl
index 3c24509..2b612cc 100644
--- a/gd/packet/parser/test/test_packets.pdl
+++ b/gd/packet/parser/test/test_packets.pdl
@@ -394,3 +394,14 @@
   one_array : LengthTypeValueStruct[],
   _padding_[40],
 }
+
+packet ByteSizedFields {
+  one : 8,
+  two : 16,
+  three : 24,
+  four : 32,
+  five : 40,
+  six : 48,
+  seven : 56,
+  eight : 64,
+}
diff --git a/gd/packet/raw_builder.cc b/gd/packet/raw_builder.cc
index ec5159b..ec2ff68 100644
--- a/gd/packet/raw_builder.cc
+++ b/gd/packet/raw_builder.cc
@@ -17,6 +17,7 @@
 #include "packet/raw_builder.h"
 
 #include <algorithm>
+#include <utility>
 
 #include "os/log.h"
 
@@ -27,7 +28,7 @@
 namespace packet {
 
 RawBuilder::RawBuilder(size_t max_bytes) : max_bytes_(max_bytes) {}
-RawBuilder::RawBuilder(std::vector<uint8_t> vec) : max_bytes_(vec.size()), payload_(vec) {}
+RawBuilder::RawBuilder(std::vector<uint8_t> vec) : payload_(std::move(vec)) {}
 
 bool RawBuilder::AddOctets(size_t octets, const vector<uint8_t>& bytes) {
   if (payload_.size() + octets > max_bytes_) return false;
diff --git a/gd/packet/raw_builder.h b/gd/packet/raw_builder.h
index 9b0e959..1c9552a 100644
--- a/gd/packet/raw_builder.h
+++ b/gd/packet/raw_builder.h
@@ -64,7 +64,7 @@
   // - the new size of the payload is still <= |max_bytes_|
   bool AddOctets(size_t octets, uint64_t value);
 
-  size_t max_bytes_{255};
+  size_t max_bytes_{0xffff};
 
   // Underlying containers for storing the actual packet
   std::vector<uint8_t> payload_;
diff --git a/gd/packet/raw_builder_unittest.cc b/gd/packet/raw_builder_unittest.cc
index 5210fb6..64ca0ed 100644
--- a/gd/packet/raw_builder_unittest.cc
+++ b/gd/packet/raw_builder_unittest.cc
@@ -65,5 +65,48 @@
   ASSERT_EQ(count, packet);
 }
 
+TEST(RawBuilderTest, buildStartingWithVector) {
+  std::vector<uint8_t> count_first(count.begin(), count.begin() + 0x8);
+  std::unique_ptr<RawBuilder> count_builder = std::make_unique<RawBuilder>(count_first);
+  count_builder->AddOctets4(0x0b0a0908);
+  count_builder->AddOctets2(0x0d0c);
+  count_builder->AddOctets1(0x0e);
+  count_builder->AddOctets1(0x0f);
+  count_builder->AddOctets8(0x1716151413121110);
+  std::vector<uint8_t> count_last(count.begin() + 0x18, count.end());
+  count_builder->AddOctets(count_last);
+
+  ASSERT_EQ(count.size(), count_builder->size());
+
+  std::vector<uint8_t> packet;
+  BitInserter it(packet);
+
+  count_builder->Serialize(it);
+
+  ASSERT_EQ(count, packet);
+}
+
+TEST(RawBuilderTest, testMaxBytes) {
+  const size_t kMaxBytes = count.size();
+  std::unique_ptr<RawBuilder> count_builder = std::make_unique<RawBuilder>(kMaxBytes);
+  ASSERT_TRUE(count_builder->AddOctets(count));
+  ASSERT_FALSE(count_builder->AddOctets4(0x0b0a0908));
+  ASSERT_FALSE(count_builder->AddOctets2(0x0d0c));
+  ASSERT_FALSE(count_builder->AddOctets1(0x0e));
+  ASSERT_FALSE(count_builder->AddOctets1(0x0f));
+  ASSERT_FALSE(count_builder->AddOctets8(0x1716151413121110));
+  std::vector<uint8_t> count_last(count.begin() + 0x18, count.end());
+  ASSERT_FALSE(count_builder->AddOctets(count_last));
+
+  ASSERT_EQ(count.size(), count_builder->size());
+
+  std::vector<uint8_t> packet;
+  BitInserter it(packet);
+
+  count_builder->Serialize(it);
+
+  ASSERT_EQ(count, packet);
+}
+
 }  // namespace packet
 }  // namespace bluetooth
diff --git a/gd/packet/view.h b/gd/packet/view.h
index 4f3b508..3b8b679 100644
--- a/gd/packet/view.h
+++ b/gd/packet/view.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <cstdint>
+#include <memory>
 #include <vector>
 
 namespace bluetooth {
diff --git a/gd/security/internal/security_manager_impl.cc b/gd/security/internal/security_manager_impl.cc
index 26179b5..b245ff9 100644
--- a/gd/security/internal/security_manager_impl.cc
+++ b/gd/security/internal/security_manager_impl.cc
@@ -121,20 +121,20 @@
   LOG_ALWAYS_FATAL("Listener has not been registered!");
 }
 
-void SecurityManagerImpl::NotifyDeviceBonded(std::shared_ptr<Device> device) {
+void SecurityManagerImpl::NotifyDeviceBonded(hci::AddressWithType device) {
   for (auto& iter : listeners_) {
     iter.second->Post(common::Bind(&ISecurityManagerListener::OnDeviceBonded, common::Unretained(iter.first), device));
   }
 }
 
-void SecurityManagerImpl::NotifyDeviceBondFailed(std::shared_ptr<Device> device) {
+void SecurityManagerImpl::NotifyDeviceBondFailed(hci::AddressWithType device) {
   for (auto& iter : listeners_) {
     iter.second->Post(
         common::Bind(&ISecurityManagerListener::OnDeviceBondFailed, common::Unretained(iter.first), device));
   }
 }
 
-void SecurityManagerImpl::NotifyDeviceUnbonded(std::shared_ptr<Device> device) {
+void SecurityManagerImpl::NotifyDeviceUnbonded(hci::AddressWithType device) {
   for (auto& iter : listeners_) {
     iter.second->Post(
         common::Bind(&ISecurityManagerListener::OnDeviceUnbonded, common::Unretained(iter.first), device));
diff --git a/gd/security/internal/security_manager_impl.h b/gd/security/internal/security_manager_impl.h
index 23b39e1..d6768c9 100644
--- a/gd/security/internal/security_manager_impl.h
+++ b/gd/security/internal/security_manager_impl.h
@@ -93,9 +93,9 @@
 
  protected:
   std::vector<std::pair<ISecurityManagerListener*, os::Handler*>> listeners_;
-  void NotifyDeviceBonded(std::shared_ptr<bluetooth::hci::Device> device);
-  void NotifyDeviceBondFailed(std::shared_ptr<bluetooth::hci::Device> device);
-  void NotifyDeviceUnbonded(std::shared_ptr<bluetooth::hci::Device> device);
+  void NotifyDeviceBonded(hci::AddressWithType device);
+  void NotifyDeviceBondFailed(hci::AddressWithType device);
+  void NotifyDeviceUnbonded(hci::AddressWithType device);
 
   // ISecurityManagerChannel
   void OnChangeConnectionLinkKeyComplete(std::shared_ptr<hci::Device> device,
diff --git a/gd/security/security_manager.h b/gd/security/security_manager.h
index deb2788..d83c8d3 100644
--- a/gd/security/security_manager.h
+++ b/gd/security/security_manager.h
@@ -21,8 +21,8 @@
 #include <memory>
 #include <vector>
 
+#include "hci/address_with_type.h"
 #include "hci/device.h"
-#include "hci/device_database.h"
 #include "security/internal/security_manager_impl.h"
 
 namespace bluetooth {
@@ -38,23 +38,23 @@
   /**
    * Called when a device is successfully bonded.
    *
-   * @param device pointer to the bonded device
+   * @param address of the newly bonded device
    */
-  virtual void OnDeviceBonded(std::shared_ptr<bluetooth::hci::Device> device) = 0;
+  virtual void OnDeviceBonded(bluetooth::hci::AddressWithType device) = 0;
 
   /**
    * Called when a device is successfully un-bonded.
    *
-   * @param device pointer to the device that is no longer bonded
+   * @param address of device that is no longer bonded
    */
-  virtual void OnDeviceUnbonded(std::shared_ptr<bluetooth::hci::Device> device) = 0;
+  virtual void OnDeviceUnbonded(bluetooth::hci::AddressWithType device) = 0;
 
   /**
    * Called as a result of a failure during the bonding process.
    *
-   * @param device pointer to the device that is no longer bonded
+   * @param address of the device that failed to bond
    */
-  virtual void OnDeviceBondFailed(std::shared_ptr<bluetooth::hci::Device> device) = 0;
+  virtual void OnDeviceBondFailed(bluetooth::hci::AddressWithType device) = 0;
 };
 
 /**
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/main/Android.bp b/main/Android.bp
index ef7dba6..2a5b8cf 100644
--- a/main/Android.bp
+++ b/main/Android.bp
@@ -69,12 +69,9 @@
         "libtinyxml2",
         "libz",
         "libcrypto",
-        "android.hardware.keymaster@4.0",
-        "android.hardware.keymaster@3.0",
-        "libkeymaster4support",
-        "libkeystore_aidl",
-        "libkeystore_binder",
-        "libkeystore_parcelables",
+        //"android.hardware.keymaster@4.0",
+        //"libkeymaster4support",
+        //"libkeystore_binder",
     ],
     static_libs: [
         "libbt-sbc-decoder",
diff --git a/test/rootcanal/bluetooth_hci.cc b/test/rootcanal/bluetooth_hci.cc
index 3a636f5..2bebd9a 100644
--- a/test/rootcanal/bluetooth_hci.cc
+++ b/test/rootcanal/bluetooth_hci.cc
@@ -104,7 +104,9 @@
 
   controller_ = std::make_shared<DualModeController>();
 
-  controller_->Initialize({"dmc", "3C:5A:B4:01:02:03"});
+  char mac_property[PROPERTY_VALUE_MAX] = "";
+  property_get("bt.rootcanal_mac_address", mac_property, "3C:5A:B4:01:02:03");
+  controller_->Initialize({"dmc", std::string(mac_property)});
 
   controller_->RegisterEventChannel(
       [this, cb](std::shared_ptr<std::vector<uint8_t>> packet) {
diff --git a/vendor_libs/test_vendor_lib/model/controller/acl_connection_handler.cc b/vendor_libs/test_vendor_lib/model/controller/acl_connection_handler.cc
index c1fcd84..014015a 100644
--- a/vendor_libs/test_vendor_lib/model/controller/acl_connection_handler.cc
+++ b/vendor_libs/test_vendor_lib/model/controller/acl_connection_handler.cc
@@ -42,19 +42,25 @@
   return unused_handle;
 }
 
-bool AclConnectionHandler::CreatePendingConnection(Address addr) {
+bool AclConnectionHandler::CreatePendingConnection(
+    Address addr, bool authenticate_on_connect) {
   if (classic_connection_pending_) {
     return false;
   }
   classic_connection_pending_ = true;
   pending_connection_address_ = addr;
+  authenticate_pending_classic_connection_ = authenticate_on_connect;
   return true;
 }
 
-bool AclConnectionHandler::HasPendingConnection(Address addr) {
+bool AclConnectionHandler::HasPendingConnection(Address addr) const {
   return classic_connection_pending_ && pending_connection_address_ == addr;
 }
 
+bool AclConnectionHandler::AuthenticatePendingConnection() const {
+  return authenticate_pending_classic_connection_;
+}
+
 bool AclConnectionHandler::CancelPendingConnection(Address addr) {
   if (!classic_connection_pending_ || pending_connection_address_ != addr) {
     return false;
@@ -79,7 +85,8 @@
   return true;
 }
 
-bool AclConnectionHandler::HasPendingLeConnection(Address addr, uint8_t address_type) {
+bool AclConnectionHandler::HasPendingLeConnection(Address addr,
+                                                  uint8_t address_type) const {
   return le_connection_pending_ && pending_le_connection_address_ == addr &&
          pending_le_connection_address_type_ == address_type;
 }
diff --git a/vendor_libs/test_vendor_lib/model/controller/acl_connection_handler.h b/vendor_libs/test_vendor_lib/model/controller/acl_connection_handler.h
index 6b32952..c08f529 100644
--- a/vendor_libs/test_vendor_lib/model/controller/acl_connection_handler.h
+++ b/vendor_libs/test_vendor_lib/model/controller/acl_connection_handler.h
@@ -34,12 +34,13 @@
 
   virtual ~AclConnectionHandler() = default;
 
-  bool CreatePendingConnection(Address addr);
-  bool HasPendingConnection(Address addr);
+  bool CreatePendingConnection(Address addr, bool authenticate_on_connect);
+  bool HasPendingConnection(Address addr) const;
   bool CancelPendingConnection(Address addr);
+  bool AuthenticatePendingConnection() const;
 
   bool CreatePendingLeConnection(Address addr, uint8_t addr_type);
-  bool HasPendingLeConnection(Address addr, uint8_t addr_type);
+  bool HasPendingLeConnection(Address addr, uint8_t addr_type) const;
   bool CancelPendingLeConnection(Address addr, uint8_t addr_type);
 
   uint16_t CreateConnection(Address addr);
@@ -65,10 +66,12 @@
  private:
   std::unordered_map<uint16_t, AclConnection> acl_connections_;
   bool classic_connection_pending_{false};
-  Address pending_connection_address_;
+  Address pending_connection_address_{Address::kEmpty};
+  bool authenticate_pending_classic_connection_{false};
   bool le_connection_pending_{false};
-  Address pending_le_connection_address_;
-  uint8_t pending_le_connection_address_type_;
+  Address pending_le_connection_address_{Address::kEmpty};
+  uint8_t pending_le_connection_address_type_{false};
+
   uint16_t GetUnusedHandle();
   uint16_t last_handle_{acl::kReservedHandle - 2};
   void set_own_address_type(uint16_t handle, uint8_t own_address_type);
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 bc48af9..75b87b0 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
@@ -26,9 +26,8 @@
 #include "packet/raw_builder.h"
 
 #include "hci.h"
-#include "packets/hci/acl_packet_view.h"
 #include "packets/hci/command_packet_view.h"
-#include "packets/hci/sco_packet_view.h"
+#include "packets/packet_view.h"
 
 using std::vector;
 using test_vendor_lib::hci::EventCode;
@@ -50,6 +49,7 @@
 namespace test_vendor_lib {
 constexpr char DualModeController::kControllerPropertiesFile[];
 constexpr uint16_t DualModeController::kSecurityManagerNumKeys;
+constexpr uint16_t kNumCommandPackets = 0x01;
 
 // Device methods.
 void DualModeController::Initialize(const std::vector<std::string>& args) {
@@ -76,16 +76,10 @@
   link_layer_controller_.TimerTick();
 }
 
-void DualModeController::SendCommandCompleteSuccess(
-    bluetooth::hci::OpCode command_opcode) const {
-  SendCommandCompleteOnlyStatus(command_opcode,
-                                bluetooth::hci::ErrorCode::SUCCESS);
-}
-
 void DualModeController::SendCommandCompleteUnknownOpCodeEvent(uint16_t command_opcode) const {
   std::unique_ptr<bluetooth::packet::RawBuilder> raw_builder_ptr =
       std::make_unique<bluetooth::packet::RawBuilder>();
-  raw_builder_ptr->AddOctets1(0x01);  // num_responses
+  raw_builder_ptr->AddOctets1(kNumCommandPackets);
   raw_builder_ptr->AddOctets2(command_opcode);
   raw_builder_ptr->AddOctets1(
       static_cast<uint8_t>(bluetooth::hci::ErrorCode::UNKNOWN_HCI_COMMAND));
@@ -95,32 +89,6 @@
   send_event_(std::move(packet));
 }
 
-void DualModeController::SendCommandCompleteOnlyStatus(
-    bluetooth::hci::OpCode command_opcode,
-    bluetooth::hci::ErrorCode status) const {
-  std::unique_ptr<bluetooth::packet::RawBuilder> raw_builder_ptr =
-      std::make_unique<bluetooth::packet::RawBuilder>();
-  raw_builder_ptr->AddOctets1(static_cast<uint8_t>(status));
-  auto packet = bluetooth::hci::CommandCompleteBuilder::Create(
-      0x01, command_opcode, std::move(raw_builder_ptr));
-  send_event_(std::move(packet));
-}
-
-void DualModeController::SendCommandStatus(
-    bluetooth::hci::ErrorCode status,
-    bluetooth::hci::OpCode command_opcode) const {
-  std::unique_ptr<bluetooth::packet::RawBuilder> raw_builder_ptr =
-      std::make_unique<bluetooth::packet::RawBuilder>();
-  auto packet = bluetooth::hci::CommandStatusBuilder::Create(
-      status, 0x01, command_opcode, std::move(raw_builder_ptr));
-  send_event_(std::move(packet));
-}
-
-void DualModeController::SendCommandStatusSuccess(
-    bluetooth::hci::OpCode command_opcode) const {
-  SendCommandStatus(bluetooth::hci::ErrorCode::SUCCESS, command_opcode);
-}
-
 DualModeController::DualModeController(const std::string& properties_filename, uint16_t num_keys)
     : Device(properties_filename), security_manager_(num_keys) {
   loopback_mode_ = hci::LoopbackMode::NO;
@@ -159,7 +127,7 @@
   SET_HANDLER(OpCode::WRITE_SIMPLE_PAIRING_MODE, HciWriteSimplePairingMode);
   SET_HANDLER(OpCode::WRITE_LE_HOST_SUPPORT, HciWriteLeHostSupport);
   SET_HANDLER(OpCode::WRITE_SECURE_CONNECTIONS_HOST_SUPPORT,
-              HciWriteSecureConnectionHostSupport);
+              HciWriteSecureConnectionsHostSupport);
   SET_HANDLER(OpCode::SET_EVENT_MASK, HciSetEventMask);
   SET_HANDLER(OpCode::WRITE_INQUIRY_MODE, HciWriteInquiryMode);
   SET_HANDLER(OpCode::WRITE_PAGE_SCAN_TYPE, HciWritePageScanType);
@@ -245,7 +213,7 @@
   uint16_t handle = args.begin().extract<uint16_t>();
 
   auto packet = bluetooth::hci::SniffSubratingCompleteBuilder::Create(
-      0x01, bluetooth::hci::ErrorCode::SUCCESS, handle);
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS, handle);
   send_event_(std::move(packet));
 }
 
@@ -265,19 +233,19 @@
 }
 
 void DualModeController::HandleAcl(std::shared_ptr<std::vector<uint8_t>> packet) {
-  auto acl_packet = packets::AclPacketView::Create(packet);
+  bluetooth::hci::PacketView<bluetooth::hci::kLittleEndian> raw_packet(packet);
+  auto acl_packet = bluetooth::hci::AclPacketView::Create(raw_packet);
+  ASSERT(acl_packet.IsValid());
   if (loopback_mode_ == hci::LoopbackMode::LOCAL) {
     uint16_t handle = acl_packet.GetHandle();
 
-    std::unique_ptr<bluetooth::packet::RawBuilder> raw_builder_ptr =
-        std::make_unique<bluetooth::packet::RawBuilder>();
-    raw_builder_ptr->AddOctets1(0x01);
-    raw_builder_ptr->AddOctets2(handle);
-    raw_builder_ptr->AddOctets2(0x01);
-
-    auto packet = bluetooth::hci::EventPacketBuilder::Create(
-        bluetooth::hci::EventCode::NUMBER_OF_COMPLETED_PACKETS,
-        std::move(raw_builder_ptr));
+    std::vector<bluetooth::hci::CompletedPackets> completed_packets;
+    bluetooth::hci::CompletedPackets cp;
+    cp.connection_handle_ = handle;
+    cp.host_num_of_completed_packets_ = kNumCommandPackets;
+    completed_packets.push_back(cp);
+    auto packet = bluetooth::hci::NumberOfCompletedPacketsBuilder::Create(
+        completed_packets);
     send_event_(std::move(packet));
     return;
   }
@@ -286,19 +254,18 @@
 }
 
 void DualModeController::HandleSco(std::shared_ptr<std::vector<uint8_t>> packet) {
-  auto sco_packet = packets::ScoPacketView::Create(packet);
+  bluetooth::hci::PacketView<bluetooth::hci::kLittleEndian> raw_packet(packet);
+  auto sco_packet = bluetooth::hci::ScoPacketView::Create(raw_packet);
   if (loopback_mode_ == hci::LoopbackMode::LOCAL) {
     uint16_t handle = sco_packet.GetHandle();
     send_sco_(packet);
-    std::unique_ptr<bluetooth::packet::RawBuilder> raw_builder_ptr =
-        std::make_unique<bluetooth::packet::RawBuilder>();
-    raw_builder_ptr->AddOctets1(0x01);
-    raw_builder_ptr->AddOctets2(handle);
-    raw_builder_ptr->AddOctets2(0x01);
-
-    auto packet = bluetooth::hci::EventPacketBuilder::Create(
-        bluetooth::hci::EventCode::NUMBER_OF_COMPLETED_PACKETS,
-        std::move(raw_builder_ptr));
+    std::vector<bluetooth::hci::CompletedPackets> completed_packets;
+    bluetooth::hci::CompletedPackets cp;
+    cp.connection_handle_ = handle;
+    cp.host_num_of_completed_packets_ = kNumCommandPackets;
+    completed_packets.push_back(cp);
+    auto packet = bluetooth::hci::NumberOfCompletedPacketsBuilder::Create(
+        completed_packets);
     send_event_(std::move(packet));
     return;
   }
@@ -349,8 +316,15 @@
 
 void DualModeController::RegisterAclChannel(
     const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>& callback) {
-  link_layer_controller_.RegisterAclChannel(callback);
-  send_acl_ = callback;
+  send_acl_ =
+      [callback](std::shared_ptr<bluetooth::hci::AclPacketBuilder> acl_data) {
+        auto bytes = std::make_shared<std::vector<uint8_t>>();
+        bluetooth::packet::BitInserter bit_inserter(*bytes);
+        bytes->reserve(acl_data->size());
+        acl_data->Serialize(bit_inserter);
+        callback(std::move(bytes));
+      };
+  link_layer_controller_.RegisterAclChannel(send_acl_);
 }
 
 void DualModeController::RegisterScoChannel(
@@ -373,14 +347,15 @@
     loopback_mode_ = hci::LoopbackMode::NO;
   }
 
-  SendCommandCompleteSuccess(bluetooth::hci::OpCode::RESET);
+  send_event_(bluetooth::hci::ResetCompleteBuilder::Create(
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS));
 }
 
 void DualModeController::HciReadBufferSize(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 0, "%s  size=%zu", __func__, args.size());
 
   auto packet = bluetooth::hci::ReadBufferSizeCompleteBuilder::Create(
-      0x01, bluetooth::hci::ErrorCode::SUCCESS,
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS,
       properties_.GetAclDataPacketSize(),
       properties_.GetSynchronousDataPacketSize(),
       properties_.GetTotalNumAclDataPackets(),
@@ -395,32 +370,34 @@
   uint16_t handle = args.begin().extract<uint16_t>();
 
   auto packet = bluetooth::hci::ReadEncryptionKeySizeCompleteBuilder::Create(
-      0x01, bluetooth::hci::ErrorCode::SUCCESS, handle,
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS, handle,
       properties_.GetEncryptionKeySize());
   send_event_(std::move(packet));
 }
 
 void DualModeController::HciHostBufferSize(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 7, "%s  size=%zu", __func__, args.size());
-  SendCommandCompleteSuccess(bluetooth::hci::OpCode::HOST_BUFFER_SIZE);
+  auto packet = bluetooth::hci::HostBufferSizeCompleteBuilder::Create(
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciReadLocalVersionInformation(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 0, "%s  size=%zu", __func__, args.size());
 
-  std::unique_ptr<bluetooth::packet::RawBuilder> raw_builder_ptr =
-      std::make_unique<bluetooth::packet::RawBuilder>();
-  raw_builder_ptr->AddOctets1(
-      static_cast<uint8_t>(bluetooth::hci::ErrorCode::SUCCESS));
-  raw_builder_ptr->AddOctets1(properties_.GetVersion());
-  raw_builder_ptr->AddOctets2(properties_.GetRevision());
-  raw_builder_ptr->AddOctets1(properties_.GetLmpPalVersion());
-  raw_builder_ptr->AddOctets2(properties_.GetManufacturerName());
-  raw_builder_ptr->AddOctets2(properties_.GetLmpPalSubversion());
-
-  auto packet = bluetooth::hci::CommandCompleteBuilder::Create(
-      0x01, bluetooth::hci::OpCode::READ_LOCAL_VERSION_INFORMATION,
-      std::move(raw_builder_ptr));
+  bluetooth::hci::LocalVersionInformation local_version_information;
+  local_version_information.hci_version_ =
+      static_cast<bluetooth::hci::HciVersion>(properties_.GetVersion());
+  local_version_information.hci_revision_ = properties_.GetRevision();
+  local_version_information.lmp_version_ =
+      static_cast<bluetooth::hci::LmpVersion>(properties_.GetLmpPalVersion());
+  local_version_information.manufacturer_name_ =
+      properties_.GetManufacturerName();
+  local_version_information.lmp_subversion_ = properties_.GetLmpPalSubversion();
+  auto packet =
+      bluetooth::hci::ReadLocalVersionInformationCompleteBuilder::Create(
+          kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS,
+          local_version_information);
   send_event_(std::move(packet));
 }
 
@@ -432,14 +409,17 @@
   auto status = link_layer_controller_.SendCommandToRemoteByHandle(
       bluetooth::hci::OpCode::READ_REMOTE_VERSION_INFORMATION, args, handle);
 
-  SendCommandStatus(status,
-                    bluetooth::hci::OpCode::READ_REMOTE_VERSION_INFORMATION);
+  auto packet =
+      bluetooth::hci::ReadRemoteVersionInformationStatusBuilder::Create(
+          status, kNumCommandPackets);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciReadBdAddr(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 0, "%s  size=%zu", __func__, args.size());
   auto packet = bluetooth::hci::ReadBdAddrCompleteBuilder::Create(
-      0x01, bluetooth::hci::ErrorCode::SUCCESS, properties_.GetAddress());
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS,
+      properties_.GetAddress());
   send_event_(std::move(packet));
 }
 
@@ -457,7 +437,8 @@
 
   auto packet =
       bluetooth::hci::ReadLocalSupportedCommandsCompleteBuilder::Create(
-          0x01, bluetooth::hci::ErrorCode::SUCCESS, supported_commands);
+          kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS,
+          supported_commands);
   send_event_(std::move(packet));
 }
 
@@ -465,7 +446,7 @@
   ASSERT_LOG(args.size() == 0, "%s  size=%zu", __func__, args.size());
   auto packet =
       bluetooth::hci::ReadLocalSupportedFeaturesCompleteBuilder::Create(
-          0x01, bluetooth::hci::ErrorCode::SUCCESS,
+          kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS,
           properties_.GetSupportedFeatures());
   send_event_(std::move(packet));
 }
@@ -473,7 +454,7 @@
 void DualModeController::HciReadLocalSupportedCodecs(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 0, "%s  size=%zu", __func__, args.size());
   auto packet = bluetooth::hci::ReadLocalSupportedCodecsCompleteBuilder::Create(
-      0x01, bluetooth::hci::ErrorCode::SUCCESS,
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS,
       properties_.GetSupportedCodecs(), properties_.GetVendorSpecificCodecs());
   send_event_(std::move(packet));
 }
@@ -484,7 +465,7 @@
 
   auto pakcet =
       bluetooth::hci::ReadLocalExtendedFeaturesCompleteBuilder::Create(
-          0x01, bluetooth::hci::ErrorCode::SUCCESS, page_number,
+          kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS, page_number,
           properties_.GetExtendedFeaturesMaximumPageNumber(),
           properties_.GetExtendedFeatures(page_number));
   send_event_(std::move(pakcet));
@@ -498,8 +479,9 @@
   auto status = link_layer_controller_.SendCommandToRemoteByHandle(
       bluetooth::hci::OpCode::READ_REMOTE_EXTENDED_FEATURES, args, handle);
 
-  SendCommandStatus(status,
-                    bluetooth::hci::OpCode::READ_REMOTE_EXTENDED_FEATURES);
+  auto packet = bluetooth::hci::ReadRemoteExtendedFeaturesStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciSwitchRole(packets::PacketView<true> args) {
@@ -510,7 +492,9 @@
 
   auto status = link_layer_controller_.SwitchRole(address, role);
 
-  SendCommandStatus(status, bluetooth::hci::OpCode::SWITCH_ROLE);
+  auto packet = bluetooth::hci::SwitchRoleStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciReadRemoteSupportedFeatures(packets::PacketView<true> args) {
@@ -521,8 +505,10 @@
   auto status = link_layer_controller_.SendCommandToRemoteByHandle(
       bluetooth::hci::OpCode::READ_REMOTE_SUPPORTED_FEATURES, args, handle);
 
-  SendCommandStatus(status,
-                    bluetooth::hci::OpCode::READ_REMOTE_SUPPORTED_FEATURES);
+  auto packet =
+      bluetooth::hci::ReadRemoteSupportedFeaturesStatusBuilder::Create(
+          status, kNumCommandPackets);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciReadClockOffset(packets::PacketView<true> args) {
@@ -533,7 +519,9 @@
   auto status = link_layer_controller_.SendCommandToRemoteByHandle(
       bluetooth::hci::OpCode::READ_CLOCK_OFFSET, args, handle);
 
-  SendCommandStatus(status, bluetooth::hci::OpCode::READ_CLOCK_OFFSET);
+  auto packet = bluetooth::hci::ReadClockOffsetStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciIoCapabilityRequestReply(packets::PacketView<true> args) {
@@ -548,7 +536,7 @@
   auto status = link_layer_controller_.IoCapabilityRequestReply(
       peer, io_capability, oob_data_present_flag, authentication_requirements);
   auto packet = bluetooth::hci::IoCapabilityRequestReplyCompleteBuilder::Create(
-      0x01, status, peer);
+      kNumCommandPackets, status, peer);
 
   send_event_(std::move(packet));
 }
@@ -561,7 +549,7 @@
   auto status = link_layer_controller_.UserConfirmationRequestReply(peer);
   auto packet =
       bluetooth::hci::UserConfirmationRequestReplyCompleteBuilder::Create(
-          0x01, status, peer);
+          kNumCommandPackets, status, peer);
 
   send_event_(std::move(packet));
 }
@@ -575,7 +563,7 @@
       link_layer_controller_.UserConfirmationRequestNegativeReply(peer);
   auto packet =
       bluetooth::hci::UserConfirmationRequestNegativeReplyCompleteBuilder::
-          Create(0x01, status, peer);
+          Create(kNumCommandPackets, status, peer);
 
   send_event_(std::move(packet));
 }
@@ -590,7 +578,7 @@
   auto status =
       link_layer_controller_.UserPasskeyRequestReply(peer, numeric_value);
   auto packet = bluetooth::hci::UserPasskeyRequestReplyCompleteBuilder::Create(
-      0x01, status, peer);
+      kNumCommandPackets, status, peer);
 
   send_event_(std::move(packet));
 }
@@ -603,7 +591,7 @@
   auto status = link_layer_controller_.UserPasskeyRequestNegativeReply(peer);
   auto packet =
       bluetooth::hci::UserPasskeyRequestNegativeReplyCompleteBuilder::Create(
-          0x01, status, peer);
+          kNumCommandPackets, status, peer);
 
   send_event_(std::move(packet));
 }
@@ -624,7 +612,7 @@
   auto status = link_layer_controller_.RemoteOobDataRequestReply(peer, c, r);
   auto packet =
       bluetooth::hci::RemoteOobDataRequestReplyCompleteBuilder::Create(
-          0x01, status, peer);
+          kNumCommandPackets, status, peer);
 
   send_event_(std::move(packet));
 }
@@ -637,7 +625,7 @@
   auto status = link_layer_controller_.RemoteOobDataRequestNegativeReply(peer);
   auto packet =
       bluetooth::hci::RemoteOobDataRequestNegativeReplyCompleteBuilder::Create(
-          0x01, status, peer);
+          kNumCommandPackets, status, peer);
 
   send_event_(std::move(packet));
 }
@@ -653,7 +641,7 @@
       link_layer_controller_.IoCapabilityRequestNegativeReply(peer, reason);
   auto packet =
       bluetooth::hci::IoCapabilityRequestNegativeReplyCompleteBuilder::Create(
-          0x01, status, peer);
+          kNumCommandPackets, status, peer);
 
   send_event_(std::move(packet));
 }
@@ -662,7 +650,9 @@
   ASSERT_LOG(args.size() == 1, "%s  size=%zu", __func__, args.size());
   ASSERT(args[0] == 1 || args[0] == 0);
   link_layer_controller_.WriteSimplePairingMode(args[0] == 1);
-  SendCommandCompleteSuccess(bluetooth::hci::OpCode::WRITE_SIMPLE_PAIRING_MODE);
+  auto packet = bluetooth::hci::WriteSimplePairingModeCompleteBuilder::Create(
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciChangeConnectionPacketType(packets::PacketView<true> args) {
@@ -674,42 +664,55 @@
   auto status =
       link_layer_controller_.ChangeConnectionPacketType(handle, packet_type);
 
-  SendCommandStatus(status,
-                    bluetooth::hci::OpCode::CHANGE_CONNECTION_PACKET_TYPE);
+  auto packet = bluetooth::hci::ChangeConnectionPacketTypeStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciWriteLeHostSupport(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 2, "%s  size=%zu", __func__, args.size());
-  SendCommandCompleteSuccess(bluetooth::hci::OpCode::WRITE_LE_HOST_SUPPORT);
+  auto packet = bluetooth::hci::WriteLeHostSupportCompleteBuilder::Create(
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
-void DualModeController::HciWriteSecureConnectionHostSupport(
+void DualModeController::HciWriteSecureConnectionsHostSupport(
     packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 1, "%s  size=%zu", __func__, args.size());
   properties_.SetExtendedFeatures(properties_.GetExtendedFeatures(1) | 0x8, 1);
-  SendCommandCompleteSuccess(
-      bluetooth::hci::OpCode::WRITE_SECURE_CONNECTIONS_HOST_SUPPORT);
+  auto packet =
+      bluetooth::hci::WriteSecureConnectionsHostSupportCompleteBuilder::Create(
+          kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciSetEventMask(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 8, "%s  size=%zu", __func__, args.size());
-  SendCommandCompleteSuccess(bluetooth::hci::OpCode::SET_EVENT_MASK);
+  auto packet = bluetooth::hci::SetEventMaskCompleteBuilder::Create(
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciWriteInquiryMode(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 1, "%s  size=%zu", __func__, args.size());
   link_layer_controller_.SetInquiryMode(args[0]);
-  SendCommandCompleteSuccess(bluetooth::hci::OpCode::WRITE_INQUIRY_MODE);
+  auto packet = bluetooth::hci::WriteInquiryModeCompleteBuilder::Create(
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciWritePageScanType(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 1, "%s  size=%zu", __func__, args.size());
-  SendCommandCompleteSuccess(bluetooth::hci::OpCode::WRITE_PAGE_SCAN_TYPE);
+  auto packet = bluetooth::hci::WritePageScanTypeCompleteBuilder::Create(
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciWriteInquiryScanType(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 1, "%s  size=%zu", __func__, args.size());
-  SendCommandCompleteSuccess(bluetooth::hci::OpCode::WRITE_INQUIRY_SCAN_TYPE);
+  auto packet = bluetooth::hci::WriteInquiryScanTypeCompleteBuilder::Create(
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciAuthenticationRequested(packets::PacketView<true> args) {
@@ -717,7 +720,9 @@
   uint16_t handle = args.begin().extract<uint16_t>();
   auto status = link_layer_controller_.AuthenticationRequested(handle);
 
-  SendCommandStatus(status, bluetooth::hci::OpCode::AUTHENTICATION_REQUESTED);
+  auto packet = bluetooth::hci::AuthenticationRequestedStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciSetConnectionEncryption(packets::PacketView<true> args) {
@@ -728,7 +733,9 @@
   auto status =
       link_layer_controller_.SetConnectionEncryption(handle, encryption_enable);
 
-  SendCommandStatus(status, bluetooth::hci::OpCode::SET_CONNECTION_ENCRYPTION);
+  auto packet = bluetooth::hci::SetConnectionEncryptionStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciChangeConnectionLinkKey(packets::PacketView<true> args) {
@@ -738,7 +745,9 @@
 
   auto status = link_layer_controller_.ChangeConnectionLinkKey(handle);
 
-  SendCommandStatus(status, bluetooth::hci::OpCode::CHANGE_CONNECTION_LINK_KEY);
+  auto packet = bluetooth::hci::ChangeConnectionLinkKeyStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciMasterLinkKey(packets::PacketView<true> args) {
@@ -748,20 +757,24 @@
 
   auto status = link_layer_controller_.MasterLinkKey(key_flag);
 
-  SendCommandStatus(status, bluetooth::hci::OpCode::MASTER_LINK_KEY);
+  auto packet = bluetooth::hci::MasterLinkKeyStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciWriteAuthenticationEnable(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 1, "%s  size=%zu", __func__, args.size());
   properties_.SetAuthenticationEnable(args[0]);
-  SendCommandCompleteSuccess(
-      bluetooth::hci::OpCode::WRITE_AUTHENTICATION_ENABLE);
+  auto packet =
+      bluetooth::hci::WriteAuthenticationEnableCompleteBuilder::Create(
+          kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciReadAuthenticationEnable(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 0, "%s  size=%zu", __func__, args.size());
   auto packet = bluetooth::hci::ReadAuthenticationEnableCompleteBuilder::Create(
-      0x01, bluetooth::hci::ErrorCode::SUCCESS,
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS,
       static_cast<bluetooth::hci::AuthenticationEnable>(
           properties_.GetAuthenticationEnable()));
   send_event_(std::move(packet));
@@ -770,12 +783,16 @@
 void DualModeController::HciWriteClassOfDevice(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 3, "%s  size=%zu", __func__, args.size());
   properties_.SetClassOfDevice(args[0], args[1], args[2]);
-  SendCommandCompleteSuccess(bluetooth::hci::OpCode::WRITE_CLASS_OF_DEVICE);
+  auto packet = bluetooth::hci::WriteClassOfDeviceCompleteBuilder::Create(
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciWritePageTimeout(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 2, "%s  size=%zu", __func__, args.size());
-  SendCommandCompleteSuccess(bluetooth::hci::OpCode::WRITE_PAGE_TIMEOUT);
+  auto packet = bluetooth::hci::WritePageTimeoutCompleteBuilder::Create(
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciHoldMode(packets::PacketView<true> args) {
@@ -788,7 +805,9 @@
   auto status = link_layer_controller_.HoldMode(handle, hold_mode_max_interval,
                                                 hold_mode_min_interval);
 
-  SendCommandStatus(status, bluetooth::hci::OpCode::HOLD_MODE);
+  auto packet =
+      bluetooth::hci::HoldModeStatusBuilder::Create(status, kNumCommandPackets);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciSniffMode(packets::PacketView<true> args) {
@@ -804,7 +823,9 @@
                                                  sniff_min_interval,
                                                  sniff_attempt, sniff_timeout);
 
-  SendCommandStatus(status, bluetooth::hci::OpCode::SNIFF_MODE);
+  auto packet = bluetooth::hci::SniffModeStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciExitSniffMode(packets::PacketView<true> args) {
@@ -814,7 +835,9 @@
 
   auto status = link_layer_controller_.ExitSniffMode(handle);
 
-  SendCommandStatus(status, bluetooth::hci::OpCode::EXIT_SNIFF_MODE);
+  auto packet = bluetooth::hci::ExitSniffModeStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciQosSetup(packets::PacketView<true> args) {
@@ -832,13 +855,17 @@
       link_layer_controller_.QosSetup(handle, service_type, token_rate,
                                       peak_bandwidth, latency, delay_variation);
 
-  SendCommandStatus(status, bluetooth::hci::OpCode::QOS_SETUP);
+  auto packet =
+      bluetooth::hci::QosSetupStatusBuilder::Create(status, kNumCommandPackets);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciWriteDefaultLinkPolicySettings(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 2, "%s  size=%zu", __func__, args.size());
-  SendCommandCompleteSuccess(
-      bluetooth::hci::OpCode::WRITE_DEFAULT_LINK_POLICY_SETTINGS);
+  auto packet =
+      bluetooth::hci::WriteDefaultLinkPolicySettingsCompleteBuilder::Create(
+          kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciFlowSpecification(packets::PacketView<true> args) {
@@ -857,7 +884,9 @@
       handle, flow_direction, service_type, token_rate, token_bucket_size,
       peak_bandwidth, access_latency);
 
-  SendCommandStatus(status, bluetooth::hci::OpCode::FLOW_SPECIFICATION);
+  auto packet = bluetooth::hci::FlowSpecificationStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciWriteLinkPolicySettings(packets::PacketView<true> args) {
@@ -871,7 +900,7 @@
       link_layer_controller_.WriteLinkPolicySettings(handle, settings);
 
   auto packet = bluetooth::hci::WriteLinkPolicySettingsCompleteBuilder::Create(
-      0x01, status, handle);
+      kNumCommandPackets, status, handle);
   send_event_(std::move(packet));
 }
 
@@ -886,7 +915,7 @@
       link_layer_controller_.WriteLinkSupervisionTimeout(handle, timeout);
   auto packet =
       bluetooth::hci::WriteLinkSupervisionTimeoutCompleteBuilder::Create(
-          0x01, status, handle);
+          kNumCommandPackets, status, handle);
   send_event_(std::move(packet));
 }
 
@@ -902,7 +931,7 @@
   std::copy_n(properties_.GetName().begin(), len, local_name.begin());
 
   auto packet = bluetooth::hci::ReadLocalNameCompleteBuilder::Create(
-      0x01, bluetooth::hci::ErrorCode::SUCCESS, local_name);
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS, local_name);
   send_event_(std::move(packet));
 }
 
@@ -910,7 +939,9 @@
   ASSERT_LOG(args.size() == 248, "%s  size=%zu", __func__, args.size());
   std::vector<uint8_t> clipped(args.begin(), args.begin() + LastNonZero(args) + 1);
   properties_.SetName(clipped);
-  SendCommandCompleteSuccess(bluetooth::hci::OpCode::WRITE_LOCAL_NAME);
+  auto packet = bluetooth::hci::WriteLocalNameCompleteBuilder::Create(
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciWriteExtendedInquiryResponse(packets::PacketView<true> args) {
@@ -918,51 +949,63 @@
   // Strip FEC byte and trailing zeros
   std::vector<uint8_t> clipped(args.begin() + 1, args.begin() + LastNonZero(args) + 1);
   properties_.SetExtendedInquiryData(clipped);
-  LOG_WARN("Write EIR Inquiry - Size = %d (%d)", static_cast<int>(properties_.GetExtendedInquiryData().size()),
-           static_cast<int>(clipped.size()));
-  SendCommandCompleteSuccess(
-      bluetooth::hci::OpCode::WRITE_EXTENDED_INQUIRY_RESPONSE);
+  auto packet =
+      bluetooth::hci::WriteExtendedInquiryResponseCompleteBuilder::Create(
+          kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciRefreshEncryptionKey(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 2, "%s  size=%zu", __func__, args.size());
   auto args_itr = args.begin();
   uint16_t handle = args_itr.extract<uint16_t>();
-  SendCommandStatusSuccess(bluetooth::hci::OpCode::REFRESH_ENCRYPTION_KEY);
+  auto status_packet =
+      bluetooth::hci::RefreshEncryptionKeyStatusBuilder::Create(
+          bluetooth::hci::ErrorCode::SUCCESS, kNumCommandPackets);
+  send_event_(std::move(status_packet));
   // TODO: Support this in the link layer
-  auto packet = bluetooth::hci::EncryptionKeyRefreshCompleteBuilder::Create(
-      bluetooth::hci::ErrorCode::SUCCESS, handle);
-  send_event_(std::move(packet));
+  auto complete_packet =
+      bluetooth::hci::EncryptionKeyRefreshCompleteBuilder::Create(
+          bluetooth::hci::ErrorCode::SUCCESS, handle);
+  send_event_(std::move(complete_packet));
 }
 
 void DualModeController::HciWriteVoiceSetting(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 2, "%s  size=%zu", __func__, args.size());
-  SendCommandCompleteSuccess(bluetooth::hci::OpCode::WRITE_VOICE_SETTING);
+  auto packet = bluetooth::hci::WriteVoiceSettingCompleteBuilder::Create(
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciWriteCurrentIacLap(packets::PacketView<true> args) {
   ASSERT(args.size() > 0);
   ASSERT(args.size() == 1 + (3 * args[0]));  // count + 3-byte IACs
-
-  SendCommandCompleteSuccess(bluetooth::hci::OpCode::WRITE_CURRENT_IAC_LAP);
+  auto packet = bluetooth::hci::WriteCurrentIacLapCompleteBuilder::Create(
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciWriteInquiryScanActivity(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 4, "%s  size=%zu", __func__, args.size());
-  SendCommandCompleteSuccess(
-      bluetooth::hci::OpCode::WRITE_INQUIRY_SCAN_ACTIVITY);
+  auto packet = bluetooth::hci::WriteInquiryScanActivityCompleteBuilder::Create(
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciWriteScanEnable(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 1, "%s  size=%zu", __func__, args.size());
   link_layer_controller_.SetInquiryScanEnable(args[0] & 0x1);
   link_layer_controller_.SetPageScanEnable(args[0] & 0x2);
-  SendCommandCompleteSuccess(bluetooth::hci::OpCode::WRITE_SCAN_ENABLE);
+  auto packet = bluetooth::hci::WriteScanEnableCompleteBuilder::Create(
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciSetEventFilter(packets::PacketView<true> args) {
   ASSERT(args.size() > 0);
-  SendCommandCompleteSuccess(bluetooth::hci::OpCode::SET_EVENT_FILTER);
+  auto packet = bluetooth::hci::SetEventFilterCompleteBuilder::Create(
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciInquiry(packets::PacketView<true> args) {
@@ -971,13 +1014,17 @@
   link_layer_controller_.SetInquiryMaxResponses(args[4]);
   link_layer_controller_.StartInquiry(std::chrono::milliseconds(args[3] * 1280));
 
-  SendCommandStatusSuccess(bluetooth::hci::OpCode::INQUIRY);
+  auto packet = bluetooth::hci::InquiryStatusBuilder::Create(
+      bluetooth::hci::ErrorCode::SUCCESS, kNumCommandPackets);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciInquiryCancel(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 0, "%s  size=%zu", __func__, args.size());
   link_layer_controller_.InquiryCancel();
-  SendCommandCompleteSuccess(bluetooth::hci::OpCode::INQUIRY_CANCEL);
+  auto packet = bluetooth::hci::InquiryCancelCompleteBuilder::Create(
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciAcceptConnectionRequest(packets::PacketView<true> args) {
@@ -986,7 +1033,9 @@
   bool try_role_switch = args[6] == 0;
   auto status =
       link_layer_controller_.AcceptConnectionRequest(addr, try_role_switch);
-  SendCommandStatus(status, bluetooth::hci::OpCode::ACCEPT_CONNECTION_REQUEST);
+  auto packet = bluetooth::hci::AcceptConnectionRequestStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciRejectConnectionRequest(packets::PacketView<true> args) {
@@ -995,16 +1044,19 @@
   Address addr = args_itr.extract<Address>();
   uint8_t reason = args_itr.extract<uint8_t>();
   auto status = link_layer_controller_.RejectConnectionRequest(addr, reason);
-  SendCommandStatus(status, bluetooth::hci::OpCode::REJECT_CONNECTION_REQUEST);
+  auto packet = bluetooth::hci::RejectConnectionRequestStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciLinkKeyRequestReply(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 22, "%s  size=%zu", __func__, args.size());
-  Address addr = args.begin().extract<Address>();
-  packets::PacketView<true> key = args.SubViewLittleEndian(6, 22);
+  auto args_it = args.begin();
+  Address addr = args_it.extract<Address>();
+  auto key = args.begin().extract<std::array<uint8_t, 16>>();
   auto status = link_layer_controller_.LinkKeyRequestReply(addr, key);
-  auto packet =
-      bluetooth::hci::LinkKeyRequestReplyCompleteBuilder::Create(0x01, status);
+  auto packet = bluetooth::hci::LinkKeyRequestReplyCompleteBuilder::Create(
+      kNumCommandPackets, status);
   send_event_(std::move(packet));
 }
 
@@ -1014,7 +1066,7 @@
   auto status = link_layer_controller_.LinkKeyRequestNegativeReply(addr);
   auto packet =
       bluetooth::hci::LinkKeyRequestNegativeReplyCompleteBuilder::Create(
-          0x01, status, addr);
+          kNumCommandPackets, status, addr);
   send_event_(std::move(packet));
 }
 
@@ -1033,7 +1085,7 @@
   }
 
   auto packet = bluetooth::hci::DeleteStoredLinkKeyCompleteBuilder::Create(
-      0x01, bluetooth::hci::ErrorCode::SUCCESS, deleted_keys);
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS, deleted_keys);
 
   send_event_(std::move(packet));
 }
@@ -1044,9 +1096,11 @@
   Address remote_addr = args.begin().extract<Address>();
 
   auto status = link_layer_controller_.SendCommandToRemoteByAddress(
-      bluetooth::hci::OpCode::REMOTE_NAME_REQUEST, args, remote_addr, false);
+      bluetooth::hci::OpCode::REMOTE_NAME_REQUEST, args, remote_addr);
 
-  SendCommandStatus(status, bluetooth::hci::OpCode::REMOTE_NAME_REQUEST);
+  auto packet = bluetooth::hci::RemoteNameRequestStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciLeSetEventMask(packets::PacketView<true> args) {
@@ -1055,22 +1109,20 @@
     uint64_t mask = args.begin().extract<uint64_t>();
     link_layer_controller_.SetLeEventMask(mask);
   */
-  SendCommandCompleteSuccess(bluetooth::hci::OpCode::LE_SET_EVENT_MASK);
+  auto packet = bluetooth::hci::LeSetEventMaskCompleteBuilder::Create(
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciLeReadBufferSize(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 0, "%s  size=%zu", __func__, args.size());
 
-  std::unique_ptr<bluetooth::packet::RawBuilder> raw_builder_ptr =
-      std::make_unique<bluetooth::packet::RawBuilder>();
-  raw_builder_ptr->AddOctets1(
-      static_cast<uint8_t>(bluetooth::hci::ErrorCode::SUCCESS));
-  raw_builder_ptr->AddOctets2(properties_.GetLeDataPacketLength());
-  raw_builder_ptr->AddOctets1(properties_.GetTotalNumLeDataPackets());
+  bluetooth::hci::LeBufferSize le_buffer_size;
+  le_buffer_size.le_data_packet_length_ = properties_.GetLeDataPacketLength();
+  le_buffer_size.total_num_le_packets_ = properties_.GetTotalNumLeDataPackets();
 
-  auto packet = bluetooth::hci::CommandCompleteBuilder::Create(
-      0x01, bluetooth::hci::OpCode::LE_READ_BUFFER_SIZE,
-      std::move(raw_builder_ptr));
+  auto packet = bluetooth::hci::LeReadBufferSizeCompleteBuilder::Create(
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS, le_buffer_size);
   send_event_(std::move(packet));
 }
 
@@ -1078,7 +1130,7 @@
   ASSERT_LOG(args.size() == 0, "%s  size=%zu", __func__, args.size());
   auto packet =
       bluetooth::hci::LeReadLocalSupportedFeaturesCompleteBuilder::Create(
-          0x01, bluetooth::hci::ErrorCode::SUCCESS,
+          kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS,
           properties_.GetLeSupportedFeatures());
   send_event_(std::move(packet));
 }
@@ -1086,7 +1138,9 @@
 void DualModeController::HciLeSetRandomAddress(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 6, "%s  size=%zu", __func__, args.size());
   properties_.SetLeAddress(args.begin().extract<Address>());
-  SendCommandCompleteSuccess(bluetooth::hci::OpCode::LE_SET_RANDOM_ADDRESS);
+  auto packet = bluetooth::hci::LeSetRandomAddressCompleteBuilder::Create(
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciLeSetAdvertisingParameters(packets::PacketView<true> args) {
@@ -1100,28 +1154,35 @@
       args_itr.extract<uint8_t>() /* AdvertisingFilterPolicy */
   );
 
-  SendCommandCompleteSuccess(
-      bluetooth::hci::OpCode::LE_SET_ADVERTISING_PARAMETERS);
+  auto packet =
+      bluetooth::hci::LeSetAdvertisingParametersCompleteBuilder::Create(
+          kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciLeSetAdvertisingData(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 32, "%s  size=%zu", __func__, args.size());
   properties_.SetLeAdvertisement(std::vector<uint8_t>(args.begin() + 1, args.end()));
-  SendCommandCompleteSuccess(bluetooth::hci::OpCode::LE_SET_ADVERTISING_DATA);
+  auto packet = bluetooth::hci::LeSetAdvertisingDataCompleteBuilder::Create(
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciLeSetScanResponseData(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 32, "%s  size=%zu", __func__, args.size());
   properties_.SetLeScanResponse(std::vector<uint8_t>(args.begin() + 1, args.end()));
-  SendCommandCompleteSuccess(bluetooth::hci::OpCode::LE_SET_SCAN_RESPONSE_DATA);
+  auto packet = bluetooth::hci::LeSetScanResponseDataCompleteBuilder::Create(
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciLeSetAdvertisingEnable(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 1, "%s  size=%zu", __func__, args.size());
   auto status = link_layer_controller_.SetLeAdvertisingEnable(
       args.begin().extract<uint8_t>());
-  SendCommandCompleteOnlyStatus(
-      bluetooth::hci::OpCode::LE_SET_ADVERTISING_ENABLE, status);
+  auto packet = bluetooth::hci::LeSetAdvertisingEnableCompleteBuilder::Create(
+      kNumCommandPackets, status);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciLeSetScanParameters(packets::PacketView<true> args) {
@@ -1131,15 +1192,18 @@
   link_layer_controller_.SetLeScanWindow(args[3] | (args[4], 8));
   link_layer_controller_.SetLeAddressType(args[5]);
   link_layer_controller_.SetLeScanFilterPolicy(args[6]);
-  SendCommandCompleteSuccess(bluetooth::hci::OpCode::LE_SET_SCAN_PARAMETERS);
+  auto packet = bluetooth::hci::LeSetScanParametersCompleteBuilder::Create(
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciLeSetScanEnable(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 2, "%s  size=%zu", __func__, args.size());
-  LOG_INFO("SetScanEnable: %d %d", args[0], args[1]);
   link_layer_controller_.SetLeScanEnable(args[0]);
   link_layer_controller_.SetLeFilterDuplicates(args[1]);
-  SendCommandCompleteSuccess(bluetooth::hci::OpCode::LE_SET_SCAN_ENABLE);
+  auto packet = bluetooth::hci::LeSetScanEnableCompleteBuilder::Create(
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciLeCreateConnection(packets::PacketView<true> args) {
@@ -1166,18 +1230,23 @@
 
   auto status = link_layer_controller_.SetLeConnect(true);
 
-  SendCommandStatus(status, bluetooth::hci::OpCode::LE_CREATE_CONNECTION);
+  auto packet = bluetooth::hci::LeCreateConnectionStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciLeConnectionUpdate(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 14, "%s  size=%zu", __func__, args.size());
 
-  SendCommandStatus(
+  auto status_packet = bluetooth::hci::LeConnectionUpdateStatusBuilder::Create(
       bluetooth::hci::ErrorCode::CONNECTION_REJECTED_UNACCEPTABLE_BD_ADDR,
-      bluetooth::hci::OpCode::LE_CONNECTION_UPDATE);
+      kNumCommandPackets);
+  send_event_(std::move(status_packet));
 
-  auto packet = bluetooth::hci::LeConnectionUpdateCompleteBuilder::Create(
-      bluetooth::hci::ErrorCode::SUCCESS, 0x0002, 0x0006, 0x0000, 0x01f4);
+  auto complete_packet =
+      bluetooth::hci::LeConnectionUpdateCompleteBuilder::Create(
+          bluetooth::hci::ErrorCode::SUCCESS, 0x0002, 0x0006, 0x0000, 0x01f4);
+  send_event_(std::move(complete_packet));
 }
 
 void DualModeController::HciCreateConnection(packets::PacketView<true> args) {
@@ -1193,7 +1262,9 @@
   auto status = link_layer_controller_.CreateConnection(
       address, packet_type, page_scan_mode, clock_offset, allow_role_switch);
 
-  SendCommandStatus(status, bluetooth::hci::OpCode::CREATE_CONNECTION);
+  auto packet = bluetooth::hci::CreateConnectionStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciDisconnect(packets::PacketView<true> args) {
@@ -1205,13 +1276,17 @@
 
   auto status = link_layer_controller_.Disconnect(handle, reason);
 
-  SendCommandStatus(status, bluetooth::hci::OpCode::DISCONNECT);
+  auto packet = bluetooth::hci::DisconnectStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciLeConnectionCancel(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 0, "%s  size=%zu", __func__, args.size());
   link_layer_controller_.SetLeConnect(false);
-  SendCommandStatusSuccess(bluetooth::hci::OpCode::LE_CREATE_CONNECTION_CANCEL);
+  auto packet = bluetooth::hci::LeCreateConnectionCancelStatusBuilder::Create(
+      bluetooth::hci::ErrorCode::SUCCESS, kNumCommandPackets);
+  send_event_(std::move(packet));
   /* For testing Jakub's patch:  Figure out a neat way to call this without
      recompiling.  I'm thinking about a bad device. */
   /*
@@ -1223,7 +1298,7 @@
 void DualModeController::HciLeReadWhiteListSize(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 0, "%s  size=%zu", __func__, args.size());
   auto packet = bluetooth::hci::LeReadWhiteListSizeCompleteBuilder::Create(
-      0x01, bluetooth::hci::ErrorCode::SUCCESS,
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS,
       properties_.GetLeWhiteListSize());
   send_event_(std::move(packet));
 }
@@ -1231,24 +1306,28 @@
 void DualModeController::HciLeClearWhiteList(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 0, "%s  size=%zu", __func__, args.size());
   link_layer_controller_.LeWhiteListClear();
-  SendCommandCompleteSuccess(bluetooth::hci::OpCode::LE_CLEAR_WHITE_LIST);
+  auto packet = bluetooth::hci::LeClearWhiteListCompleteBuilder::Create(
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciLeAddDeviceToWhiteList(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 7, "%s  size=%zu", __func__, args.size());
 
   if (link_layer_controller_.LeWhiteListFull()) {
-    SendCommandCompleteOnlyStatus(
-        bluetooth::hci::OpCode::LE_ADD_DEVICE_TO_WHITE_LIST,
+    auto packet = bluetooth::hci::LeAddDeviceToWhiteListCompleteBuilder::Create(
+        kNumCommandPackets,
         bluetooth::hci::ErrorCode::MEMORY_CAPACITY_EXCEEDED);
+    send_event_(std::move(packet));
     return;
   }
   auto args_itr = args.begin();
   uint8_t addr_type = args_itr.extract<uint8_t>();
   Address address = args_itr.extract<Address>();
   link_layer_controller_.LeWhiteListAddDevice(address, addr_type);
-  SendCommandCompleteSuccess(
-      bluetooth::hci::OpCode::LE_ADD_DEVICE_TO_WHITE_LIST);
+  auto packet = bluetooth::hci::LeAddDeviceToWhiteListCompleteBuilder::Create(
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciLeRemoveDeviceFromWhiteList(packets::PacketView<true> args) {
@@ -1258,15 +1337,19 @@
   uint8_t addr_type = args_itr.extract<uint8_t>();
   Address address = args_itr.extract<Address>();
   link_layer_controller_.LeWhiteListRemoveDevice(address, addr_type);
-  SendCommandCompleteSuccess(
-      bluetooth::hci::OpCode::LE_REMOVE_DEVICE_FROM_WHITE_LIST);
+  auto packet =
+      bluetooth::hci::LeRemoveDeviceFromWhiteListCompleteBuilder::Create(
+          kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciLeClearResolvingList(
     packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 0, "%s  size=%zu", __func__, args.size());
   link_layer_controller_.LeResolvingListClear();
-  SendCommandCompleteSuccess(bluetooth::hci::OpCode::LE_CLEAR_RESOLVING_LIST);
+  auto packet = bluetooth::hci::LeClearResolvingListCompleteBuilder::Create(
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciLeAddDeviceToResolvingList(
@@ -1274,9 +1357,11 @@
   ASSERT_LOG(args.size() == 39, "%s  size=%zu", __func__, args.size());
 
   if (link_layer_controller_.LeResolvingListFull()) {
-    SendCommandCompleteOnlyStatus(
-        bluetooth::hci::OpCode::LE_ADD_DEVICE_TO_RESOLVING_LIST,
-        bluetooth::hci::ErrorCode::MEMORY_CAPACITY_EXCEEDED);
+    auto packet =
+        bluetooth::hci::LeAddDeviceToResolvingListCompleteBuilder::Create(
+            kNumCommandPackets,
+            bluetooth::hci::ErrorCode::MEMORY_CAPACITY_EXCEEDED);
+    send_event_(std::move(packet));
     return;
   }
   auto args_itr = args.begin();
@@ -1296,8 +1381,10 @@
 
   link_layer_controller_.LeResolvingListAddDevice(address, addr_type, peerIrk,
                                                   localIrk);
-  SendCommandCompleteSuccess(
-      bluetooth::hci::OpCode::LE_ADD_DEVICE_TO_RESOLVING_LIST);
+  auto packet =
+      bluetooth::hci::LeAddDeviceToResolvingListCompleteBuilder::Create(
+          kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciLeRemoveDeviceFromResolvingList(
@@ -1308,8 +1395,10 @@
   uint8_t addr_type = args_itr.extract<uint8_t>();
   Address address = args_itr.extract<Address>();
   link_layer_controller_.LeResolvingListRemoveDevice(address, addr_type);
-  SendCommandCompleteSuccess(
-      bluetooth::hci::OpCode::LE_REMOVE_DEVICE_FROM_RESOLVING_LIST);
+  auto packet =
+      bluetooth::hci::LeRemoveDeviceFromResolvingListCompleteBuilder::Create(
+          kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciLeSetPrivacyMode(packets::PacketView<true> args) {
@@ -1326,7 +1415,9 @@
         peer_identity_address_type, peer_identity_address, privacy_mode);
   }
 
-  SendCommandCompleteSuccess(bluetooth::hci::OpCode::LE_SET_PRIVACY_MODE);
+  auto packet = bluetooth::hci::LeSetPrivacyModeCompleteBuilder::Create(
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciLeReadRemoteFeatures(packets::PacketView<true> args) {
@@ -1337,7 +1428,9 @@
   auto status = link_layer_controller_.SendCommandToRemoteByHandle(
       bluetooth::hci::OpCode::LE_READ_REMOTE_FEATURES, args, handle);
 
-  SendCommandStatus(status, bluetooth::hci::OpCode::LE_READ_REMOTE_FEATURES);
+  auto packet = bluetooth::hci::LeConnectionUpdateStatusBuilder::Create(
+      status, kNumCommandPackets);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::HciLeRand(packets::PacketView<true> args) {
@@ -1348,14 +1441,14 @@
   }
 
   auto packet = bluetooth::hci::LeRandCompleteBuilder::Create(
-      0x01, bluetooth::hci::ErrorCode::SUCCESS, random_val);
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS, random_val);
   send_event_(std::move(packet));
 }
 
 void DualModeController::HciLeReadSupportedStates(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 0, "%s  size=%zu", __func__, args.size());
   auto packet = bluetooth::hci::LeReadSupportedStatesCompleteBuilder::Create(
-      0x01, bluetooth::hci::ErrorCode::SUCCESS,
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS,
       properties_.GetLeSupportedStates());
   send_event_(std::move(packet));
 }
@@ -1364,9 +1457,8 @@
   ASSERT_LOG(args.size() == 0, "%s  size=%zu", __func__, args.size());
   vector<uint8_t> caps = properties_.GetLeVendorCap();
   if (caps.size() == 0) {
-    SendCommandCompleteOnlyStatus(
-        bluetooth::hci::OpCode::LE_GET_VENDOR_CAPABILITIES,
-        bluetooth::hci::ErrorCode::UNKNOWN_HCI_COMMAND);
+    SendCommandCompleteUnknownOpCodeEvent(static_cast<uint16_t>(
+        bluetooth::hci::OpCode::LE_GET_VENDOR_CAPABILITIES));
     return;
   }
 
@@ -1377,33 +1469,33 @@
   raw_builder_ptr->AddOctets(properties_.GetLeVendorCap());
 
   auto packet = bluetooth::hci::CommandCompleteBuilder::Create(
-      0x01, bluetooth::hci::OpCode::LE_GET_VENDOR_CAPABILITIES,
+      kNumCommandPackets, bluetooth::hci::OpCode::LE_GET_VENDOR_CAPABILITIES,
       std::move(raw_builder_ptr));
   send_event_(std::move(packet));
 }
 
 void DualModeController::HciLeVendorMultiAdv(packets::PacketView<true> args) {
   ASSERT(args.size() > 0);
-  SendCommandCompleteOnlyStatus(bluetooth::hci::OpCode::LE_MULTI_ADVT,
-                                bluetooth::hci::ErrorCode::UNKNOWN_HCI_COMMAND);
+  SendCommandCompleteUnknownOpCodeEvent(
+      static_cast<uint16_t>(bluetooth::hci::OpCode::LE_MULTI_ADVT));
 }
 
 void DualModeController::HciLeAdvertisingFilter(packets::PacketView<true> args) {
   ASSERT(args.size() > 0);
-  SendCommandCompleteOnlyStatus(bluetooth::hci::OpCode::LE_ADV_FILTER,
-                                bluetooth::hci::ErrorCode::UNKNOWN_HCI_COMMAND);
+  SendCommandCompleteUnknownOpCodeEvent(
+      static_cast<uint16_t>(bluetooth::hci::OpCode::LE_ADV_FILTER));
 }
 
 void DualModeController::HciLeEnergyInfo(packets::PacketView<true> args) {
   ASSERT(args.size() > 0);
-  SendCommandCompleteOnlyStatus(bluetooth::hci::OpCode::LE_ENERGY_INFO,
-                                bluetooth::hci::ErrorCode::UNKNOWN_HCI_COMMAND);
+  SendCommandCompleteUnknownOpCodeEvent(
+      static_cast<uint16_t>(bluetooth::hci::OpCode::LE_ENERGY_INFO));
 }
 
 void DualModeController::HciLeExtendedScanParams(packets::PacketView<true> args) {
   ASSERT(args.size() > 0);
-  SendCommandCompleteOnlyStatus(bluetooth::hci::OpCode::LE_EXTENDED_SCAN_PARAMS,
-                                bluetooth::hci::ErrorCode::UNKNOWN_HCI_COMMAND);
+  SendCommandCompleteUnknownOpCodeEvent(
+      static_cast<uint16_t>(bluetooth::hci::OpCode::LE_EXTENDED_SCAN_PARAMS));
 }
 
 void DualModeController::HciLeStartEncryption(packets::PacketView<true> args) {
@@ -1417,13 +1509,14 @@
   // for (size_t i = 0; i < 16; i++) {
   //   long_term_key.push_back(args_itr.extract<uint18_t>();
   // }
-  SendCommandStatus(bluetooth::hci::ErrorCode::SUCCESS,
-                    bluetooth::hci::OpCode::LE_START_ENCRYPTION);
+  auto status_packet = bluetooth::hci::LeStartEncryptionStatusBuilder::Create(
+      bluetooth::hci::ErrorCode::SUCCESS, kNumCommandPackets);
+  send_event_(std::move(status_packet));
 
-  auto packet = bluetooth::hci::EncryptionChangeBuilder::Create(
+  auto complete_packet = bluetooth::hci::EncryptionChangeBuilder::Create(
       bluetooth::hci::ErrorCode::SUCCESS, handle,
       bluetooth::hci::EncryptionEnabled::OFF);
-  send_event_(std::move(packet));
+  send_event_(std::move(complete_packet));
 #if 0
 
   std::shared_ptr<packets::AclPacketBuilder> encryption_information =
@@ -1481,7 +1574,7 @@
 void DualModeController::HciReadLoopbackMode(packets::PacketView<true> args) {
   ASSERT_LOG(args.size() == 0, "%s size=%zu", __func__, args.size());
   auto packet = bluetooth::hci::ReadLoopbackModeCompleteBuilder::Create(
-      0x01, bluetooth::hci::ErrorCode::SUCCESS,
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS,
       static_cast<bluetooth::hci::LoopbackMode>(loopback_mode_));
   send_event_(std::move(packet));
 }
@@ -1501,7 +1594,9 @@
       bluetooth::hci::ErrorCode::SUCCESS, sco_handle, properties_.GetAddress(),
       bluetooth::hci::LinkType::SCO, bluetooth::hci::Enable::DISABLED);
   send_event_(std::move(packet_sco));
-  SendCommandCompleteSuccess(bluetooth::hci::OpCode::WRITE_LOOPBACK_MODE);
+  auto packet = bluetooth::hci::WriteLoopbackModeCompleteBuilder::Create(
+      kNumCommandPackets, bluetooth::hci::ErrorCode::SUCCESS);
+  send_event_(std::move(packet));
 }
 
 void DualModeController::SetAddress(Address address) {
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 d18f7e4..bd9c17d 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
@@ -286,7 +286,7 @@
   void HciWriteLeHostSupport(packets::PacketView<true> args);
 
   // 7.3.92
-  void HciWriteSecureConnectionHostSupport(packets::PacketView<true> args);
+  void HciWriteSecureConnectionsHostSupport(packets::PacketView<true> args);
 
   // Informational Parameters Commands
   // Bluetooth Core Specification Version 4.2 Volume 2 Part E 7.4
@@ -432,24 +432,11 @@
   // Creates a command complete event and sends it back to the HCI.
   void SendCommandComplete(hci::OpCode command_opcode, const std::vector<uint8_t>& return_parameters) const;
 
-  // Sends a command complete event with no return parameters.
-  void SendCommandCompleteSuccess(bluetooth::hci::OpCode command_opcode) const;
-
   void SendCommandCompleteUnknownOpCodeEvent(uint16_t command_opcode) const;
 
-  // Sends a command complete event with no return parameters.
-  void SendCommandCompleteOnlyStatus(bluetooth::hci::OpCode command_opcode,
-                                     bluetooth::hci::ErrorCode status) const;
-
-  // Creates a command status event and sends it back to the HCI.
-  void SendCommandStatus(bluetooth::hci::ErrorCode status,
-                         bluetooth::hci::OpCode command_opcode) const;
-
-  // Sends a command status event with default event parameters.
-  void SendCommandStatusSuccess(bluetooth::hci::OpCode command_opcode) const;
-
   // Callbacks to send packets back to the HCI.
-  std::function<void(std::shared_ptr<std::vector<uint8_t>>)> send_acl_;
+  std::function<void(std::shared_ptr<bluetooth::hci::AclPacketBuilder>)>
+      send_acl_;
   std::function<void(std::shared_ptr<bluetooth::hci::EventPacketBuilder>)>
       send_event_;
   std::function<void(std::shared_ptr<std::vector<uint8_t>>)> send_sco_;
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 a272f84..aebe621 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
@@ -19,9 +19,7 @@
 #include "hci.h"
 #include "include/le_advertisement.h"
 #include "os/log.h"
-#include "packets/hci/acl_packet_builder.h"
 #include "packets/hci/command_packet_view.h"
-#include "packets/hci/sco_packet_builder.h"
 #include "packets/raw_builder.h"
 
 #include "packet/raw_builder.h"
@@ -32,6 +30,8 @@
 
 namespace test_vendor_lib {
 
+constexpr uint16_t kNumCommandPackets = 0x01;
+
 // TODO: Model Rssi?
 static uint8_t GetRssi() {
   static uint8_t rssi = 0;
@@ -61,39 +61,60 @@
 }
 
 bluetooth::hci::ErrorCode LinkLayerController::SendCommandToRemoteByAddress(
-    bluetooth::hci::OpCode opcode, PacketView<true> args, const Address& remote,
-    bool use_public_address) {
-  Address local_address;
-  if (use_public_address) {
-    local_address = properties_.GetAddress();
-  } else {
-    local_address = properties_.GetLeAddress();
+    bluetooth::hci::OpCode opcode, PacketView<true> args,
+    const Address& remote) {
+  Address local_address = properties_.GetAddress();
+
+  switch (opcode) {
+    case (bluetooth::hci::OpCode::REMOTE_NAME_REQUEST):
+      // LMP features get requested with remote name requests.
+      SendLinkLayerPacket(model::packets::ReadRemoteLmpFeaturesBuilder::Create(
+          local_address, remote));
+      SendLinkLayerPacket(model::packets::RemoteNameRequestBuilder::Create(
+          local_address, remote));
+      break;
+    case (bluetooth::hci::OpCode::READ_REMOTE_SUPPORTED_FEATURES):
+      SendLinkLayerPacket(
+          model::packets::ReadRemoteSupportedFeaturesBuilder::Create(
+              local_address, remote));
+      break;
+    case (bluetooth::hci::OpCode::READ_REMOTE_EXTENDED_FEATURES): {
+      uint8_t page_number =
+          (args.begin() + 2).extract<uint8_t>();  // skip the handle
+      SendLinkLayerPacket(
+          model::packets::ReadRemoteExtendedFeaturesBuilder::Create(
+              local_address, remote, page_number));
+    } break;
+    case (bluetooth::hci::OpCode::READ_REMOTE_VERSION_INFORMATION):
+      SendLinkLayerPacket(
+          model::packets::ReadRemoteVersionInformationBuilder::Create(
+              local_address, remote));
+      break;
+    case (bluetooth::hci::OpCode::READ_CLOCK_OFFSET):
+      SendLinkLayerPacket(model::packets::ReadClockOffsetBuilder::Create(
+          local_address, remote));
+      break;
+    default:
+      LOG_INFO("Dropping unhandled command 0x%04x",
+               static_cast<uint16_t>(opcode));
+      return bluetooth::hci::ErrorCode::UNKNOWN_HCI_COMMAND;
   }
 
-  std::unique_ptr<bluetooth::packet::RawBuilder> raw_builder_ptr =
-      std::make_unique<bluetooth::packet::RawBuilder>();
-  std::vector<uint8_t> payload_bytes(args.begin(), args.end());
-  raw_builder_ptr->AddOctets2(static_cast<uint16_t>(opcode));
-  raw_builder_ptr->AddOctets(payload_bytes);
-
-  auto command = model::packets::CommandBuilder::Create(
-      local_address, remote, std::move(raw_builder_ptr));
-
-  SendLinkLayerPacket(std::move(command));
   return bluetooth::hci::ErrorCode::SUCCESS;
 }
 
 bluetooth::hci::ErrorCode LinkLayerController::SendCommandToRemoteByHandle(
     bluetooth::hci::OpCode opcode, PacketView<true> args, uint16_t handle) {
   // TODO: Handle LE connections
-  bool use_public_address = true;
   if (!connections_.HasHandle(handle)) {
     return bluetooth::hci::ErrorCode::UNKNOWN_CONNECTION;
   }
-  return SendCommandToRemoteByAddress(opcode, args, connections_.GetAddress(handle), use_public_address);
+  return SendCommandToRemoteByAddress(opcode, args,
+                                      connections_.GetAddress(handle));
 }
 
-hci::Status LinkLayerController::SendAclToRemote(AclPacketView acl_packet) {
+hci::Status LinkLayerController::SendAclToRemote(
+    bluetooth::hci::AclPacketView acl_packet) {
   uint16_t handle = acl_packet.GetHandle();
   if (!connections_.HasHandle(handle)) {
     return hci::Status::UNKNOWN_CONNECTION;
@@ -109,15 +130,13 @@
            static_cast<int>(acl_packet.size()));
 
   ScheduleTask(milliseconds(5), [this, handle]() {
-    std::unique_ptr<bluetooth::packet::RawBuilder> raw_builder_ptr =
-        std::make_unique<bluetooth::packet::RawBuilder>();
-    raw_builder_ptr->AddOctets1(0x01);
-    raw_builder_ptr->AddOctets2(handle);
-    raw_builder_ptr->AddOctets2(0x01);
-
-    auto packet = bluetooth::hci::EventPacketBuilder::Create(
-        bluetooth::hci::EventCode::NUMBER_OF_COMPLETED_PACKETS,
-        std::move(raw_builder_ptr));
+    std::vector<bluetooth::hci::CompletedPackets> completed_packets;
+    bluetooth::hci::CompletedPackets cp;
+    cp.connection_handle_ = handle;
+    cp.host_num_of_completed_packets_ = kNumCommandPackets;
+    completed_packets.push_back(cp);
+    auto packet = bluetooth::hci::NumberOfCompletedPacketsBuilder::Create(
+        completed_packets);
     send_event_(std::move(packet));
   });
 
@@ -129,8 +148,8 @@
 
   uint16_t first_two_bytes =
       static_cast<uint16_t>(acl_packet.GetHandle()) +
-      (static_cast<uint16_t>(acl_packet.GetPacketBoundaryFlags()) << 12) +
-      (static_cast<uint16_t>(acl_packet.GetBroadcastFlags()) << 14);
+      (static_cast<uint16_t>(acl_packet.GetPacketBoundaryFlag()) << 12) +
+      (static_cast<uint16_t>(acl_packet.GetBroadcastFlag()) << 14);
   raw_builder_ptr->AddOctets2(first_two_bytes);
   raw_builder_ptr->AddOctets2(static_cast<uint16_t>(payload_bytes.size()));
   raw_builder_ptr->AddOctets(payload_bytes);
@@ -158,9 +177,6 @@
     case model::packets::PacketType::ACL:
       IncomingAclPacket(incoming);
       break;
-    case model::packets::PacketType::COMMAND:
-      IncomingCommandPacket(incoming);
-      break;
     case model::packets::PacketType::DISCONNECT:
       IncomingDisconnectPacket(incoming);
       break;
@@ -181,6 +197,9 @@
     case model::packets::PacketType::IO_CAPABILITY_REQUEST:
       IncomingIoCapabilityRequestPacket(incoming);
       break;
+    case model::packets::PacketType::IO_CAPABILITY_RESPONSE:
+      IncomingIoCapabilityResponsePacket(incoming);
+      break;
     case model::packets::PacketType::IO_CAPABILITY_NEGATIVE_RESPONSE:
       IncomingIoCapabilityNegativeResponsePacket(incoming);
       break;
@@ -215,11 +234,45 @@
     case model::packets::PacketType::PAGE_REJECT:
       IncomingPageRejectPacket(incoming);
       break;
-    case model::packets::PacketType::RESPONSE:
-      IncomingResponsePacket(incoming);
+    case (model::packets::PacketType::REMOTE_NAME_REQUEST):
+      IncomingRemoteNameRequest(incoming);
+      break;
+    case (model::packets::PacketType::REMOTE_NAME_REQUEST_RESPONSE):
+      IncomingRemoteNameRequestResponse(incoming);
+      break;
+    case (model::packets::PacketType::READ_REMOTE_SUPPORTED_FEATURES):
+      IncomingReadRemoteSupportedFeatures(incoming);
+      break;
+    case (model::packets::PacketType::READ_REMOTE_SUPPORTED_FEATURES_RESPONSE):
+      IncomingReadRemoteSupportedFeaturesResponse(incoming);
+      break;
+    case (model::packets::PacketType::READ_REMOTE_LMP_FEATURES):
+      IncomingReadRemoteLmpFeatures(incoming);
+      break;
+    case (model::packets::PacketType::READ_REMOTE_LMP_FEATURES_RESPONSE):
+      IncomingReadRemoteLmpFeaturesResponse(incoming);
+      break;
+    case (model::packets::PacketType::READ_REMOTE_EXTENDED_FEATURES):
+      IncomingReadRemoteExtendedFeatures(incoming);
+      break;
+    case (model::packets::PacketType::READ_REMOTE_EXTENDED_FEATURES_RESPONSE):
+      IncomingReadRemoteExtendedFeaturesResponse(incoming);
+      break;
+    case (model::packets::PacketType::READ_REMOTE_VERSION_INFORMATION):
+      IncomingReadRemoteVersion(incoming);
+      break;
+    case (model::packets::PacketType::READ_REMOTE_VERSION_INFORMATION_RESPONSE):
+      IncomingReadRemoteVersionResponse(incoming);
+      break;
+    case (model::packets::PacketType::READ_CLOCK_OFFSET):
+      IncomingReadClockOffset(incoming);
+      break;
+    case (model::packets::PacketType::READ_CLOCK_OFFSET_RESPONSE):
+      IncomingReadClockOffsetResponse(incoming);
       break;
     default:
-      LOG_WARN("Dropping unhandled packet of type %d", static_cast<int32_t>(incoming.GetType()));
+      LOG_WARN("Dropping unhandled packet of type %s",
+               model::packets::PacketTypeText(incoming.GetType()).c_str());
   }
 }
 
@@ -234,95 +287,171 @@
   std::shared_ptr<std::vector<uint8_t>> payload_bytes =
       std::make_shared<std::vector<uint8_t>>(payload.begin(), payload.end());
 
-  AclPacketView acl_view = AclPacketView::Create(payload_bytes);
+  bluetooth::hci::PacketView<bluetooth::hci::kLittleEndian> raw_packet(
+      payload_bytes);
+  auto acl_view = bluetooth::hci::AclPacketView::Create(raw_packet);
+  ASSERT(acl_view.IsValid());
+
   LOG_INFO("%s: remote handle 0x%x size %d", __func__, acl_view.GetHandle(), static_cast<int>(acl_view.size()));
   uint16_t local_handle = connections_.GetHandle(incoming.GetSourceAddress());
   LOG_INFO("%s: local handle 0x%x", __func__, local_handle);
 
-  acl::PacketBoundaryFlagsType boundary_flags = acl_view.GetPacketBoundaryFlags();
-  acl::BroadcastFlagsType broadcast_flags = acl_view.GetBroadcastFlags();
-  std::unique_ptr<RawBuilder> builder = std::make_unique<RawBuilder>();
-  std::vector<uint8_t> raw_data(acl_view.GetPayload().begin(),
-                                acl_view.GetPayload().end());
-  builder->AddOctets(raw_data);
-  send_acl_(AclPacketBuilder::Create(local_handle, boundary_flags, broadcast_flags, std::move(builder))->ToVector());
-}
-
-void LinkLayerController::IncomingCommandPacket(
-    model::packets::LinkLayerPacketView incoming) {
-  // TODO: Check the destination address to see if this packet is for me.
-  auto command = model::packets::CommandView::Create(incoming);
-  ASSERT(command.IsValid());
-
-  auto args = command.GetPayload().begin();
-  std::vector<uint64_t> response_data;
-  hci::OpCode opcode = static_cast<hci::OpCode>(args.extract<uint16_t>());
   std::unique_ptr<bluetooth::packet::RawBuilder> raw_builder_ptr =
       std::make_unique<bluetooth::packet::RawBuilder>();
+  std::vector<uint8_t> payload_data(acl_view.GetPayload().begin(),
+                                    acl_view.GetPayload().end());
+  raw_builder_ptr->AddOctets(payload_data);
 
-  switch (opcode) {
-    case (hci::OpCode::REMOTE_NAME_REQUEST): {
-      std::vector<uint8_t> name = properties_.GetName();
-      LOG_INFO("Remote Name (Local Name) %d", static_cast<int>(name.size()));
-      raw_builder_ptr->AddOctets1(
-          static_cast<uint8_t>(bluetooth::hci::ErrorCode::SUCCESS));
-      raw_builder_ptr->AddOctets8(name.size());
-      raw_builder_ptr->AddOctets(name);
-    } break;
-    case (hci::OpCode::READ_REMOTE_SUPPORTED_FEATURES):
-      LOG_INFO("(%s) Remote Supported Features Requested by: %s %x",
-               incoming.GetDestinationAddress().ToString().c_str(), incoming.GetSourceAddress().ToString().c_str(),
-               static_cast<int>(properties_.GetSupportedFeatures()));
-      raw_builder_ptr->AddOctets1(
-          static_cast<uint8_t>(bluetooth::hci::ErrorCode::SUCCESS));
-      raw_builder_ptr->AddOctets8(properties_.GetSupportedFeatures());
-      break;
-    case (hci::OpCode::READ_REMOTE_EXTENDED_FEATURES): {
-      uint8_t page_number = (args + 2).extract<uint8_t>();  // skip the handle
-      LOG_INFO("(%s) Remote Extended Features %d Requested by: %s", incoming.GetDestinationAddress().ToString().c_str(),
-               page_number, incoming.GetSourceAddress().ToString().c_str());
-      uint8_t max_page_number = properties_.GetExtendedFeaturesMaximumPageNumber();
-      if (page_number > max_page_number) {
-        raw_builder_ptr->AddOctets1(static_cast<uint8_t>(
-            bluetooth::hci::ErrorCode::INVALID_HCI_COMMAND_PARAMETERS));
-        raw_builder_ptr->AddOctets1(page_number);
-        raw_builder_ptr->AddOctets1(max_page_number);
-        raw_builder_ptr->AddOctets8(0);
-      } else {
-        raw_builder_ptr->AddOctets1(
-            static_cast<uint8_t>(bluetooth::hci::ErrorCode::SUCCESS));
-        raw_builder_ptr->AddOctets1(page_number);
-        raw_builder_ptr->AddOctets1(max_page_number);
-        raw_builder_ptr->AddOctets8(
-            properties_.GetExtendedFeatures(page_number));
-      }
-    } break;
-    case (hci::OpCode::READ_REMOTE_VERSION_INFORMATION):
-      raw_builder_ptr->AddOctets1(
-          static_cast<uint8_t>(bluetooth::hci::ErrorCode::SUCCESS));
-      raw_builder_ptr->AddOctets1(properties_.GetLmpPalVersion());
-      raw_builder_ptr->AddOctets2(properties_.GetManufacturerName());
-      raw_builder_ptr->AddOctets2(properties_.GetLmpPalSubversion());
-      break;
-    case (hci::OpCode::READ_CLOCK_OFFSET):
-      raw_builder_ptr->AddOctets1(
-          static_cast<uint8_t>(bluetooth::hci::ErrorCode::SUCCESS));
-      raw_builder_ptr->AddOctets2(properties_.GetClockOffset());
-      break;
-    default:
-      LOG_INFO("Dropping unhandled command 0x%04x", static_cast<uint16_t>(opcode));
-      return;
+  auto acl_packet = bluetooth::hci::AclPacketBuilder::Create(
+      local_handle, acl_view.GetPacketBoundaryFlag(),
+      acl_view.GetBroadcastFlag(), std::move(raw_builder_ptr));
+
+  send_acl_(std::move(acl_packet));
+}
+
+void LinkLayerController::IncomingRemoteNameRequest(
+    model::packets::LinkLayerPacketView packet) {
+  auto view = model::packets::RemoteNameRequestView::Create(packet);
+  ASSERT(view.IsValid());
+
+  SendLinkLayerPacket(model::packets::RemoteNameRequestResponseBuilder::Create(
+      packet.GetDestinationAddress(), packet.GetSourceAddress(),
+      properties_.GetName()));
+}
+
+void LinkLayerController::IncomingRemoteNameRequestResponse(
+    model::packets::LinkLayerPacketView packet) {
+  auto view = model::packets::RemoteNameRequestResponseView::Create(packet);
+  ASSERT(view.IsValid());
+
+  send_event_(bluetooth::hci::RemoteNameRequestCompleteBuilder::Create(
+      bluetooth::hci::ErrorCode::SUCCESS, packet.GetSourceAddress(),
+      view.GetName()));
+}
+
+void LinkLayerController::IncomingReadRemoteLmpFeatures(
+    model::packets::LinkLayerPacketView packet) {
+  SendLinkLayerPacket(
+      model::packets::ReadRemoteLmpFeaturesResponseBuilder::Create(
+          packet.GetDestinationAddress(), packet.GetSourceAddress(),
+          properties_.GetExtendedFeatures(1)));
+}
+
+void LinkLayerController::IncomingReadRemoteLmpFeaturesResponse(
+    model::packets::LinkLayerPacketView packet) {
+  auto view = model::packets::ReadRemoteLmpFeaturesResponseView::Create(packet);
+  ASSERT(view.IsValid());
+  send_event_(
+      bluetooth::hci::RemoteHostSupportedFeaturesNotificationBuilder::Create(
+          packet.GetSourceAddress(), view.GetFeatures()));
+}
+
+void LinkLayerController::IncomingReadRemoteSupportedFeatures(
+    model::packets::LinkLayerPacketView packet) {
+  SendLinkLayerPacket(
+      model::packets::ReadRemoteSupportedFeaturesResponseBuilder::Create(
+          packet.GetDestinationAddress(), packet.GetSourceAddress(),
+          properties_.GetSupportedFeatures()));
+}
+
+void LinkLayerController::IncomingReadRemoteSupportedFeaturesResponse(
+    model::packets::LinkLayerPacketView packet) {
+  auto view =
+      model::packets::ReadRemoteSupportedFeaturesResponseView::Create(packet);
+  ASSERT(view.IsValid());
+  Address source = packet.GetSourceAddress();
+  if (connections_.IsDeviceConnected(source)) {
+    uint16_t handle = connections_.GetHandle(source);
+    send_event_(
+        bluetooth::hci::ReadRemoteSupportedFeaturesCompleteBuilder::Create(
+            bluetooth::hci::ErrorCode::SUCCESS, handle, view.GetFeatures()));
+  } else {
+    LOG_INFO("Discarding response from a disconnected device %s",
+             source.ToString().c_str());
   }
+}
 
-  for (uint64_t data : response_data) {
-    raw_builder_ptr->AddOctets8(data);
+void LinkLayerController::IncomingReadRemoteExtendedFeatures(
+    model::packets::LinkLayerPacketView packet) {
+  auto view = model::packets::ReadRemoteExtendedFeaturesView::Create(packet);
+  ASSERT(view.IsValid());
+  uint8_t page_number = view.GetPageNumber();
+  uint8_t error_code = static_cast<uint8_t>(bluetooth::hci::ErrorCode::SUCCESS);
+  if (page_number > properties_.GetExtendedFeaturesMaximumPageNumber()) {
+    error_code = static_cast<uint8_t>(
+        bluetooth::hci::ErrorCode::INVALID_LMP_OR_LL_PARAMETERS);
   }
+  SendLinkLayerPacket(
+      model::packets::ReadRemoteExtendedFeaturesResponseBuilder::Create(
+          packet.GetDestinationAddress(), packet.GetSourceAddress(), error_code,
+          page_number, properties_.GetExtendedFeaturesMaximumPageNumber(),
+          properties_.GetExtendedFeatures(view.GetPageNumber())));
+}
 
-  auto response = model::packets::ResponseBuilder::Create(
-      properties_.GetAddress(), incoming.GetSourceAddress(),
-      static_cast<uint16_t>(opcode), std::move(raw_builder_ptr));
+void LinkLayerController::IncomingReadRemoteExtendedFeaturesResponse(
+    model::packets::LinkLayerPacketView packet) {
+  auto view =
+      model::packets::ReadRemoteExtendedFeaturesResponseView::Create(packet);
+  ASSERT(view.IsValid());
+  Address source = packet.GetSourceAddress();
+  if (connections_.IsDeviceConnected(source)) {
+    uint16_t handle = connections_.GetHandle(packet.GetSourceAddress());
+    send_event_(
+        bluetooth::hci::ReadRemoteExtendedFeaturesCompleteBuilder::Create(
+            static_cast<bluetooth::hci::ErrorCode>(view.GetStatus()), handle,
+            view.GetPageNumber(), view.GetMaxPageNumber(), view.GetFeatures()));
+  } else {
+    LOG_INFO("Discarding response from a disconnected device %s",
+             source.ToString().c_str());
+  }
+}
 
-  SendLinkLayerPacket(std::move(response));
+void LinkLayerController::IncomingReadRemoteVersion(
+    model::packets::LinkLayerPacketView packet) {
+  SendLinkLayerPacket(
+      model::packets::ReadRemoteSupportedFeaturesResponseBuilder::Create(
+          packet.GetDestinationAddress(), packet.GetSourceAddress(),
+          properties_.GetSupportedFeatures()));
+}
+
+void LinkLayerController::IncomingReadRemoteVersionResponse(
+    model::packets::LinkLayerPacketView packet) {
+  auto view =
+      model::packets::ReadRemoteVersionInformationResponseView::Create(packet);
+  ASSERT(view.IsValid());
+  Address source = packet.GetSourceAddress();
+  if (connections_.IsDeviceConnected(source)) {
+    uint16_t handle = connections_.GetHandle(packet.GetSourceAddress());
+    send_event_(
+        bluetooth::hci::ReadRemoteVersionInformationCompleteBuilder::Create(
+            bluetooth::hci::ErrorCode::SUCCESS, handle, view.GetLmpVersion(),
+            view.GetManufacturerName(), view.GetLmpSubversion()));
+  } else {
+    LOG_INFO("Discarding response from a disconnected device %s",
+             source.ToString().c_str());
+  }
+}
+
+void LinkLayerController::IncomingReadClockOffset(
+    model::packets::LinkLayerPacketView packet) {
+  SendLinkLayerPacket(model::packets::ReadClockOffsetResponseBuilder::Create(
+      packet.GetDestinationAddress(), packet.GetSourceAddress(),
+      properties_.GetClockOffset()));
+}
+
+void LinkLayerController::IncomingReadClockOffsetResponse(
+    model::packets::LinkLayerPacketView packet) {
+  auto view = model::packets::ReadClockOffsetResponseView::Create(packet);
+  ASSERT(view.IsValid());
+  Address source = packet.GetSourceAddress();
+  if (connections_.IsDeviceConnected(source)) {
+    uint16_t handle = connections_.GetHandle(packet.GetSourceAddress());
+    send_event_(bluetooth::hci::ReadClockOffsetCompleteBuilder::Create(
+        bluetooth::hci::ErrorCode::SUCCESS, handle, view.GetOffset()));
+  } else {
+    LOG_INFO("Discarding response from a disconnected device %s",
+             source.ToString().c_str());
+  }
 }
 
 void LinkLayerController::IncomingDisconnectPacket(
@@ -354,12 +483,19 @@
     LOG_INFO("%s: Unknown connection @%s", __func__, peer.ToString().c_str());
     return;
   }
-  auto packet = bluetooth::hci::EncryptionChangeBuilder::Create(
+  send_event_(bluetooth::hci::EncryptionChangeBuilder::Create(
       bluetooth::hci::ErrorCode::SUCCESS, handle,
-      bluetooth::hci::EncryptionEnabled::ON);
-  send_event_(std::move(packet));
+      bluetooth::hci::EncryptionEnabled::ON));
+
+  uint16_t count = security_manager_.ReadKey(peer);
+  if (count == 0) {
+    LOG_ERROR("NO KEY HERE for %s", peer.ToString().c_str());
+    return;
+  }
+  auto array = security_manager_.GetKey(peer);
+  std::vector<uint8_t> key_vec{array.begin(), array.end()};
   auto response = model::packets::EncryptConnectionResponseBuilder::Create(
-      properties_.GetAddress(), peer, security_manager_.GetKey(peer));
+      properties_.GetAddress(), peer, key_vec);
   SendLinkLayerPacket(std::move(response));
 }
 
@@ -428,7 +564,6 @@
 
   switch (basic_inquiry_response.GetInquiryType()) {
     case (model::packets::InquiryType::STANDARD): {
-      LOG_WARN("Incoming Standard Inquiry Response");
       // TODO: Support multiple inquiries in the same packet.
       auto inquiry_response =
           model::packets::InquiryResponseView::Create(basic_inquiry_response);
@@ -447,7 +582,6 @@
     } break;
 
     case (model::packets::InquiryType::RSSI): {
-      LOG_WARN("Incoming RSSI Inquiry Response");
       auto inquiry_response =
           model::packets::InquiryResponseWithRssiView::Create(
               basic_inquiry_response);
@@ -465,7 +599,6 @@
     } break;
 
     case (model::packets::InquiryType::EXTENDED): {
-      LOG_WARN("Incoming Extended Inquiry Response");
       auto inquiry_response =
           model::packets::ExtendedInquiryResponseView::Create(
               basic_inquiry_response);
@@ -473,7 +606,7 @@
 
       std::unique_ptr<bluetooth::packet::RawBuilder> raw_builder_ptr =
           std::make_unique<bluetooth::packet::RawBuilder>();
-      raw_builder_ptr->AddOctets1(0x01);  // num_responses
+      raw_builder_ptr->AddOctets1(kNumCommandPackets);
       raw_builder_ptr->AddAddress(inquiry_response.GetSourceAddress());
       raw_builder_ptr->AddOctets1(inquiry_response.GetPageScanRepetitionMode());
       raw_builder_ptr->AddOctets1(0x00);  // _reserved_
@@ -735,7 +868,8 @@
   ASSERT(page.IsValid());
   LOG_INFO("%s from %s", __func__, incoming.GetSourceAddress().ToString().c_str());
 
-  if (!connections_.CreatePendingConnection(incoming.GetSourceAddress())) {
+  if (!connections_.CreatePendingConnection(
+          incoming.GetSourceAddress(), properties_.GetAuthenticationEnable())) {
     // Send a response to indicate that we're busy, or drop the packet?
     LOG_WARN("%s: Failed to create a pending connection for %s", __func__,
              incoming.GetSourceAddress().ToString().c_str());
@@ -767,8 +901,10 @@
 
 void LinkLayerController::IncomingPageResponsePacket(
     model::packets::LinkLayerPacketView incoming) {
-  LOG_INFO("%s: %s", __func__, incoming.GetSourceAddress().ToString().c_str());
-  uint16_t handle = connections_.CreateConnection(incoming.GetSourceAddress());
+  Address peer = incoming.GetSourceAddress();
+  LOG_INFO("%s: %s", __func__, peer.ToString().c_str());
+  bool awaiting_authentication = connections_.AuthenticatePendingConnection();
+  uint16_t handle = connections_.CreateConnection(peer);
   if (handle == acl::kReservedHandle) {
     LOG_WARN("%s: No free handles", __func__);
     return;
@@ -777,71 +913,11 @@
       bluetooth::hci::ErrorCode::SUCCESS, handle, incoming.GetSourceAddress(),
       bluetooth::hci::LinkType::ACL, bluetooth::hci::Enable::DISABLED);
   send_event_(std::move(packet));
-}
 
-void LinkLayerController::IncomingResponsePacket(
-    model::packets::LinkLayerPacketView incoming) {
-  auto response = model::packets::ResponseView::Create(incoming);
-  ASSERT(response.IsValid());
-
-  // TODO: Check to see if I'm expecting this response.
-
-  hci::OpCode opcode = static_cast<hci::OpCode>(response.GetOpcode());
-  auto args = response.GetPayload().begin();
-  auto status = static_cast<bluetooth::hci::ErrorCode>(args.extract<uint8_t>());
-
-  uint16_t handle = connections_.GetHandle(incoming.GetSourceAddress());
-
-  switch (opcode) {
-    case (hci::OpCode::REMOTE_NAME_REQUEST): {
-      std::array<uint8_t, 248> remote_name;
-      remote_name.fill(0x00);
-      uint64_t len = args.extract<uint64_t>();
-      if (len > 247) {
-        len = 247;  // one byte for NULL octet (0x00)
-      }
-      for (uint64_t i = 0; i < len; i++) {
-        remote_name[i] = args.extract<uint8_t>();
-      }
-      auto packet = bluetooth::hci::RemoteNameRequestCompleteBuilder::Create(
-          status, incoming.GetSourceAddress(), remote_name);
-      send_event_(std::move(packet));
-    } break;
-    case (hci::OpCode::READ_REMOTE_SUPPORTED_FEATURES): {
-      auto packet =
-          bluetooth::hci::ReadRemoteSupportedFeaturesCompleteBuilder::Create(
-              status, handle, args.extract<uint64_t>());
-      send_event_(std::move(packet));
-    } break;
-    case (hci::OpCode::READ_REMOTE_EXTENDED_FEATURES): {
-      if (status == bluetooth::hci::ErrorCode::SUCCESS) {
-        auto packet =
-            bluetooth::hci::ReadRemoteExtendedFeaturesCompleteBuilder::Create(
-                status, handle, args.extract<uint8_t>(),
-                args.extract<uint8_t>(), args.extract<uint64_t>());
-        send_event_(std::move(packet));
-      } else {
-        auto packet =
-            bluetooth::hci::ReadRemoteExtendedFeaturesCompleteBuilder::Create(
-                status, handle, 0, 0, 0);
-        send_event_(std::move(packet));
-      }
-    } break;
-    case (hci::OpCode::READ_REMOTE_VERSION_INFORMATION): {
-      auto packet =
-          bluetooth::hci::ReadRemoteVersionInformationCompleteBuilder::Create(
-              status, handle, args.extract<uint8_t>(), args.extract<uint16_t>(),
-              args.extract<uint16_t>());
-      send_event_(std::move(packet));
-      LOG_INFO("Read remote version handle 0x%04x", handle);
-    } break;
-    case (hci::OpCode::READ_CLOCK_OFFSET): {
-      auto packet = bluetooth::hci::ReadClockOffsetCompleteBuilder::Create(
-          status, handle, args.extract<uint16_t>());
-      send_event_(std::move(packet));
-    } break;
-    default:
-      LOG_INFO("Unhandled response to command 0x%04x", static_cast<uint16_t>(opcode));
+  if (awaiting_authentication) {
+    ScheduleTask(milliseconds(5), [this, peer, handle]() {
+      HandleAuthenticationRequest(peer, handle);
+    });
   }
 }
 
@@ -889,7 +965,8 @@
 }
 
 void LinkLayerController::RegisterAclChannel(
-    const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>& callback) {
+    const std::function<
+        void(std::shared_ptr<bluetooth::hci::AclPacketBuilder>)>& callback) {
   send_acl_ = callback;
 }
 
@@ -964,25 +1041,24 @@
   ASSERT(security_manager_.GetAuthenticationAddress() == peer);
   // TODO: Public key exchange first?
   switch (pairing_type) {
-    case PairingType::AUTO_CONFIRMATION: {
-      auto packet =
-          bluetooth::hci::UserConfirmationRequestBuilder::Create(peer, 123456);
-      send_event_(std::move(packet));
-    } break;
+    case PairingType::AUTO_CONFIRMATION:
+      send_event_(
+          bluetooth::hci::UserConfirmationRequestBuilder::Create(peer, 123456));
+      break;
     case PairingType::CONFIRM_Y_N:
-      LOG_ALWAYS_FATAL("Unimplemented PairingType %d", static_cast<int>(pairing_type));
+      send_event_(
+          bluetooth::hci::UserConfirmationRequestBuilder::Create(peer, 123456));
       break;
     case PairingType::DISPLAY_PIN:
-      LOG_ALWAYS_FATAL("Unimplemented PairingType %d", static_cast<int>(pairing_type));
+      send_event_(
+          bluetooth::hci::UserConfirmationRequestBuilder::Create(peer, 123456));
       break;
     case PairingType::DISPLAY_AND_CONFIRM:
-      LOG_ALWAYS_FATAL("Unimplemented PairingType %d", static_cast<int>(pairing_type));
+      send_event_(
+          bluetooth::hci::UserConfirmationRequestBuilder::Create(peer, 123456));
       break;
     case PairingType::INPUT_PIN:
-      LOG_ALWAYS_FATAL("Unimplemented PairingType %d", static_cast<int>(pairing_type));
-      break;
-    case PairingType::INVALID:
-      LOG_ALWAYS_FATAL("Unimplemented PairingType %d", static_cast<int>(pairing_type));
+      send_event_(bluetooth::hci::UserPasskeyRequestBuilder::Create(peer));
       break;
     default:
       LOG_ALWAYS_FATAL("Invalid PairingType %d", static_cast<int>(pairing_type));
@@ -999,9 +1075,8 @@
 }
 
 bluetooth::hci::ErrorCode LinkLayerController::LinkKeyRequestReply(
-    const Address& peer, PacketView<true> key) {
-  std::vector<uint8_t> key_vec(key.begin(), key.end());
-  security_manager_.WriteKey(peer, key_vec);
+    const Address& peer, const std::array<uint8_t, 16>& key) {
+  security_manager_.WriteKey(peer, key);
   security_manager_.AuthenticationRequestFinished();
 
   ScheduleTask(milliseconds(5), [this, peer]() { AuthenticateRemoteStage2(peer); });
@@ -1033,19 +1108,18 @@
   PairingType pairing_type = security_manager_.GetSimplePairingType();
 
   if (pairing_type != PairingType::INVALID) {
-    ScheduleTask(milliseconds(5), [this, peer, pairing_type]() { AuthenticateRemoteStage1(peer, pairing_type); });
-    auto packet = model::packets::IoCapabilityResponseBuilder::Create(
+    ScheduleTask(milliseconds(5), [this, peer, pairing_type]() {
+      AuthenticateRemoteStage1(peer, pairing_type);
+    });
+    SendLinkLayerPacket(model::packets::IoCapabilityResponseBuilder::Create(
         properties_.GetAddress(), peer, io_capability, oob_data_present_flag,
-        authentication_requirements);
-    SendLinkLayerPacket(std::move(packet));
-
+        authentication_requirements));
   } else {
     LOG_INFO("%s: Requesting remote capability", __func__);
 
-    auto packet = model::packets::IoCapabilityRequestBuilder::Create(
+    SendLinkLayerPacket(model::packets::IoCapabilityRequestBuilder::Create(
         properties_.GetAddress(), peer, io_capability, oob_data_present_flag,
-        authentication_requirements);
-    SendLinkLayerPacket(std::move(packet));
+        authentication_requirements));
   }
 
   return bluetooth::hci::ErrorCode::SUCCESS;
@@ -1072,12 +1146,19 @@
     return bluetooth::hci::ErrorCode::AUTHENTICATION_FAILURE;
   }
   // TODO: Key could be calculated here.
-  std::vector<uint8_t> key_vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
+  std::array<uint8_t, 16> key_vec{1, 2,  3,  4,  5,  6,  7,  8,
+                                  9, 10, 11, 12, 13, 14, 15, 16};
   security_manager_.WriteKey(peer, key_vec);
 
   security_manager_.AuthenticationRequestFinished();
 
-  ScheduleTask(milliseconds(5), [this, peer]() { AuthenticateRemoteStage2(peer); });
+  ScheduleTask(milliseconds(5), [this, peer, key_vec]() {
+    send_event_(bluetooth::hci::LinkKeyNotificationBuilder::Create(
+        peer, key_vec, bluetooth::hci::KeyType::AUTHENTICATED_P256));
+  });
+
+  ScheduleTask(milliseconds(15),
+               [this, peer]() { AuthenticateRemoteStage2(peer); });
   return bluetooth::hci::ErrorCode::SUCCESS;
 }
 
@@ -1163,8 +1244,15 @@
     return;
   }
 
+  uint16_t count = security_manager_.ReadKey(peer);
+  if (count == 0) {
+    LOG_ERROR("NO KEY HERE for %s", peer.ToString().c_str());
+    return;
+  }
+  auto array = security_manager_.GetKey(peer);
+  std::vector<uint8_t> key_vec{array.begin(), array.end()};
   auto packet = model::packets::EncryptConnectionBuilder::Create(
-      properties_.GetAddress(), peer, security_manager_.GetKey(peer));
+      properties_.GetAddress(), peer, key_vec);
   SendLinkLayerPacket(std::move(packet));
 }
 
@@ -1231,11 +1319,8 @@
     return bluetooth::hci::ErrorCode::UNKNOWN_CONNECTION;
   }
 
-  LOG_INFO("%s: Reject in 200ms", __func__);
-  ScheduleTask(milliseconds(200), [this, addr, reason]() {
-    LOG_INFO("%s: Reject", __func__);
-    RejectSlaveConnection(addr, reason);
-  });
+  ScheduleTask(milliseconds(200),
+               [this, addr, reason]() { RejectSlaveConnection(addr, reason); });
 
   return bluetooth::hci::ErrorCode::SUCCESS;
 }
@@ -1243,10 +1328,10 @@
 void LinkLayerController::RejectSlaveConnection(const Address& addr, uint8_t reason) {
   auto to_send = model::packets::PageRejectBuilder::Create(
       properties_.GetAddress(), addr, reason);
-  LOG_INFO("%s sending page reject to %s", __func__, addr.ToString().c_str());
+  LOG_INFO("%s sending page reject to %s (reason 0x%02hhx)", __func__,
+           addr.ToString().c_str(), reason);
   SendLinkLayerPacket(std::move(to_send));
 
-  ASSERT(reason >= 0x0d && reason <= 0x0f);
   auto packet = bluetooth::hci::ConnectionCompleteBuilder::Create(
       static_cast<bluetooth::hci::ErrorCode>(reason), 0xeff, addr,
       bluetooth::hci::LinkType::ACL, bluetooth::hci::Enable::DISABLED);
@@ -1256,10 +1341,10 @@
 bluetooth::hci::ErrorCode LinkLayerController::CreateConnection(
     const Address& addr, uint16_t, uint8_t, uint16_t,
     uint8_t allow_role_switch) {
-  if (!connections_.CreatePendingConnection(addr)) {
+  if (!connections_.CreatePendingConnection(
+          addr, properties_.GetAuthenticationEnable() == 1)) {
     return bluetooth::hci::ErrorCode::CONTROLLER_BUSY;
   }
-
   auto page = model::packets::PageBuilder::Create(
       properties_.GetAddress(), addr, properties_.GetClassOfDevice(),
       allow_role_switch);
@@ -1535,7 +1620,6 @@
 void LinkLayerController::StartInquiry(milliseconds timeout) {
   ScheduleTask(milliseconds(timeout), [this]() { LinkLayerController::InquiryTimeout(); });
   inquiry_state_ = Inquiry::InquiryState::INQUIRY;
-  LOG_INFO("InquiryState = %d ", static_cast<int>(inquiry_state_));
 }
 
 void LinkLayerController::InquiryCancel() {
@@ -1569,7 +1653,6 @@
   if (duration_cast<milliseconds>(now - last_inquiry_) < milliseconds(2000)) {
     return;
   }
-  LOG_INFO("Inquiry ");
 
   auto packet = model::packets::InquiryBuilder::Create(
       properties_.GetAddress(), Address::kEmpty, inquiry_mode_);
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 ffc9044..cffc935 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
@@ -24,9 +24,8 @@
 #include "include/phy.h"
 #include "model/devices/device_properties.h"
 #include "model/setup/async_manager.h"
-#include "packets/hci/acl_packet_view.h"
-#include "packets/hci/sco_packet_view.h"
 #include "packets/link_layer_packets.h"
+#include "packets/packet_view.h"
 #include "security_manager.h"
 
 namespace test_vendor_lib {
@@ -40,19 +39,19 @@
   LinkLayerController(const DeviceProperties& properties) : properties_(properties) {}
   bluetooth::hci::ErrorCode SendCommandToRemoteByAddress(
       bluetooth::hci::OpCode opcode, packets::PacketView<true> args,
-      const Address& remote, bool use_public_address);
+      const Address& remote);
   bluetooth::hci::ErrorCode SendCommandToRemoteByHandle(
       bluetooth::hci::OpCode opcode, packets::PacketView<true> args,
       uint16_t handle);
-  hci::Status SendScoToRemote(packets::ScoPacketView sco_packet);
-  hci::Status SendAclToRemote(packets::AclPacketView acl_packet);
+  hci::Status SendScoToRemote(bluetooth::hci::ScoPacketView sco_packet);
+  hci::Status SendAclToRemote(bluetooth::hci::AclPacketView acl_packet);
 
   void WriteSimplePairingMode(bool enabled);
   void StartSimplePairing(const Address& address);
   void AuthenticateRemoteStage1(const Address& address, PairingType pairing_type);
   void AuthenticateRemoteStage2(const Address& address);
-  bluetooth::hci::ErrorCode LinkKeyRequestReply(const Address& address,
-                                                packets::PacketView<true> key);
+  bluetooth::hci::ErrorCode LinkKeyRequestReply(
+      const Address& address, const std::array<uint8_t, 16>& key);
   bluetooth::hci::ErrorCode LinkKeyRequestNegativeReply(const Address& address);
   bluetooth::hci::ErrorCode IoCapabilityRequestReply(
       const Address& peer, uint8_t io_capability, uint8_t oob_data_present_flag,
@@ -106,9 +105,11 @@
   // Set the callbacks for sending packets to the HCI.
   void RegisterEventChannel(
       const std::function<void(
-          std::shared_ptr<bluetooth::hci::EventPacketBuilder>)>& send_event_);
+          std::shared_ptr<bluetooth::hci::EventPacketBuilder>)>& send_event);
 
-  void RegisterAclChannel(const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>& send_acl);
+  void RegisterAclChannel(
+      const std::function<
+          void(std::shared_ptr<bluetooth::hci::AclPacketBuilder>)>& send_acl);
 
   void RegisterScoChannel(const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>& send_sco);
 
@@ -261,7 +262,6 @@
       std::unique_ptr<model::packets::LinkLayerPacketBuilder> packet);
   void IncomingAclPacket(model::packets::LinkLayerPacketView packet);
   void IncomingAclAckPacket(model::packets::LinkLayerPacketView packet);
-  void IncomingCommandPacket(model::packets::LinkLayerPacketView packet);
   void IncomingCreateConnectionPacket(
       model::packets::LinkLayerPacketView packet);
   void IncomingDisconnectPacket(model::packets::LinkLayerPacketView packet);
@@ -287,7 +287,27 @@
   void IncomingPagePacket(model::packets::LinkLayerPacketView packet);
   void IncomingPageRejectPacket(model::packets::LinkLayerPacketView packet);
   void IncomingPageResponsePacket(model::packets::LinkLayerPacketView packet);
-  void IncomingResponsePacket(model::packets::LinkLayerPacketView packet);
+  void IncomingReadRemoteLmpFeatures(
+      model::packets::LinkLayerPacketView packet);
+  void IncomingReadRemoteLmpFeaturesResponse(
+      model::packets::LinkLayerPacketView packet);
+  void IncomingReadRemoteSupportedFeatures(
+      model::packets::LinkLayerPacketView packet);
+  void IncomingReadRemoteSupportedFeaturesResponse(
+      model::packets::LinkLayerPacketView packet);
+  void IncomingReadRemoteExtendedFeatures(
+      model::packets::LinkLayerPacketView packet);
+  void IncomingReadRemoteExtendedFeaturesResponse(
+      model::packets::LinkLayerPacketView packet);
+  void IncomingReadRemoteVersion(model::packets::LinkLayerPacketView packet);
+  void IncomingReadRemoteVersionResponse(
+      model::packets::LinkLayerPacketView packet);
+  void IncomingReadClockOffset(model::packets::LinkLayerPacketView packet);
+  void IncomingReadClockOffsetResponse(
+      model::packets::LinkLayerPacketView packet);
+  void IncomingRemoteNameRequest(model::packets::LinkLayerPacketView packet);
+  void IncomingRemoteNameRequestResponse(
+      model::packets::LinkLayerPacketView packet);
 
  private:
   const DeviceProperties& properties_;
@@ -308,7 +328,8 @@
   std::function<void(AsyncTaskId)> cancel_task_;
 
   // Callbacks to send packets back to the HCI.
-  std::function<void(std::shared_ptr<std::vector<uint8_t>>)> send_acl_;
+  std::function<void(std::shared_ptr<bluetooth::hci::AclPacketBuilder>)>
+      send_acl_;
   std::function<void(std::shared_ptr<bluetooth::hci::EventPacketBuilder>)>
       send_event_;
   std::function<void(std::shared_ptr<std::vector<uint8_t>>)> send_sco_;
diff --git a/vendor_libs/test_vendor_lib/model/controller/security_manager.cc b/vendor_libs/test_vendor_lib/model/controller/security_manager.cc
index bd3072c..38b9e64 100644
--- a/vendor_libs/test_vendor_lib/model/controller/security_manager.cc
+++ b/vendor_libs/test_vendor_lib/model/controller/security_manager.cc
@@ -44,7 +44,8 @@
   return key_store_.count(addr.ToString());
 }
 
-uint16_t SecurityManager::WriteKey(const Address& addr, const std::vector<uint8_t>& key) {
+uint16_t SecurityManager::WriteKey(const Address& addr,
+                                   const std::array<uint8_t, 16>& key) {
   if (key_store_.size() >= max_keys_) {
     return 0;
   }
@@ -52,7 +53,8 @@
   return 1;
 }
 
-const std::vector<uint8_t>& SecurityManager::GetKey(const Address& addr) const {
+const std::array<uint8_t, 16>& SecurityManager::GetKey(
+    const Address& addr) const {
   ASSERT_LOG(ReadKey(addr), "No such key");
   return key_store_.at(addr.ToString());
 }
@@ -130,7 +132,53 @@
   if (!(peer_requires_mitm || host_requires_mitm)) {
     return PairingType::AUTO_CONFIRMATION;
   }
-  return PairingType::INVALID;
+  LOG_INFO("%s: host does%s require peer does%s require MITM",
+           peer_address_.ToString().c_str(), host_requires_mitm ? "" : "n't",
+           peer_requires_mitm ? "" : "n't");
+  switch (peer_io_capability_) {
+    case IoCapabilityType::DISPLAY_ONLY:
+      switch (host_io_capability_) {
+        case IoCapabilityType::DISPLAY_ONLY:
+        case IoCapabilityType::DISPLAY_YES_NO:
+          return PairingType::AUTO_CONFIRMATION;
+        case IoCapabilityType::KEYBOARD_ONLY:
+          return PairingType::INPUT_PIN;
+        case IoCapabilityType::NO_INPUT_NO_OUTPUT:
+          return PairingType::AUTO_CONFIRMATION;
+        default:
+          return PairingType::INVALID;
+      }
+    case IoCapabilityType::DISPLAY_YES_NO:
+      switch (host_io_capability_) {
+        case IoCapabilityType::DISPLAY_ONLY:
+          return PairingType::AUTO_CONFIRMATION;
+        case IoCapabilityType::DISPLAY_YES_NO:
+          return PairingType::DISPLAY_AND_CONFIRM;
+        case IoCapabilityType::KEYBOARD_ONLY:
+          return PairingType::DISPLAY_PIN;
+        case IoCapabilityType::NO_INPUT_NO_OUTPUT:
+          return PairingType::AUTO_CONFIRMATION;
+        default:
+          return PairingType::INVALID;
+      }
+    case IoCapabilityType::KEYBOARD_ONLY:
+      switch (host_io_capability_) {
+        case IoCapabilityType::DISPLAY_ONLY:
+          return PairingType::DISPLAY_PIN;
+        case IoCapabilityType::DISPLAY_YES_NO:
+          return PairingType::DISPLAY_PIN;
+        case IoCapabilityType::KEYBOARD_ONLY:
+          return PairingType::INPUT_PIN;
+        case IoCapabilityType::NO_INPUT_NO_OUTPUT:
+          return PairingType::AUTO_CONFIRMATION;
+        default:
+          return PairingType::INVALID;
+      }
+    case IoCapabilityType::NO_INPUT_NO_OUTPUT:
+      return PairingType::AUTO_CONFIRMATION;
+    default:
+      return PairingType::INVALID;
+  }
 }
 
 }  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/model/controller/security_manager.h b/vendor_libs/test_vendor_lib/model/controller/security_manager.h
index 8d566ea..e77f1d1 100644
--- a/vendor_libs/test_vendor_lib/model/controller/security_manager.h
+++ b/vendor_libs/test_vendor_lib/model/controller/security_manager.h
@@ -16,10 +16,10 @@
 
 #pragma once
 
+#include <array>
 #include <cstdint>
 #include <string>
 #include <unordered_map>
-#include <vector>
 
 #include "hci/address.h"
 
@@ -64,12 +64,10 @@
   uint16_t DeleteKey(const Address& addr);
   uint16_t ReadAllKeys() const;
   uint16_t ReadKey(const Address& addr) const;
-  uint16_t WriteKey(const Address& addr, const std::vector<uint8_t>& key);
-  uint16_t ReadCapacity() const {
-    return max_keys_;
-  };
+  uint16_t WriteKey(const Address& addr, const std::array<uint8_t, 16>& key);
+  uint16_t ReadCapacity() const { return max_keys_; };
 
-  const std::vector<uint8_t>& GetKey(const Address& addr) const;
+  const std::array<uint8_t, 16>& GetKey(const Address& addr) const;
 
   void AuthenticationRequest(const Address& addr, uint16_t handle);
   void AuthenticationRequestFinished();
@@ -89,16 +87,16 @@
 
  private:
   uint16_t max_keys_;
-  std::unordered_map<std::string, std::vector<uint8_t>> key_store_;
+  std::unordered_map<std::string, std::array<uint8_t, 16>> key_store_;
 
   bool peer_capabilities_valid_{false};
   IoCapabilityType peer_io_capability_;
-  bool peer_oob_present_flag_;
+  bool peer_oob_present_flag_{false};
   AuthenticationType peer_authentication_requirements_;
 
   bool host_capabilities_valid_{false};
   IoCapabilityType host_io_capability_;
-  bool host_oob_present_flag_;
+  bool host_oob_present_flag_{false};
   AuthenticationType host_authentication_requirements_;
 
   bool authenticating_{false};
diff --git a/vendor_libs/test_vendor_lib/model/devices/car_kit.cc b/vendor_libs/test_vendor_lib/model/devices/car_kit.cc
index f3fc3e6..65603f1 100644
--- a/vendor_libs/test_vendor_lib/model/devices/car_kit.cc
+++ b/vendor_libs/test_vendor_lib/model/devices/car_kit.cc
@@ -31,7 +31,8 @@
   page_scan_delay_ms_ = std::chrono::milliseconds(600);
 
   // Stub in packet handling for now
-  link_layer_controller_.RegisterAclChannel([](std::shared_ptr<std::vector<uint8_t>>) {});
+  link_layer_controller_.RegisterAclChannel(
+      [](std::shared_ptr<bluetooth::hci::AclPacketBuilder>) {});
   link_layer_controller_.RegisterEventChannel(
       [](std::shared_ptr<bluetooth::hci::EventPacketBuilder>) {});
   link_layer_controller_.RegisterScoChannel([](std::shared_ptr<std::vector<uint8_t>>) {});
diff --git a/vendor_libs/test_vendor_lib/model/devices/device_properties.h b/vendor_libs/test_vendor_lib/model/devices/device_properties.h
index b9d03ba..2cbd4b0 100644
--- a/vendor_libs/test_vendor_lib/model/devices/device_properties.h
+++ b/vendor_libs/test_vendor_lib/model/devices/device_properties.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <array>
 #include <cstdint>
 #include <string>
 #include <vector>
@@ -145,12 +146,13 @@
   }
 
   void SetName(const std::vector<uint8_t>& name) {
-    name_ = name;
+    name_.fill(0);
+    for (size_t i = 0; i < 248 && i < name.size(); i++) {
+      name_[i] = name[i];
+    }
   }
 
-  const std::vector<uint8_t>& GetName() const {
-    return name_;
-  }
+  const std::array<uint8_t, 248>& GetName() const { return name_; }
 
   void SetExtendedInquiryData(const std::vector<uint8_t>& eid) {
     extended_inquiry_data_ = eid;
@@ -315,7 +317,7 @@
   std::vector<uint64_t> extended_features_{{0x875b3fd8fe8ffeff, 0x0f}};
   ClassOfDevice class_of_device_{{0, 0, 0}};
   std::vector<uint8_t> extended_inquiry_data_;
-  std::vector<uint8_t> name_;
+  std::array<uint8_t, 248> name_;
   Address address_;
   uint8_t page_scan_repetition_mode_;
   uint16_t clock_offset_;
diff --git a/vendor_libs/test_vendor_lib/packets/Android.bp b/vendor_libs/test_vendor_lib/packets/Android.bp
index d25a11f..a769e14 100644
--- a/vendor_libs/test_vendor_lib/packets/Android.bp
+++ b/vendor_libs/test_vendor_lib/packets/Android.bp
@@ -14,13 +14,9 @@
         "packet_view.cc",
         "raw_builder.cc",
         "view.cc",
-        "hci/acl_packet_builder.cc",
-        "hci/acl_packet_view.cc",
         "hci/command_packet_builder.cc",
         "hci/command_packet_view.cc",
         "hci/hci_packet_builder.cc",
-        "hci/sco_packet_builder.cc",
-        "hci/sco_packet_view.cc",
     ],
     cflags: [
         "-fvisibility=hidden",
@@ -53,7 +49,6 @@
     srcs: [
         "test/packet_builder_test.cc",
         "test/packet_view_test.cc",
-        "hci/test/acl_builder_test.cc",
         ":BluetoothHciClassSources",
     ],
     header_libs: [
diff --git a/vendor_libs/test_vendor_lib/packets/hci/acl_packet_builder.cc b/vendor_libs/test_vendor_lib/packets/hci/acl_packet_builder.cc
deleted file mode 100644
index 3c74ad6..0000000
--- a/vendor_libs/test_vendor_lib/packets/hci/acl_packet_builder.cc
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "packets/hci/acl_packet_builder.h"
-
-#include "os/log.h"
-
-using std::vector;
-using test_vendor_lib::acl::BroadcastFlagsType;
-using test_vendor_lib::acl::PacketBoundaryFlagsType;
-
-namespace test_vendor_lib {
-namespace packets {
-
-AclPacketBuilder::AclPacketBuilder(uint16_t handle, PacketBoundaryFlagsType packet_boundary_flags,
-                                   BroadcastFlagsType broadcast_flags, std::unique_ptr<BasePacketBuilder> payload)
-    : handle_(handle), packet_boundary_flags_(packet_boundary_flags), broadcast_flags_(broadcast_flags),
-      payload_(std::move(payload)) {}
-
-std::unique_ptr<AclPacketBuilder> AclPacketBuilder::Create(uint16_t handle,
-                                                           PacketBoundaryFlagsType packet_boundary_flags,
-                                                           BroadcastFlagsType broadcast_flags,
-                                                           std::unique_ptr<BasePacketBuilder> payload) {
-  return std::unique_ptr<AclPacketBuilder>(
-      new AclPacketBuilder(handle, packet_boundary_flags, broadcast_flags, std::move(payload)));
-}
-
-size_t AclPacketBuilder::size() const {
-  return 2 * sizeof(uint16_t) + payload_->size();
-}
-
-void AclPacketBuilder::Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const {
-  insert(static_cast<uint16_t>((handle_ & 0xfff) | (static_cast<uint16_t>(packet_boundary_flags_) << 12) |
-                               (static_cast<uint16_t>(broadcast_flags_) << 14)),
-         it);
-  uint16_t payload_size = payload_->size();
-
-  ASSERT_LOG(static_cast<size_t>(payload_size) == payload_->size(), "Payload too large for an ACL packet: %d",
-             static_cast<int>(payload_->size()));
-  insert(payload_size, it);
-  payload_->Serialize(it);
-}
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/hci/acl_packet_builder.h b/vendor_libs/test_vendor_lib/packets/hci/acl_packet_builder.h
deleted file mode 100644
index 9599130..0000000
--- a/vendor_libs/test_vendor_lib/packets/hci/acl_packet_builder.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2018 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 <cstdint>
-#include <memory>
-#include <vector>
-
-#include "include/acl.h"
-#include "packets/hci/hci_packet_builder.h"
-#include "packets/packet_builder.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-// ACL data packets are specified in the Bluetooth Core Specification Version
-// 4.2, Volume 2, Part E, Section 5.4.2
-class AclPacketBuilder : public HciPacketBuilder {
- public:
-  virtual ~AclPacketBuilder() override = default;
-
-  static std::unique_ptr<AclPacketBuilder> Create(uint16_t handle, acl::PacketBoundaryFlagsType packet_boundary_flags,
-                                                  acl::BroadcastFlagsType broadcast_flags,
-                                                  std::unique_ptr<BasePacketBuilder> payload);
-
-  virtual size_t size() const override;
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const;
-
- private:
-  AclPacketBuilder(uint16_t handle, acl::PacketBoundaryFlagsType packet_boundary_flags,
-                   acl::BroadcastFlagsType broadcast_flags, std::unique_ptr<BasePacketBuilder> payload);
-  AclPacketBuilder() = delete;
-  uint16_t handle_;
-  acl::PacketBoundaryFlagsType packet_boundary_flags_;
-  acl::BroadcastFlagsType broadcast_flags_;
-  std::unique_ptr<BasePacketBuilder> payload_;
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/hci/acl_packet_view.cc b/vendor_libs/test_vendor_lib/packets/hci/acl_packet_view.cc
deleted file mode 100644
index 4e41a73..0000000
--- a/vendor_libs/test_vendor_lib/packets/hci/acl_packet_view.cc
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "packets/hci/acl_packet_view.h"
-
-#include "os/log.h"
-
-using std::vector;
-using test_vendor_lib::acl::BroadcastFlagsType;
-using test_vendor_lib::acl::PacketBoundaryFlagsType;
-
-namespace test_vendor_lib {
-namespace packets {
-
-AclPacketView::AclPacketView(std::shared_ptr<std::vector<uint8_t>> packet) : PacketView<true>(packet) {}
-
-AclPacketView::AclPacketView(PacketView<true> packet_view) : PacketView<true>(packet_view) {}
-
-AclPacketView AclPacketView::Create(std::shared_ptr<std::vector<uint8_t>> packet) {
-  return AclPacketView(packet);
-}
-
-AclPacketView AclPacketView::Create(PacketView<true> packet_view) {
-  return AclPacketView(packet_view);
-}
-
-uint16_t AclPacketView::GetHandle() const {
-  return begin().extract<uint16_t>() & 0xfff;
-}
-
-PacketBoundaryFlagsType AclPacketView::GetPacketBoundaryFlags() const {
-  return static_cast<PacketBoundaryFlagsType>(((begin() + 1).extract<uint8_t>() & 0x30) >> 4);
-}
-
-BroadcastFlagsType AclPacketView::GetBroadcastFlags() const {
-  return static_cast<BroadcastFlagsType>(((begin() + 1).extract<uint8_t>() & 0xc0) >> 6);
-}
-
-PacketView<true> AclPacketView::GetPayload() const {
-  uint16_t payload_size = (begin() + sizeof(uint16_t)).extract<uint16_t>();
-  ASSERT_LOG(static_cast<uint16_t>(size() - 2 * sizeof(uint16_t)) == payload_size,
-             "Malformed ACL packet payload_size %d + 4 != %d", static_cast<int>(payload_size),
-             static_cast<int>(size()));
-  return SubViewLittleEndian(2 * sizeof(uint16_t), size());
-}
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/hci/acl_packet_view.h b/vendor_libs/test_vendor_lib/packets/hci/acl_packet_view.h
deleted file mode 100644
index 1e69cda..0000000
--- a/vendor_libs/test_vendor_lib/packets/hci/acl_packet_view.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2018 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 <cstdint>
-#include <memory>
-#include <vector>
-
-#include "include/acl.h"
-#include "packets/packet_view.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-// ACL data packets are specified in the Bluetooth Core Specification Version
-// 4.2, Volume 2, Part E, Section 5.4.2
-class AclPacketView : public PacketView<true> {
- public:
-  virtual ~AclPacketView() override = default;
-
-  static AclPacketView Create(std::shared_ptr<std::vector<uint8_t>> packet);
-  static AclPacketView Create(PacketView<true> packet_view);
-
-  uint16_t GetHandle() const;
-  acl::PacketBoundaryFlagsType GetPacketBoundaryFlags() const;
-  acl::BroadcastFlagsType GetBroadcastFlags() const;
-  PacketView<true> GetPayload() const;
-
- private:
-  AclPacketView(std::shared_ptr<std::vector<uint8_t>> packet);
-  AclPacketView(PacketView<true> packet_view);
-  AclPacketView() = delete;
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/hci/sco_packet_builder.cc b/vendor_libs/test_vendor_lib/packets/hci/sco_packet_builder.cc
deleted file mode 100644
index ae61124..0000000
--- a/vendor_libs/test_vendor_lib/packets/hci/sco_packet_builder.cc
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "packets/hci/sco_packet_builder.h"
-
-#include "os/log.h"
-
-using std::vector;
-using test_vendor_lib::sco::PacketStatusFlagsType;
-
-namespace test_vendor_lib {
-namespace packets {
-
-ScoPacketBuilder::ScoPacketBuilder(uint16_t handle, PacketStatusFlagsType packet_status_flags,
-                                   std::unique_ptr<BasePacketBuilder> payload)
-    : handle_(handle), packet_status_flags_(packet_status_flags), payload_(std::move(payload)) {}
-
-size_t ScoPacketBuilder::size() const {
-  return 2 * sizeof(uint16_t) + payload_->size();
-}
-
-void ScoPacketBuilder::Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const {
-  insert(static_cast<uint16_t>((handle_ & 0xfff) | (static_cast<uint16_t>(packet_status_flags_) << 12)), it);
-  uint8_t payload_size = payload_->size();
-
-  ASSERT_LOG(static_cast<size_t>(payload_size) == payload_->size(), "Payload too large for a SCO packet: %d",
-             static_cast<int>(payload_->size()));
-  insert(payload_size, it);
-  payload_->Serialize(it);
-}
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/hci/sco_packet_builder.h b/vendor_libs/test_vendor_lib/packets/hci/sco_packet_builder.h
deleted file mode 100644
index 94a71f5..0000000
--- a/vendor_libs/test_vendor_lib/packets/hci/sco_packet_builder.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2018 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 <cstdint>
-#include <memory>
-#include <vector>
-
-#include "include/sco.h"
-#include "packets/hci/hci_packet_builder.h"
-#include "packets/packet_builder.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-// SCO data packets are specified in the Bluetooth Core Specification Version
-// 4.2, Volume 2, Part E, Section 5.4.3
-class ScoPacketBuilder : public HciPacketBuilder {
- public:
-  virtual ~ScoPacketBuilder() override = default;
-
-  static std::unique_ptr<ScoPacketBuilder> Create(uint16_t handle, sco::PacketStatusFlagsType packet_status_flags,
-                                                  std::unique_ptr<BasePacketBuilder> payload);
-
-  virtual size_t size() const override;
-
-  virtual void Serialize(std::back_insert_iterator<std::vector<uint8_t>> it) const override;
-
- private:
-  ScoPacketBuilder(uint16_t handle, sco::PacketStatusFlagsType packet_status_flags,
-                   std::unique_ptr<BasePacketBuilder> payload);
-  ScoPacketBuilder() = delete;
-  uint16_t handle_;
-  sco::PacketStatusFlagsType packet_status_flags_;
-  std::unique_ptr<BasePacketBuilder> payload_;
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/hci/sco_packet_view.cc b/vendor_libs/test_vendor_lib/packets/hci/sco_packet_view.cc
deleted file mode 100644
index 415ce05..0000000
--- a/vendor_libs/test_vendor_lib/packets/hci/sco_packet_view.cc
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "packets/hci/sco_packet_view.h"
-
-#include "os/log.h"
-
-using test_vendor_lib::sco::PacketStatusFlagsType;
-
-namespace test_vendor_lib {
-namespace packets {
-
-ScoPacketView::ScoPacketView(std::shared_ptr<std::vector<uint8_t>> packet) : PacketView<true>(packet) {}
-
-ScoPacketView ScoPacketView::Create(std::shared_ptr<std::vector<uint8_t>> packet) {
-  return ScoPacketView(packet);
-}
-
-uint16_t ScoPacketView::GetHandle() const {
-  return begin().extract<uint16_t>() & 0xfff;
-}
-
-PacketStatusFlagsType ScoPacketView::GetPacketStatusFlags() const {
-  return static_cast<PacketStatusFlagsType>(((begin() + 1).extract<uint8_t>() & 0x30) >> 4);
-}
-
-PacketView<true> ScoPacketView::GetPayload() const {
-  uint8_t payload_size = (begin() + sizeof(uint16_t)).extract<uint8_t>();
-  ASSERT_LOG(static_cast<uint8_t>(size() - sizeof(uint16_t) - sizeof(uint8_t)) == payload_size,
-             "Malformed SCO packet payload_size %d + 4 != %d", static_cast<int>(payload_size),
-             static_cast<int>(size()));
-  return SubViewLittleEndian(sizeof(uint16_t) + sizeof(uint8_t), size());
-}
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/hci/sco_packet_view.h b/vendor_libs/test_vendor_lib/packets/hci/sco_packet_view.h
deleted file mode 100644
index 3fb24b0..0000000
--- a/vendor_libs/test_vendor_lib/packets/hci/sco_packet_view.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2018 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 <cstdint>
-#include <memory>
-#include <vector>
-
-#include "include/sco.h"
-#include "packets/packet_view.h"
-
-namespace test_vendor_lib {
-namespace packets {
-
-// SCO data packets are specified in the Bluetooth Core Specification Version
-// 4.2, Volume 2, Part E, Section 5.4.3
-class ScoPacketView : public PacketView<true> {
- public:
-  virtual ~ScoPacketView() override = default;
-
-  static ScoPacketView Create(std::shared_ptr<std::vector<uint8_t>> packet);
-
-  uint16_t GetHandle() const;
-  sco::PacketStatusFlagsType GetPacketStatusFlags() const;
-  PacketView<true> GetPayload() const;
-
- private:
-  ScoPacketView(std::shared_ptr<std::vector<uint8_t>> packet);
-  ScoPacketView() = delete;
-};
-
-}  // namespace packets
-}  // namespace test_vendor_lib
diff --git a/vendor_libs/test_vendor_lib/packets/hci/test/acl_builder_test.cc b/vendor_libs/test_vendor_lib/packets/hci/test/acl_builder_test.cc
deleted file mode 100644
index ad99df1..0000000
--- a/vendor_libs/test_vendor_lib/packets/hci/test/acl_builder_test.cc
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "packets/hci/acl_packet_builder.h"
-#include "packets/hci/acl_packet_view.h"
-#include "packets/raw_builder.h"
-
-#include <gtest/gtest.h>
-#include <forward_list>
-#include <memory>
-
-#include "hci/address.h"
-
-using ::bluetooth::hci::Address;
-using std::vector;
-using test_vendor_lib::acl::BroadcastFlagsType;
-using test_vendor_lib::acl::PacketBoundaryFlagsType;
-
-namespace {
-vector<uint8_t> count = {
-    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
-    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
-};
-
-vector<uint8_t> information_request = {
-    0xfe, 0x2e, 0x0a, 0x00, 0x06, 0x00, 0x01, 0x00, 0x0a, 0x02, 0x02, 0x00, 0x02, 0x00,
-};
-
-}  // namespace
-
-namespace test_vendor_lib {
-namespace packets {
-
-class AclBuilderTest : public ::testing::Test {
- public:
-  AclBuilderTest() = default;
-  ~AclBuilderTest() override = default;
-};
-
-TEST(AclBuilderTest, buildAclCountTest) {
-  uint16_t handle = 0x0102;
-  PacketBoundaryFlagsType packet_boundary_flags = PacketBoundaryFlagsType::FIRST_AUTOMATICALLY_FLUSHABLE;
-  BroadcastFlagsType broadcast_flags = BroadcastFlagsType::ACTIVE_SLAVE_BROADCAST;
-
-  std::unique_ptr<RawBuilder> count_payload = std::make_unique<RawBuilder>();
-  count_payload->AddOctets(count);
-  ASSERT_EQ(count.size(), count_payload->size());
-
-  std::unique_ptr<AclPacketBuilder> count_packet =
-      AclPacketBuilder::Create(handle, packet_boundary_flags, broadcast_flags, std::move(count_payload));
-
-  ASSERT_EQ(count.size() + 4, count_packet->size());
-
-  std::shared_ptr<std::vector<uint8_t>> count_packet_bytes = count_packet->ToVector();
-  AclPacketView count_packet_view = AclPacketView::Create(count_packet_bytes);
-
-  ASSERT_EQ(handle, count_packet_view.GetHandle());
-  ASSERT_EQ(packet_boundary_flags, count_packet_view.GetPacketBoundaryFlags());
-  ASSERT_EQ(broadcast_flags, count_packet_view.GetBroadcastFlags());
-  PacketView<true> count_view = count_packet_view.GetPayload();
-
-  ASSERT_EQ(count_view.size(), count.size());
-  for (size_t i = 0; i < count_view.size(); i++) {
-    ASSERT_EQ(count_view[i], count[i]);
-  }
-}
-
-TEST(AclBuilderTest, buildInformationRequest) {
-  uint16_t handle = 0x0efe;
-  PacketBoundaryFlagsType packet_boundary_flags = PacketBoundaryFlagsType::FIRST_AUTOMATICALLY_FLUSHABLE;
-  BroadcastFlagsType broadcast_flags = BroadcastFlagsType::POINT_TO_POINT;
-
-  std::vector<uint8_t> payload_bytes(information_request.begin() + 4, information_request.end());
-  std::unique_ptr<RawBuilder> payload = std::make_unique<RawBuilder>();
-  payload->AddOctets(payload_bytes);
-  ASSERT_EQ(payload_bytes.size(), payload->size());
-
-  std::unique_ptr<AclPacketBuilder> packet =
-      AclPacketBuilder::Create(handle, packet_boundary_flags, broadcast_flags, std::move(payload));
-
-  ASSERT_EQ(information_request.size(), packet->size());
-
-  std::shared_ptr<std::vector<uint8_t>> packet_bytes = packet->ToVector();
-  AclPacketView packet_view = AclPacketView::Create(packet_bytes);
-
-  ASSERT_EQ(packet_bytes->size(), information_request.size());
-  for (size_t i = 0; i < packet_bytes->size(); i++) {
-    ASSERT_EQ((*packet_bytes)[i], information_request[i]);
-  }
-
-  ASSERT_EQ(handle, packet_view.GetHandle());
-  ASSERT_EQ(packet_boundary_flags, packet_view.GetPacketBoundaryFlags());
-  ASSERT_EQ(broadcast_flags, packet_view.GetBroadcastFlags());
-  PacketView<true> payload_view = packet_view.GetPayload();
-
-  ASSERT_EQ(payload_view.size(), payload_bytes.size());
-  for (size_t i = 0; i < payload_view.size(); i++) {
-    ASSERT_EQ(payload_view[i], payload_bytes[i]);
-  }
-
-  ASSERT_EQ(packet_view.size(), information_request.size());
-  for (size_t i = 0; i < packet_view.size(); i++) {
-    ASSERT_EQ(packet_view[i], information_request[i]);
-  }
-}
-
-}  // namespace packets
-}  // namespace test_vendor_lib
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 0c27d97..2d601cd 100644
--- a/vendor_libs/test_vendor_lib/packets/link_layer_packets.pdl
+++ b/vendor_libs/test_vendor_lib/packets/link_layer_packets.pdl
@@ -227,8 +227,7 @@
 }
 
 packet RemoteNameRequestResponse : LinkLayerPacket (type = REMOTE_NAME_REQUEST_RESPONSE) {
-  _size_(name) : 8,
-  name : 8[],
+  name : 8[248],
 }
 
 packet ScoPacket : LinkLayerPacket (type = SCO) {
diff --git a/vendor_libs/test_vendor_lib/test/security_manager_unittest.cc b/vendor_libs/test_vendor_lib/test/security_manager_unittest.cc
index 13aab3c..9c19c4f 100644
--- a/vendor_libs/test_vendor_lib/test/security_manager_unittest.cc
+++ b/vendor_libs/test_vendor_lib/test/security_manager_unittest.cc
@@ -16,22 +16,20 @@
  *
  ******************************************************************************/
 
-#include <gtest/gtest.h>
-#include <string>
-#include <vector>
-using std::vector;
-
 #include "model/controller/security_manager.h"
 
+#include <gtest/gtest.h>
+#include <array>
+#include <string>
+
 namespace {
 const std::string kTestAddr1 = "12:34:56:78:9a:bc";
 const std::string kTestAddr2 = "cb:a9:87:65:43:21";
 const std::string kTestAddr3 = "cb:a9:56:78:9a:bc";
 const std::string kTestAddr4 = "12:34:56:78:9a:bc";
-const vector<uint8_t> kZeros_octets = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
-const vector<uint8_t> kTestAddr1_octets = {0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12};
-const vector<uint8_t> kTestKey = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
-                                  0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10};
+const std::array<uint8_t, 16> kTestKey = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+                                          0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
+                                          0x0d, 0x0e, 0x0f, 0x10};
 }  // namespace
 
 namespace test_vendor_lib {