Merge "Packet: Add tests for RawBuilder, fix max"
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/gd/Android.bp b/gd/Android.bp
index 6aaf2df..46108c6 100644
--- a/gd/Android.bp
+++ b/gd/Android.bp
@@ -605,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/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_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/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/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 c7e68d5..687686f 100644
--- a/gd/l2cap/classic/cert/api.proto
+++ b/gd/l2cap/classic/cert/api.proto
@@ -5,7 +5,7 @@
 import "google/protobuf/empty.proto";
 import "facade/common.proto";
 
-service L2capModuleCert {
+service L2capClassicModuleCert {
   rpc SendL2capPacket(L2capPacket) returns (google.protobuf.Empty) {}
 
   rpc SetupLink(SetupLinkRequest) returns (SetupLinkResponse) {}
diff --git a/gd/l2cap/classic/cert/cert.cc b/gd/l2cap/classic/cert/cert.cc
index 63c8593..2de345c 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_);
@@ -323,7 +322,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)));
     }
   }
 
@@ -473,12 +472,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());
@@ -494,36 +494,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/facade.cc b/gd/l2cap/classic/facade.cc
index 9613c75..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);
@@ -126,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(
@@ -173,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_;
@@ -192,8 +192,8 @@
 
   class L2capDynamicChannelHelper {
    public:
-    L2capDynamicChannelHelper(L2capModuleFacadeService* service, L2capClassicModule* l2cap_layer, os::Handler* handler,
-                              Psm psm, RetransmissionFlowControlMode mode)
+    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;
@@ -255,7 +255,7 @@
       return packet_one;
     };
 
-    L2capModuleFacadeService* facade_service_;
+    L2capClassicModuleFacadeService* facade_service_;
     L2capClassicModule* l2cap_layer_;
     os::Handler* handler_;
     std::unique_ptr<DynamicChannelManager> dynamic_channel_manager_;
@@ -271,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_) {
@@ -329,7 +329,7 @@
       response->CopyFrom(event);
     }
 
-    L2capModuleFacadeService* service_;
+    L2capClassicModuleFacadeService* service_;
     std::map<Cid, bool> subscribed_fixed_channel_;
     std::map<Psm, bool> subscribed_dynamic_channel_;
 
@@ -339,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 7eb780d..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.
   }
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 ba613a6..c0614f9 100644
--- a/gd/l2cap/classic/internal/dynamic_channel_impl.cc
+++ b/gd/l2cap/classic/internal/dynamic_channel_impl.cc
@@ -102,21 +102,17 @@
   sender_ = sender;
 }
 
-Mtu DynamicChannelImpl::GetIncomingMtu() const {
-  return incoming_mtu_;
-}
-
 void DynamicChannelImpl::SetIncomingMtu(Mtu mtu) {
-  incoming_mtu_ = mtu;
+  sender_->SetIncomingMtu(mtu);
 }
 
 void DynamicChannelImpl::SetRetransmissionFlowControlConfig(
     const RetransmissionAndFlowControlConfigurationOption& option) {
-  sender_->SetChannelRetransmissionFlowControlMode(option.mode_);
+  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 ac1f02c..95be5c7 100644
--- a/gd/l2cap/classic/internal/dynamic_channel_impl.h
+++ b/gd/l2cap/classic/internal/dynamic_channel_impl.h
@@ -82,7 +82,6 @@
    */
   void SetSender(l2cap::internal::Sender* sender) override;
 
-  virtual Mtu GetIncomingMtu() const;
   virtual void SetIncomingMtu(Mtu mtu);
 
   virtual void SetRetransmissionFlowControlConfig(const RetransmissionAndFlowControlConfigurationOption& mode);
@@ -114,9 +113,6 @@
   ConfigurationStatus incoming_configuration_status_ = ConfigurationStatus::NOT_CONFIGURED;
 
   l2cap::internal::Sender* sender_ = nullptr;
-  Mtu incoming_mtu_ = kDefaultClassicMtu;
-  // TODO: Add all RetransmissionAndFlowControlConfigurationOptions
-  FcsType fcs_type_ = FcsType::DEFAULT;
 
   DISALLOW_COPY_AND_ASSIGN(DynamicChannelImpl);
 };
diff --git a/gd/l2cap/classic/internal/link.h b/gd/l2cap/classic/internal/link.h
index 2c6c1b8..09d7eb9 100644
--- a/gd/l2cap/classic/internal/link.h
+++ b/gd/l2cap/classic/internal/link.h
@@ -99,7 +99,7 @@
   virtual void NotifyChannelCreation(Cid cid, std::unique_ptr<DynamicChannel> channel);
   virtual void NotifyChannelFail(Cid cid);
 
-  // Information received from signalling channel
+  // Information received from signaling channel
   virtual void SetRemoteConnectionlessMtu(Mtu mtu);
   virtual Mtu GetRemoteConnectionlessMtu() const;
   virtual void SetRemoteSupportsErtm(bool supported);
@@ -107,6 +107,10 @@
   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_};
diff --git a/gd/l2cap/classic/internal/signalling_manager.cc b/gd/l2cap/classic/internal/signalling_manager.cc
index 66a3f51..7398c17 100644
--- a/gd/l2cap/classic/internal/signalling_manager.cc
+++ b/gd/l2cap/classic/internal/signalling_manager.cc
@@ -310,7 +310,6 @@
     return;
   }
 
-  RetransmissionAndFlowControlConfigurationOption rfc_option;
   channel->SetOutgoingConfigurationStatus(DynamicChannelImpl::ConfigurationStatus::CONFIGURED);
   if (channel->GetIncomingConfigurationStatus() == DynamicChannelImpl::ConfigurationStatus::CONFIGURED) {
     std::unique_ptr<DynamicChannel> user_channel = std::make_unique<DynamicChannel>(channel, handler_);
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
index 35c3002..c4cc56d 100644
--- a/gd/l2cap/internal/basic_mode_channel_data_controller_test.cc
+++ b/gd/l2cap/internal/basic_mode_channel_data_controller_test.cc
@@ -74,22 +74,28 @@
   testing::MockScheduler scheduler;
   BasicModeDataController controller{1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
   EXPECT_CALL(scheduler, OnPacketsReady(1, 1));
-  controller.OnSdu(CreateSdu({1, 2, 3}));
+  controller.OnSdu(CreateSdu({'a', 'b', 'c', 'd'}));
   auto next_packet = controller.GetNextPacket();
   EXPECT_NE(next_packet, nullptr);
+  auto view = GetPacketView(std::move(next_packet));
+  auto pdu_view = BasicFrameView::Create(view);
+  EXPECT_TRUE(pdu_view.IsValid());
+  auto payload = pdu_view.GetPayload();
+  std::string data = std::string(payload.begin(), payload.end());
+  EXPECT_EQ(data, "abcd");
 }
 
 TEST_F(BasicModeDataControllerTest, receive) {
   common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
   testing::MockScheduler scheduler;
   BasicModeDataController controller{1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
-  auto base_view = GetPacketView(CreateSdu({0, 0, 1, 0}));
-  auto basic_frame_view = BasicFrameView::Create(base_view);
-  EXPECT_TRUE(basic_frame_view.IsValid());
-  controller.OnPdu(basic_frame_view);
+  auto base_view = GetPacketView(BasicFrameBuilder::Create(1, CreateSdu({'a', 'b', 'c', 'd'})));
+  controller.OnPdu(base_view);
   sync_handler(queue_handler_);
   auto packet_view = channel_queue.GetUpEnd()->TryDequeue();
   EXPECT_NE(packet_view, nullptr);
+  std::string data = std::string(packet_view->begin(), packet_view->end());
+  EXPECT_EQ(data, "abcd");
 }
 
 }  // namespace
diff --git a/gd/l2cap/internal/data_controller.h b/gd/l2cap/internal/data_controller.h
index 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 3e5042d..2a1b930 100644
--- a/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.cc
+++ b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.cc
@@ -555,13 +555,23 @@
 
   void _send_i_frame(SegmentationAndReassembly sar, std::unique_ptr<CopyablePacketBuilder> segment, uint8_t req_seq,
                      uint8_t tx_seq, uint16_t sdu_size = 0, Final f = Final::NOT_SET) {
-    std::unique_ptr<EnhancedInformationFrameBuilder> builder;
+    std::unique_ptr<packet::BasePacketBuilder> builder;
     if (sar == SegmentationAndReassembly::START) {
-      builder = EnhancedInformationStartFrameBuilder::Create(controller_->remote_cid_, tx_seq, f, req_seq, sdu_size,
-                                                             std::move(segment));
+      if (controller_->fcs_enabled_) {
+        builder = EnhancedInformationStartFrameWithFcsBuilder::Create(controller_->remote_cid_, tx_seq, f, req_seq,
+                                                                      sdu_size, std::move(segment));
+      } else {
+        builder = EnhancedInformationStartFrameBuilder::Create(controller_->remote_cid_, tx_seq, f, req_seq, sdu_size,
+                                                               std::move(segment));
+      }
     } else {
-      builder = EnhancedInformationFrameBuilder::Create(controller_->remote_cid_, tx_seq, f, req_seq, sar,
-                                                        std::move(segment));
+      if (controller_->fcs_enabled_) {
+        builder = EnhancedInformationFrameWithFcsBuilder::Create(controller_->remote_cid_, tx_seq, f, req_seq, sar,
+                                                                 std::move(segment));
+      } else {
+        builder = EnhancedInformationFrameBuilder::Create(controller_->remote_cid_, tx_seq, f, req_seq, sar,
+                                                          std::move(segment));
+      }
     }
     controller_->send_pdu(std::move(builder));
   }
@@ -575,7 +585,6 @@
     std::unique_ptr<CopyablePacketBuilder> copyable_packet_builder =
         std::make_unique<CopyablePacketBuilder>(std::get<2>(unacked_list_.find(next_tx_seq_)->second));
     _send_i_frame(sar, std::move(copyable_packet_builder), buffer_seq_, next_tx_seq_, sdu_size, f);
-    // TODO hsz fix me
     unacked_frames_++;
     frames_sent_++;
     retry_i_frames_[next_tx_seq_] = 1;
@@ -599,7 +608,12 @@
   }
 
   void _send_s_frame(SupervisoryFunction s, uint8_t req_seq, Poll p, Final f) {
-    auto builder = EnhancedSupervisoryFrameBuilder::Create(controller_->remote_cid_, s, p, f, req_seq);
+    std::unique_ptr<packet::BasePacketBuilder> builder;
+    if (controller_->fcs_enabled_) {
+      builder = EnhancedSupervisoryFrameWithFcsBuilder::Create(controller_->remote_cid_, s, p, f, req_seq);
+    } else {
+      builder = EnhancedSupervisoryFrameBuilder::Create(controller_->remote_cid_, s, p, f, req_seq);
+    }
     controller_->send_pdu(std::move(builder));
   }
 
@@ -770,13 +784,9 @@
 
 // Segmentation is handled here
 void ErtmController::OnSdu(std::unique_ptr<packet::BasePacketBuilder> sdu) {
-  // TODO: Optimize the calculation. We don't need to count for SDU length in CONTINUATION or END packets. We don't need
-  // to FCS when disabled.
-  size_t size_each_packet =
-      (remote_mps_ - 4 /* basic L2CAP header */ - 2 /* SDU length */ - 2 /* Extended control */ - 2 /* FCS */);
   auto sdu_size = sdu->size();
   std::vector<std::unique_ptr<packet::RawBuilder>> segments;
-  packet::FragmentingInserter fragmenting_inserter(size_each_packet, std::back_insert_iterator(segments));
+  packet::FragmentingInserter fragmenting_inserter(size_each_packet_, std::back_insert_iterator(segments));
   sdu->Serialize(fragmenting_inserter);
   fragmenting_inserter.finalize();
   if (segments.size() == 1) {
@@ -790,8 +800,20 @@
   pimpl_->data_request(SegmentationAndReassembly::END, std::move(segments.back()));
 }
 
-void ErtmController::OnPdu(BasicFrameView pdu) {
-  auto standard_frame_view = StandardFrameView::Create(pdu);
+void ErtmController::OnPdu(packet::PacketView<true> pdu) {
+  if (fcs_enabled_) {
+    on_pdu_fcs(pdu);
+  } else {
+    on_pdu_no_fcs(pdu);
+  }
+}
+
+void ErtmController::on_pdu_no_fcs(const packet::PacketView<true>& pdu) {
+  auto basic_frame_view = BasicFrameView::Create(pdu);
+  if (!basic_frame_view.IsValid()) {
+    return;
+  }
+  auto standard_frame_view = StandardFrameView::Create(basic_frame_view);
   if (!standard_frame_view.IsValid()) {
     LOG_WARN("Received invalid frame");
     return;
@@ -833,7 +855,54 @@
   }
 }
 
-std::unique_ptr<BasicFrameBuilder> ErtmController::GetNextPacket() {
+void ErtmController::on_pdu_fcs(const packet::PacketView<true>& pdu) {
+  auto basic_frame_view = BasicFrameWithFcsView::Create(pdu);
+  if (!basic_frame_view.IsValid()) {
+    return;
+  }
+  auto standard_frame_view = StandardFrameWithFcsView::Create(basic_frame_view);
+  if (!standard_frame_view.IsValid()) {
+    LOG_WARN("Received invalid frame");
+    return;
+  }
+  auto type = standard_frame_view.GetFrameType();
+  if (type == FrameType::I_FRAME) {
+    auto i_frame_view = EnhancedInformationFrameWithFcsView::Create(standard_frame_view);
+    if (!i_frame_view.IsValid()) {
+      LOG_WARN("Received invalid frame");
+      return;
+    }
+    pimpl_->recv_i_frame(i_frame_view.GetF(), i_frame_view.GetTxSeq(), i_frame_view.GetReqSeq(), i_frame_view.GetSar(),
+                         i_frame_view.GetPayload());
+  } else if (type == FrameType::S_FRAME) {
+    auto s_frame_view = EnhancedSupervisoryFrameWithFcsView::Create(standard_frame_view);
+    if (!s_frame_view.IsValid()) {
+      LOG_WARN("Received invalid frame");
+      return;
+    }
+    auto req_seq = s_frame_view.GetReqSeq();
+    auto f = s_frame_view.GetF();
+    auto p = s_frame_view.GetP();
+    switch (s_frame_view.GetS()) {
+      case SupervisoryFunction::RECEIVER_READY:
+        pimpl_->recv_rr(req_seq, p, f);
+        break;
+      case SupervisoryFunction::RECEIVER_NOT_READY:
+        pimpl_->recv_rnr(req_seq, p, f);
+        break;
+      case SupervisoryFunction::REJECT:
+        pimpl_->recv_rej(req_seq, p, f);
+        break;
+      case SupervisoryFunction::SELECT_REJECT:
+        pimpl_->recv_srej(req_seq, p, f);
+        break;
+    }
+  } else {
+    LOG_WARN("Received invalid frame");
+  }
+}
+
+std::unique_ptr<packet::BasePacketBuilder> ErtmController::GetNextPacket() {
   auto next = std::move(pdu_queue_.front());
   pdu_queue_.pop();
   return next;
@@ -875,11 +944,23 @@
   }
 }
 
-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
 }
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 0566e0b..14deea3 100644
--- a/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.h
+++ b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.h
@@ -47,18 +47,19 @@
   ~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_;
+  bool fcs_enabled_ = false;
 
   class PacketViewForReassembly : public packet::PacketView<kLittleEndian> {
    public:
@@ -84,12 +85,14 @@
   SegmentationAndReassembly sar_state_ = SegmentationAndReassembly::END;
 
   void stage_for_reassembly(SegmentationAndReassembly sar, const packet::PacketView<kLittleEndian>& payload);
-  void send_pdu(std::unique_ptr<BasicFrameBuilder> pdu);
+  void send_pdu(std::unique_ptr<packet::BasePacketBuilder> pdu);
 
   void close_channel();
 
+  void on_pdu_no_fcs(const packet::PacketView<true>& pdu);
+  void on_pdu_fcs(const packet::PacketView<true>& pdu);
+
   // Configuration options
-  // TODO: Configure these number
   uint16_t local_tx_window_ = 10;
   uint16_t local_max_transmit_ = 20;
   uint16_t local_retransmit_timeout_ms_ = 2000;
@@ -98,6 +101,9 @@
   uint16_t remote_tx_window_ = 10;
   uint16_t remote_mps_ = 1010;
 
+  uint16_t size_each_packet_ =
+      (remote_mps_ - 4 /* basic L2CAP header */ - 2 /* SDU length */ - 2 /* Extended control */ - 2 /* FCS */);
+
   struct impl;
   std::unique_ptr<impl> pimpl_;
 };
diff --git a/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller_test.cc b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller_test.cc
index dd87e36..04ef82b 100644
--- a/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller_test.cc
+++ b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller_test.cc
@@ -70,7 +70,29 @@
   os::Handler* queue_handler_ = nullptr;
 };
 
-TEST_F(ErtmDataControllerTest, receive) {
+TEST_F(ErtmDataControllerTest, transmit_no_fcs) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  ErtmController controller{1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  EXPECT_CALL(scheduler, OnPacketsReady(1, 1));
+  controller.OnSdu(CreateSdu({'a', 'b', 'c', 'd'}));
+  auto next_packet = controller.GetNextPacket();
+  EXPECT_NE(next_packet, nullptr);
+  auto view = GetPacketView(std::move(next_packet));
+  auto pdu_view = BasicFrameView::Create(view);
+  EXPECT_TRUE(pdu_view.IsValid());
+  auto standard_view = StandardFrameView::Create(pdu_view);
+  EXPECT_TRUE(standard_view.IsValid());
+  auto i_frame_view = EnhancedInformationFrameView::Create(standard_view);
+  EXPECT_TRUE(i_frame_view.IsValid());
+  auto payload = i_frame_view.GetPayload();
+  std::string data = std::string(payload.begin(), payload.end());
+  EXPECT_EQ(data, "abcd");
+  EXPECT_EQ(i_frame_view.GetTxSeq(), 0);
+  EXPECT_EQ(i_frame_view.GetReqSeq(), 0);
+}
+
+TEST_F(ErtmDataControllerTest, receive_no_fcs) {
   common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
   testing::MockScheduler scheduler;
   ErtmController controller{1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
@@ -78,9 +100,47 @@
   auto builder = EnhancedInformationFrameBuilder::Create(1, 0, Final::NOT_SET, 0,
                                                          SegmentationAndReassembly::UNSEGMENTED, std::move(segment));
   auto base_view = GetPacketView(std::move(builder));
-  auto basic_frame_view = BasicFrameView::Create(base_view);
-  EXPECT_TRUE(basic_frame_view.IsValid());
-  controller.OnPdu(basic_frame_view);
+  controller.OnPdu(base_view);
+  sync_handler(queue_handler_);
+  auto payload = channel_queue.GetUpEnd()->TryDequeue();
+  EXPECT_NE(payload, nullptr);
+  std::string data = std::string(payload->begin(), payload->end());
+  EXPECT_EQ(data, "abcd");
+}
+
+TEST_F(ErtmDataControllerTest, transmit_with_fcs) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  ErtmController controller{1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  controller.EnableFcs(true);
+  EXPECT_CALL(scheduler, OnPacketsReady(1, 1));
+  controller.OnSdu(CreateSdu({'a', 'b', 'c', 'd'}));
+  auto next_packet = controller.GetNextPacket();
+  EXPECT_NE(next_packet, nullptr);
+  auto view = GetPacketView(std::move(next_packet));
+  auto pdu_view = BasicFrameWithFcsView::Create(view);
+  EXPECT_TRUE(pdu_view.IsValid());
+  auto standard_view = StandardFrameWithFcsView::Create(pdu_view);
+  EXPECT_TRUE(standard_view.IsValid());
+  auto i_frame_view = EnhancedInformationFrameWithFcsView::Create(standard_view);
+  EXPECT_TRUE(i_frame_view.IsValid());
+  auto payload = i_frame_view.GetPayload();
+  std::string data = std::string(payload.begin(), payload.end());
+  EXPECT_EQ(data, "abcd");
+  EXPECT_EQ(i_frame_view.GetTxSeq(), 0);
+  EXPECT_EQ(i_frame_view.GetReqSeq(), 0);
+}
+
+TEST_F(ErtmDataControllerTest, receive_packet_with_fcs) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  ErtmController controller{1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  controller.EnableFcs(true);
+  auto segment = CreateSdu({'a', 'b', 'c', 'd'});
+  auto builder = EnhancedInformationFrameWithFcsBuilder::Create(
+      1, 0, Final::NOT_SET, 0, SegmentationAndReassembly::UNSEGMENTED, std::move(segment));
+  auto base_view = GetPacketView(std::move(builder));
+  controller.OnPdu(base_view);
   sync_handler(queue_handler_);
   auto payload = channel_queue.GetUpEnd()->TryDequeue();
   EXPECT_NE(payload, nullptr);
diff --git a/gd/l2cap/internal/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/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/sender.cc b/gd/l2cap/internal/sender.cc
index ffe2755..7fe1dd7 100644
--- a/gd/l2cap/internal/sender.cc
+++ b/gd/l2cap/internal/sender.cc
@@ -55,22 +55,28 @@
   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
 }
diff --git a/gd/l2cap/internal/sender.h b/gd/l2cap/internal/sender.h
index 22f7737..850a88d 100644
--- a/gd/l2cap/internal/sender.h
+++ b/gd/l2cap/internal/sender.h
@@ -25,6 +25,7 @@
 #include "l2cap/cid.h"
 #include "l2cap/internal/channel_impl.h"
 #include "l2cap/internal/data_controller.h"
+#include "l2cap/l2cap_packets.h"
 #include "l2cap/mtu.h"
 #include "os/handler.h"
 #include "os/queue.h"
@@ -60,8 +61,8 @@
    */
   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/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_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/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/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) {