Merge "Add topshim for A2DP source interface"
diff --git a/audio_a2dp_hw/Android.bp b/audio_a2dp_hw/Android.bp
index 708e1a6..9207acf 100644
--- a/audio_a2dp_hw/Android.bp
+++ b/audio_a2dp_hw/Android.bp
@@ -18,7 +18,6 @@
 }
 
 // Audio A2DP shared library for target
-// ========================================================
 cc_library {
     name: "audio.a2dp.default",
     defaults: ["audio_a2dp_hw_defaults"],
@@ -44,7 +43,6 @@
 }
 
 // Audio A2DP library unit tests for target and host
-// ========================================================
 cc_test {
     name: "net_test_audio_a2dp_hw",
     test_suites: ["device-tests"],
diff --git a/audio_hal_interface/Android.bp b/audio_hal_interface/Android.bp
index 8df759a..874303a 100644
--- a/audio_hal_interface/Android.bp
+++ b/audio_hal_interface/Android.bp
@@ -1,5 +1,4 @@
 // Bluetooth Audio library for target
-// ========================================================
 package {
     // See: http://go/android-license-faq
     // A large-scale-change added 'default_applicable_licenses' to import
@@ -56,7 +55,6 @@
 }
 
 // Bluetooth Audio client interface library unit tests for target and host
-// ========================================================
 cc_test {
     name: "bluetooth-test-audio-hal-interface",
     defaults: ["fluoride_defaults"],
diff --git a/audio_hearing_aid_hw/Android.bp b/audio_hearing_aid_hw/Android.bp
index 4598077..de0f96b 100644
--- a/audio_hearing_aid_hw/Android.bp
+++ b/audio_hearing_aid_hw/Android.bp
@@ -16,7 +16,6 @@
 }
 
 // Audio A2DP shared library for target
-// ========================================================
 cc_library {
     name: "audio.hearing_aid.default",
     defaults: ["audio_hearing_aid_hw_defaults"],
@@ -32,7 +31,6 @@
 }
 
 // Audio A2DP library unit tests for target and host
-// ========================================================
 cc_test {
     name: "net_test_audio_hearing_aid_hw",
     test_suites: ["device-tests"],
diff --git a/bta/Android.bp b/bta/Android.bp
index 313ddbf..a70ceff 100644
--- a/bta/Android.bp
+++ b/bta/Android.bp
@@ -40,7 +40,6 @@
 }
 
 // BTA static library for target
-// ========================================================
 cc_library_static {
     name: "libbt-bta",
     defaults: ["fluoride_bta_defaults"],
@@ -123,7 +122,6 @@
 }
 
 // bta unit tests for target
-// ========================================================
 cc_test {
     name: "net_test_bta",
     defaults: ["fluoride_bta_defaults"],
@@ -214,7 +212,6 @@
 }
 
 // bta hf client add record tests for target
-// ========================================================
 cc_test {
     name: "net_test_hf_client_add_record",
     defaults: ["fluoride_defaults"],
@@ -245,7 +242,6 @@
 
 
 // bta unit tests for host
-// ========================================================
 cc_test {
     name: "bluetooth_vc_test",
     test_suites: ["device-tests"],
diff --git a/bta/gatt/bta_gattc_cache.cc b/bta/gatt/bta_gattc_cache.cc
index 9e4d443..9f5582a 100644
--- a/bta/gatt/bta_gattc_cache.cc
+++ b/bta/gatt/bta_gattc_cache.cc
@@ -95,22 +95,22 @@
 
 /* debug function to display the server cache */
 static void bta_gattc_display_cache_server(const Database& database) {
-  LOG(INFO) << "<================Start Server Cache =============>";
+  LOG(INFO) << "<=--------------=Start Server Cache =-----------=>";
   std::istringstream iss(database.ToString());
   for (std::string line; std::getline(iss, line);) {
     LOG(INFO) << line;
   }
-  LOG(INFO) << "<================End Server Cache =============>";
+  LOG(INFO) << "<=--------------=End Server Cache =-----------=>";
 }
 
 /** debug function to display the exploration list */
 static void bta_gattc_display_explore_record(const DatabaseBuilder& database) {
-  LOG(INFO) << "<================Start Explore Queue =============>";
+  LOG(INFO) << "<=--------------=Start Explore Queue =-----------=>";
   std::istringstream iss(database.ToString());
   for (std::string line; std::getline(iss, line);) {
     LOG(INFO) << line;
   }
-  LOG(INFO) << "<================ End Explore Queue =============>";
+  LOG(INFO) << "<=--------------= End Explore Queue =-----------=>";
 }
 #endif /* BTA_GATT_DEBUG == TRUE */
 
diff --git a/btcore/Android.bp b/btcore/Android.bp
index fc2cd4e..b9fc1ac 100644
--- a/btcore/Android.bp
+++ b/btcore/Android.bp
@@ -1,5 +1,4 @@
 // libbtcore static library for target and host
-// ========================================================
 package {
     // See: http://go/android-license-faq
     // A large-scale-change added 'default_applicable_licenses' to import
@@ -36,7 +35,6 @@
 // Note: It's good to get the tests compiled both for the host and the target so
 // we get to test with both Bionic libc and glibc
 // libbtcore unit tests for target and host
-// ========================================================
 cc_test {
     name: "net_test_btcore",
     test_suites: ["device-tests"],
diff --git a/btif/Android.bp b/btif/Android.bp
index 219f187..01c90cb 100644
--- a/btif/Android.bp
+++ b/btif/Android.bp
@@ -1,5 +1,4 @@
 // Common variables
-// ========================================================
 package {
     // See: http://go/android-license-faq
     // A large-scale-change added 'default_applicable_licenses' to import
@@ -36,7 +35,6 @@
 ]
 
 // libbtif static library for target
-// ========================================================
 cc_library_static {
     name: "libbtif",
     defaults: ["fluoride_defaults"],
@@ -139,7 +137,6 @@
 }
 
 // btif unit tests for target
-// ========================================================
 cc_test {
     name: "net_test_btif",
     defaults: ["fluoride_defaults"],
@@ -195,7 +192,6 @@
 }
 
 // btif profile queue unit tests for target
-// ========================================================
 cc_test {
     name: "net_test_btif_profile_queue",
     defaults: ["fluoride_defaults"],
@@ -218,7 +214,6 @@
 }
 
 // btif rc unit tests for target
-// ========================================================
 cc_test {
     name: "net_test_btif_rc",
     defaults: ["fluoride_defaults"],
@@ -254,7 +249,6 @@
 }
 
 // btif config cache unit tests for target
-// ========================================================
 cc_test {
     name: "net_test_btif_config_cache",
     defaults: ["fluoride_defaults"],
@@ -283,7 +277,6 @@
 }
 
 // btif hf client service tests for target
-// ========================================================
 cc_test {
     name: "net_test_btif_hf_client_service",
     defaults: ["fluoride_defaults"],
@@ -305,7 +298,6 @@
 }
 
 // Cycle stack test
-// ========================================================
 cc_test {
     name: "net_test_btif_stack",
     host_supported: true,
diff --git a/common/stop_watch_legacy.cc b/common/stop_watch_legacy.cc
index 8fb3617..110748e 100644
--- a/common/stop_watch_legacy.cc
+++ b/common/stop_watch_legacy.cc
@@ -41,7 +41,7 @@
 }
 
 void StopWatchLegacy::DumpStopWatchLog() {
-  LOG_INFO("=====================================");
+  LOG_INFO("=-----------------------------------=");
   LOG_INFO("bluetooth stopwatch log history:");
   for (int i = 0; i < LOG_BUFFER_LENGTH; i++) {
     if (current_buffer_index >= LOG_BUFFER_LENGTH) {
@@ -68,7 +68,7 @@
                      .count()));
     current_buffer_index++;
   }
-  LOG_INFO("=====================================");
+  LOG_INFO("=-----------------------------------=");
 }
 
 StopWatchLegacy::StopWatchLegacy(std::string text)
diff --git a/conf/Android.bp b/conf/Android.bp
index 9fe5ab1..15d3a8b 100644
--- a/conf/Android.bp
+++ b/conf/Android.bp
@@ -1,5 +1,4 @@
 // Bluetooth bt_stack.conf config file
-// ========================================================
 package {
     // See: http://go/android-license-faq
     // A large-scale-change added 'default_applicable_licenses' to import
@@ -16,7 +15,6 @@
 }
 
 // Bluetooth bt_did.conf config file
-// ========================================================
 prebuilt_etc {
     name: "bt_did.conf",
     src: "bt_did.conf",
diff --git a/device/Android.bp b/device/Android.bp
index 52db733..4c80258 100644
--- a/device/Android.bp
+++ b/device/Android.bp
@@ -1,5 +1,4 @@
 // Bluetooth device static library for target
-// ========================================================
 package {
     // See: http://go/android-license-faq
     // A large-scale-change added 'default_applicable_licenses' to import
@@ -34,7 +33,6 @@
 }
 
 // Bluetooth device unit tests for target
-// ========================================================
 cc_test {
     name: "net_test_device",
     test_suites: ["device-tests"],
diff --git a/device/BUILD.gn b/device/BUILD.gn
index b0c0261..27abaaa 100644
--- a/device/BUILD.gn
+++ b/device/BUILD.gn
@@ -35,6 +35,7 @@
 
   deps = [
     "//bt/gd/rust/shim:init_flags_bridge_header",
+    "//bt/gd/rust/shim:libbluetooth_rust_interop",
     "//bt/gd/rust/shim:message_loop_thread_bridge_header",
   ]
 }
diff --git a/embdrv/sbc/decoder/Android.bp b/embdrv/sbc/decoder/Android.bp
index a3e13ce..bfeb2e9 100644
--- a/embdrv/sbc/decoder/Android.bp
+++ b/embdrv/sbc/decoder/Android.bp
@@ -1,5 +1,4 @@
 // Bluetooth SBC decoder static library for target
-// ========================================================
 package {
     // See: http://go/android-license-faq
     // A large-scale-change added 'default_applicable_licenses' to import
diff --git a/gd/cert/gd_base_test.py b/gd/cert/gd_base_test.py
index b382e6a..6ed7f7d 100644
--- a/gd/cert/gd_base_test.py
+++ b/gd/cert/gd_base_test.py
@@ -64,6 +64,7 @@
         self.rootcanal_running = self.info['rootcanal_running']
         self.rootcanal_logpath = self.info['rootcanal_logpath']
         self.rootcanal_process = self.info['rootcanal_process']
+        self.rootcanal_logger = self.info['rootcanal_logger']
 
         if 'rootcanal' in self.controller_configs:
             asserts.assert_true(self.info['rootcanal_exist'],
@@ -77,7 +78,6 @@
                 msg="Cannot start root-canal at " + str(self.info['rootcanal']))
             asserts.assert_true(self.info['is_subprocess_alive'], msg="root-canal stopped immediately after running")
 
-            self.rootcanal_logger = self.info['rootcanal_logger']
             self.controller_configs = self.info['controller_configs']
 
         # Parse and construct GD device objects
diff --git a/gd/cert/gd_base_test_lib.py b/gd/cert/gd_base_test_lib.py
index 19b83f1..0cc3fac 100644
--- a/gd/cert/gd_base_test_lib.py
+++ b/gd/cert/gd_base_test_lib.py
@@ -42,6 +42,9 @@
 
     # Start root-canal if needed
     info['rootcanal_running'] = False
+    info['rootcanal_logpath'] = None
+    info['rootcanal_process'] = None
+    info['rootcanal_logger'] = None
     if 'rootcanal' in info['controller_configs']:
         info['rootcanal_running'] = True
         # Get root canal binary
diff --git a/gd/common/stop_watch.cc b/gd/common/stop_watch.cc
index 4d26d94..e5e003a 100644
--- a/gd/common/stop_watch.cc
+++ b/gd/common/stop_watch.cc
@@ -41,7 +41,7 @@
 }
 
 void StopWatch::DumpStopWatchLog() {
-  LOG_INFO("=====================================");
+  LOG_INFO("=-----------------------------------=");
   LOG_INFO("bluetooth stopwatch log history:");
   for (int i = 0; i < LOG_BUFFER_LENGTH; i++) {
     if (current_buffer_index >= LOG_BUFFER_LENGTH) {
@@ -69,7 +69,7 @@
                                 .count()));
     current_buffer_index++;
   }
-  LOG_INFO("=====================================");
+  LOG_INFO("=-----------------------------------=");
 }
 
 StopWatch::StopWatch(std::string text)
diff --git a/gd/dumpsys/Android.bp b/gd/dumpsys/Android.bp
index 4ad6376..74b32dd 100644
--- a/gd/dumpsys/Android.bp
+++ b/gd/dumpsys/Android.bp
@@ -127,7 +127,7 @@
 }
 
 cc_test {
-    name: "bluetooth_flatbuffer_test",
+    name: "bluetooth_flatbuffer_tests",
     test_suites: ["device-tests"],
     host_supported: true,
     test_options: {
diff --git a/gd/hal/cert/simple_hal_test.py b/gd/hal/cert/simple_hal_test.py
index c2224ee..3d96265 100644
--- a/gd/hal/cert/simple_hal_test.py
+++ b/gd/hal/cert/simple_hal_test.py
@@ -14,128 +14,21 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-from datetime import timedelta
-
 from cert.gd_base_test import GdBaseTestClass
-from cert.event_stream import EventStream
-from cert.truth import assertThat
-from cert.py_hal import PyHal
-from cert.matchers import HciMatchers
-from cert.captures import HciCaptures
-from google.protobuf import empty_pb2
-from facade import rootservice_pb2 as facade_rootservice_pb2
-from hal import hal_facade_pb2 as hal_facade_pb2
-from bluetooth_packets_python3 import hci_packets
-import bluetooth_packets_python3 as bt_packets
-from bluetooth_packets_python3.hci_packets import AclBuilder
-from bluetooth_packets_python3 import RawBuilder
+from hal.cert.simple_hal_test_lib import SimpleHalTestBase
 
 _GRPC_TIMEOUT = 10
 
 
-class SimpleHalTest(GdBaseTestClass):
+class SimpleHalTest(GdBaseTestClass, SimpleHalTestBase):
 
     def setup_class(self):
-        super().setup_class(dut_module='HAL', cert_module='HAL')
+        GdBaseTestClass.setup_class(self, dut_module='HAL', cert_module='HAL')
 
     def setup_test(self):
-        super().setup_test()
-
-        self.dut_hal = PyHal(self.dut)
-        self.cert_hal = PyHal(self.cert)
-
-        self.dut_hal.reset()
-        self.cert_hal.reset()
+        GdBaseTestClass.setup_test(self)
+        SimpleHalTestBase.setup_test(self, self.dut, self.cert)
 
     def teardown_test(self):
-        self.dut_hal.close()
-        self.cert_hal.close()
-        super().teardown_test()
-
-    def test_stream_events(self):
-        self.dut_hal.send_hci_command(
-            hci_packets.LeAddDeviceToConnectListBuilder(hci_packets.ConnectListAddressType.RANDOM, '0C:05:04:03:02:01'))
-
-        assertThat(self.dut_hal.get_hci_event_stream()).emits(
-            HciMatchers.Exactly(hci_packets.LeAddDeviceToConnectListCompleteBuilder(1, hci_packets.ErrorCode.SUCCESS)))
-
-    def test_loopback_hci_command(self):
-        self.dut_hal.send_hci_command(hci_packets.WriteLoopbackModeBuilder(hci_packets.LoopbackMode.ENABLE_LOCAL))
-
-        command = hci_packets.LeAddDeviceToConnectListBuilder(hci_packets.ConnectListAddressType.RANDOM,
-                                                              '0C:05:04:03:02:01')
-        self.dut_hal.send_hci_command(command)
-
-        assertThat(self.dut_hal.get_hci_event_stream()).emits(HciMatchers.LoopbackOf(command))
-
-    def test_inquiry_from_dut(self):
-        self.cert_hal.send_hci_command(hci_packets.WriteScanEnableBuilder(hci_packets.ScanEnable.INQUIRY_AND_PAGE_SCAN))
-
-        lap = hci_packets.Lap()
-        lap.lap = 0x33
-        self.dut_hal.send_hci_command(hci_packets.InquiryBuilder(lap, 0x30, 0xff))
-
-        assertThat(self.dut_hal.get_hci_event_stream()).emits(lambda packet: b'\x02\x0f' in packet.payload
-                                                              # Expecting an HCI Event (code 0x02, length 0x0f)
-                                                             )
-
-    def test_le_ad_scan_cert_advertises(self):
-        self.dut_hal.set_random_le_address('0D:05:04:03:02:01')
-
-        self.dut_hal.set_scan_parameters()
-        self.dut_hal.start_scanning()
-
-        advertisement = self.cert_hal.create_advertisement(
-            0,
-            '0C:05:04:03:02:01',
-            min_interval=512,
-            max_interval=768,
-            peer_address='A6:A5:A4:A3:A2:A1',
-            tx_power=0x7f,
-            sid=1)
-        advertisement.set_data(b'Im_A_Cert')
-        advertisement.start()
-
-        assertThat(self.dut_hal.get_hci_event_stream()).emits(lambda packet: b'Im_A_Cert' in packet.payload)
-
-        advertisement.stop()
-
-        self.dut_hal.stop_scanning()
-
-    def test_le_connection_dut_advertises(self):
-        self.cert_hal.set_random_le_address('0C:05:04:03:02:01')
-        self.cert_hal.initiate_le_connection('0D:05:04:03:02:01')
-
-        # DUT Advertises
-        advertisement = self.dut_hal.create_advertisement(0, '0D:05:04:03:02:01')
-        advertisement.set_data(b'Im_The_DUT')
-        advertisement.set_scan_response(b'Im_The_D')
-        advertisement.start()
-
-        cert_acl = self.cert_hal.complete_le_connection()
-        dut_acl = self.dut_hal.complete_le_connection()
-
-        dut_acl.send_first(b'Just SomeAclData')
-        cert_acl.send_first(b'Just SomeMoreAclData')
-
-        assertThat(self.cert_hal.get_acl_stream()).emits(lambda packet: b'SomeAclData' in packet.payload)
-        assertThat(self.dut_hal.get_acl_stream()).emits(lambda packet: b'SomeMoreAclData' in packet.payload)
-
-    def test_le_connect_list_connection_cert_advertises(self):
-        self.dut_hal.set_random_le_address('0D:05:04:03:02:01')
-        self.dut_hal.add_to_connect_list('0C:05:04:03:02:01')
-        self.dut_hal.initiate_le_connection_by_connect_list('BA:D5:A4:A3:A2:A1')
-
-        advertisement = self.cert_hal.create_advertisement(
-            1,
-            '0C:05:04:03:02:01',
-            min_interval=512,
-            max_interval=768,
-            peer_address='A6:A5:A4:A3:A2:A1',
-            tx_power=0x7F,
-            sid=0)
-        advertisement.set_data(b'Im_A_Cert')
-        advertisement.start()
-
-        assertThat(self.cert_hal.get_hci_event_stream()).emits(HciMatchers.LeConnectionComplete())
-        assertThat(self.dut_hal.get_hci_event_stream()).emits(HciMatchers.LeConnectionComplete())
+        SimpleHalTestBase.teardown_test(self)
+        GdBaseTestClass.teardown_test(self)
diff --git a/gd/hal/cert/simple_hal_test_lib.py b/gd/hal/cert/simple_hal_test_lib.py
new file mode 100644
index 0000000..9ccef7a
--- /dev/null
+++ b/gd/hal/cert/simple_hal_test_lib.py
@@ -0,0 +1,134 @@
+#!/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.
+
+from datetime import timedelta
+
+from cert.event_stream import EventStream
+from cert.truth import assertThat
+from cert.py_hal import PyHal
+from cert.matchers import HciMatchers
+from cert.captures import HciCaptures
+from google.protobuf import empty_pb2
+from facade import rootservice_pb2 as facade_rootservice_pb2
+from hal import hal_facade_pb2 as hal_facade_pb2
+from bluetooth_packets_python3 import hci_packets
+import bluetooth_packets_python3 as bt_packets
+from bluetooth_packets_python3.hci_packets import AclBuilder
+from bluetooth_packets_python3 import RawBuilder
+
+_GRPC_TIMEOUT = 10
+
+
+class SimpleHalTestBase():
+
+    def setup_test(self, dut, cert):
+        self.dut_hal = PyHal(dut)
+        self.cert_hal = PyHal(cert)
+
+        self.dut_hal.reset()
+        self.cert_hal.reset()
+
+    def teardown_test(self):
+        self.dut_hal.close()
+        self.cert_hal.close()
+
+    def test_stream_events(self):
+        self.dut_hal.send_hci_command(
+            hci_packets.LeAddDeviceToConnectListBuilder(hci_packets.ConnectListAddressType.RANDOM, '0C:05:04:03:02:01'))
+
+        assertThat(self.dut_hal.get_hci_event_stream()).emits(
+            HciMatchers.Exactly(hci_packets.LeAddDeviceToConnectListCompleteBuilder(1, hci_packets.ErrorCode.SUCCESS)))
+
+    def test_loopback_hci_command(self):
+        self.dut_hal.send_hci_command(hci_packets.WriteLoopbackModeBuilder(hci_packets.LoopbackMode.ENABLE_LOCAL))
+
+        command = hci_packets.LeAddDeviceToConnectListBuilder(hci_packets.ConnectListAddressType.RANDOM,
+                                                              '0C:05:04:03:02:01')
+        self.dut_hal.send_hci_command(command)
+
+        assertThat(self.dut_hal.get_hci_event_stream()).emits(HciMatchers.LoopbackOf(command))
+
+    def test_inquiry_from_dut(self):
+        self.cert_hal.send_hci_command(hci_packets.WriteScanEnableBuilder(hci_packets.ScanEnable.INQUIRY_AND_PAGE_SCAN))
+
+        lap = hci_packets.Lap()
+        lap.lap = 0x33
+        self.dut_hal.send_hci_command(hci_packets.InquiryBuilder(lap, 0x30, 0xff))
+
+        assertThat(self.dut_hal.get_hci_event_stream()).emits(lambda packet: b'\x02\x0f' in packet.payload
+                                                              # Expecting an HCI Event (code 0x02, length 0x0f)
+                                                             )
+
+    def test_le_ad_scan_cert_advertises(self):
+        self.dut_hal.set_random_le_address('0D:05:04:03:02:01')
+
+        self.dut_hal.set_scan_parameters()
+        self.dut_hal.start_scanning()
+
+        advertisement = self.cert_hal.create_advertisement(
+            0,
+            '0C:05:04:03:02:01',
+            min_interval=512,
+            max_interval=768,
+            peer_address='A6:A5:A4:A3:A2:A1',
+            tx_power=0x7f,
+            sid=1)
+        advertisement.set_data(b'Im_A_Cert')
+        advertisement.start()
+
+        assertThat(self.dut_hal.get_hci_event_stream()).emits(lambda packet: b'Im_A_Cert' in packet.payload)
+
+        advertisement.stop()
+
+        self.dut_hal.stop_scanning()
+
+    def test_le_connection_dut_advertises(self):
+        self.cert_hal.set_random_le_address('0C:05:04:03:02:01')
+        self.cert_hal.initiate_le_connection('0D:05:04:03:02:01')
+
+        # DUT Advertises
+        advertisement = self.dut_hal.create_advertisement(0, '0D:05:04:03:02:01')
+        advertisement.set_data(b'Im_The_DUT')
+        advertisement.set_scan_response(b'Im_The_D')
+        advertisement.start()
+
+        cert_acl = self.cert_hal.complete_le_connection()
+        dut_acl = self.dut_hal.complete_le_connection()
+
+        dut_acl.send_first(b'Just SomeAclData')
+        cert_acl.send_first(b'Just SomeMoreAclData')
+
+        assertThat(self.cert_hal.get_acl_stream()).emits(lambda packet: b'SomeAclData' in packet.payload)
+        assertThat(self.dut_hal.get_acl_stream()).emits(lambda packet: b'SomeMoreAclData' in packet.payload)
+
+    def test_le_connect_list_connection_cert_advertises(self):
+        self.dut_hal.set_random_le_address('0D:05:04:03:02:01')
+        self.dut_hal.add_to_connect_list('0C:05:04:03:02:01')
+        self.dut_hal.initiate_le_connection_by_connect_list('BA:D5:A4:A3:A2:A1')
+
+        advertisement = self.cert_hal.create_advertisement(
+            1,
+            '0C:05:04:03:02:01',
+            min_interval=512,
+            max_interval=768,
+            peer_address='A6:A5:A4:A3:A2:A1',
+            tx_power=0x7F,
+            sid=0)
+        advertisement.set_data(b'Im_A_Cert')
+        advertisement.start()
+
+        assertThat(self.cert_hal.get_hci_event_stream()).emits(HciMatchers.LeConnectionComplete())
+        assertThat(self.dut_hal.get_hci_event_stream()).emits(HciMatchers.LeConnectionComplete())
diff --git a/gd/hci/cert/controller_test.py b/gd/hci/cert/controller_test.py
index 20fe937..676b76d 100644
--- a/gd/hci/cert/controller_test.py
+++ b/gd/hci/cert/controller_test.py
@@ -14,32 +14,17 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import time
-
 from cert.gd_base_test import GdBaseTestClass
-from cert.truth import assertThat
-from google.protobuf import empty_pb2 as empty_proto
-from facade import rootservice_pb2 as facade_rootservice
-from hci.facade import controller_facade_pb2 as controller_facade
+from hci.cert.controller_test_lib import ControllerTestBase
 
 
-class ControllerTest(GdBaseTestClass):
+class ControllerTest(GdBaseTestClass, ControllerTestBase):
 
     def setup_class(self):
-        super().setup_class(dut_module='HCI_INTERFACES', cert_module='HCI_INTERFACES')
+        GdBaseTestClass.setup_class(self, dut_module='HCI_INTERFACES', cert_module='HCI_INTERFACES')
 
     def test_get_addresses(self):
-        cert_address = self.cert.hci_controller.GetMacAddressSimple()
-        dut_address = self.dut.hci_controller.GetMacAddressSimple()
-
-        assertThat(cert_address).isNotEqualTo(dut_address)
-        time.sleep(1)  # This shouldn't be needed b/149120542
+        ControllerTestBase.test_get_addresses(self, self.dut, self.cert)
 
     def test_write_local_name(self):
-        self.dut.hci_controller.WriteLocalName(controller_facade.NameMsg(name=b'ImTheDUT'))
-        self.cert.hci_controller.WriteLocalName(controller_facade.NameMsg(name=b'ImTheCert'))
-        cert_name = self.cert.hci_controller.GetLocalNameSimple()
-        dut_name = self.dut.hci_controller.GetLocalNameSimple()
-
-        assertThat(dut_name).isEqualTo(b'ImTheDUT')
-        assertThat(cert_name).isEqualTo(b'ImTheCert')
+        ControllerTestBase.test_write_local_name(self, self.dut, self.cert)
diff --git a/gd/hci/cert/controller_test_lib.py b/gd/hci/cert/controller_test_lib.py
new file mode 100644
index 0000000..e3116f4
--- /dev/null
+++ b/gd/hci/cert/controller_test_lib.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2020 - 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.
+
+import time
+
+from cert.truth import assertThat
+from google.protobuf import empty_pb2 as empty_proto
+from facade import rootservice_pb2 as facade_rootservice
+from hci.facade import controller_facade_pb2 as controller_facade
+
+
+class ControllerTestBase():
+
+    def test_get_addresses(self, dut, cert):
+        cert_address = cert.hci_controller.GetMacAddressSimple()
+        dut_address = dut.hci_controller.GetMacAddressSimple()
+
+        assertThat(cert_address).isNotEqualTo(dut_address)
+        time.sleep(1)  # This shouldn't be needed b/149120542
+
+    def test_write_local_name(self, dut, cert):
+        dut.hci_controller.WriteLocalName(controller_facade.NameMsg(name=b'ImTheDUT'))
+        cert.hci_controller.WriteLocalName(controller_facade.NameMsg(name=b'ImTheCert'))
+        cert_name = cert.hci_controller.GetLocalNameSimple()
+        dut_name = dut.hci_controller.GetLocalNameSimple()
+
+        assertThat(dut_name).isEqualTo(b'ImTheDUT')
+        assertThat(cert_name).isEqualTo(b'ImTheCert')
diff --git a/gd/hci/cert/direct_hci_test.py b/gd/hci/cert/direct_hci_test.py
index ee67e88..8e79c27 100644
--- a/gd/hci/cert/direct_hci_test.py
+++ b/gd/hci/cert/direct_hci_test.py
@@ -14,284 +14,19 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-from datetime import timedelta
-import logging
-
-from cert.captures import HalCaptures, HciCaptures
 from cert.gd_base_test import GdBaseTestClass
-from cert.matchers import HciMatchers
-from cert.py_hal import PyHal
-from cert.py_hci import PyHci
-from cert.truth import assertThat
-from hci.facade import hci_facade_pb2 as hci_facade
-from facade import common_pb2 as common
-from bluetooth_packets_python3.hci_packets import EventCode
-from bluetooth_packets_python3.hci_packets import LoopbackMode
-from bluetooth_packets_python3.hci_packets import WriteLoopbackModeBuilder
-from bluetooth_packets_python3.hci_packets import ReadLocalNameBuilder
-from bluetooth_packets_python3.hci_packets import WriteScanEnableBuilder
-from bluetooth_packets_python3.hci_packets import ScanEnable
-from bluetooth_packets_python3.hci_packets import InquiryBuilder
-from bluetooth_packets_python3.hci_packets import SubeventCode
-from bluetooth_packets_python3.hci_packets import LeSetRandomAddressBuilder
-from bluetooth_packets_python3.hci_packets import PhyScanParameters
-from bluetooth_packets_python3.hci_packets import LeScanType
-from bluetooth_packets_python3.hci_packets import LeSetExtendedScanParametersBuilder
-from bluetooth_packets_python3.hci_packets import OwnAddressType
-from bluetooth_packets_python3.hci_packets import LeScanningFilterPolicy
-from bluetooth_packets_python3.hci_packets import Enable
-from bluetooth_packets_python3.hci_packets import FilterDuplicates
-from bluetooth_packets_python3.hci_packets import LeSetExtendedAdvertisingLegacyParametersBuilder
-from bluetooth_packets_python3.hci_packets import LegacyAdvertisingProperties
-from bluetooth_packets_python3.hci_packets import PeerAddressType
-from bluetooth_packets_python3.hci_packets import AdvertisingFilterPolicy
-from bluetooth_packets_python3.hci_packets import LeSetExtendedAdvertisingRandomAddressBuilder
-from bluetooth_packets_python3.hci_packets import GapData
-from bluetooth_packets_python3.hci_packets import GapDataType
-from bluetooth_packets_python3.hci_packets import LeSetExtendedAdvertisingDataBuilder
-from bluetooth_packets_python3.hci_packets import Operation
-from bluetooth_packets_python3.hci_packets import FragmentPreference
-from bluetooth_packets_python3.hci_packets import LeSetExtendedAdvertisingScanResponseBuilder
-from bluetooth_packets_python3.hci_packets import LeSetExtendedAdvertisingEnableBuilder
-from bluetooth_packets_python3.hci_packets import LeSetExtendedScanEnableBuilder
-from bluetooth_packets_python3.hci_packets import EnabledSet
-from bluetooth_packets_python3.hci_packets import LeCreateConnPhyScanParameters
-from bluetooth_packets_python3.hci_packets import LeExtendedCreateConnectionBuilder
-from bluetooth_packets_python3.hci_packets import InitiatorFilterPolicy
-from bluetooth_packets_python3.hci_packets import AddressType
-from bluetooth_packets_python3.hci_packets import BroadcastFlag
-from bluetooth_packets_python3.hci_packets import ConnectListAddressType
-from bluetooth_packets_python3.hci_packets import LeAddDeviceToConnectListBuilder
-from bluetooth_packets_python3.hci_packets import LeSetRandomAddressBuilder
-from bluetooth_packets_python3.hci_packets import LeReadRemoteFeaturesBuilder
-from bluetooth_packets_python3.hci_packets import WritePageTimeoutBuilder
-from bluetooth_packets_python3.hci_packets import ReadBdAddrBuilder
-from bluetooth_packets_python3.hci_packets import CreateConnectionBuilder
-from bluetooth_packets_python3.hci_packets import PageScanRepetitionMode
-from bluetooth_packets_python3.hci_packets import ClockOffsetValid
-from bluetooth_packets_python3.hci_packets import CreateConnectionRoleSwitch
-from bluetooth_packets_python3.hci_packets import AcceptConnectionRequestBuilder
-from bluetooth_packets_python3.hci_packets import AcceptConnectionRequestRole
-from bluetooth_packets_python3.hci_packets import PacketBoundaryFlag
-from bluetooth_packets_python3.hci_packets import ResetBuilder
-from bluetooth_packets_python3.hci_packets import Lap
-from bluetooth_packets_python3.hci_packets import OpCode
-from bluetooth_packets_python3.hci_packets import AclBuilder
-from bluetooth_packets_python3 import RawBuilder
+from hci.cert.direct_hci_test_lib import DirectHciTestBase
 
 
-class DirectHciTest(GdBaseTestClass):
+class DirectHciTest(GdBaseTestClass, DirectHciTestBase):
 
     def setup_class(self):
-        super().setup_class(dut_module='HCI', cert_module='HAL')
+        GdBaseTestClass.setup_class(self, dut_module='HCI', cert_module='HAL')
 
     def setup_test(self):
-        super().setup_test()
-        self.dut_hci = PyHci(self.dut, acl_streaming=True)
-        self.cert_hal = PyHal(self.cert)
-        self.cert_hal.send_hci_command(ResetBuilder())
+        GdBaseTestClass.setup_test(self)
+        DirectHciTestBase.setup_test(self, self.dut, self.cert)
 
     def teardown_test(self):
-        self.dut_hci.close()
-        self.cert_hal.close()
-        super().teardown_test()
-
-    def enqueue_acl_data(self, handle, pb_flag, b_flag, data):
-        acl = AclBuilder(handle, pb_flag, b_flag, RawBuilder(data))
-        self.dut.hci.SendAcl(common.Data(payload=bytes(acl.Serialize())))
-
-    def test_local_hci_cmd_and_event(self):
-        # Loopback mode responds with ACL and SCO connection complete
-        self.dut_hci.register_for_events(EventCode.LOOPBACK_COMMAND)
-        self.dut_hci.send_command(WriteLoopbackModeBuilder(LoopbackMode.ENABLE_LOCAL))
-
-        self.dut_hci.send_command(ReadLocalNameBuilder())
-        assertThat(self.dut_hci.get_event_stream()).emits(HciMatchers.LoopbackOf(ReadLocalNameBuilder()))
-
-    def test_inquiry_from_dut(self):
-        self.dut_hci.register_for_events(EventCode.INQUIRY_RESULT)
-
-        self.cert_hal.enable_inquiry_and_page_scan()
-        lap = Lap()
-        lap.lap = 0x33
-        self.dut_hci.send_command(InquiryBuilder(lap, 0x30, 0xff))
-        assertThat(self.dut_hci.get_event_stream()).emits(HciMatchers.EventWithCode(EventCode.INQUIRY_RESULT))
-
-    def test_le_ad_scan_cert_advertises(self):
-        self.dut_hci.register_for_le_events(SubeventCode.EXTENDED_ADVERTISING_REPORT, SubeventCode.ADVERTISING_REPORT)
-
-        # DUT Scans
-        self.dut_hci.send_command(LeSetRandomAddressBuilder('0D:05:04:03:02:01'))
-        phy_scan_params = PhyScanParameters()
-        phy_scan_params.le_scan_interval = 6553
-        phy_scan_params.le_scan_window = 6553
-        phy_scan_params.le_scan_type = LeScanType.ACTIVE
-
-        self.dut_hci.send_command(
-            LeSetExtendedScanParametersBuilder(OwnAddressType.RANDOM_DEVICE_ADDRESS, LeScanningFilterPolicy.ACCEPT_ALL,
-                                               1, [phy_scan_params]))
-        self.dut_hci.send_command(LeSetExtendedScanEnableBuilder(Enable.ENABLED, FilterDuplicates.DISABLED, 0, 0))
-
-        # CERT Advertises
-        advertising_handle = 0
-        self.cert_hal.send_hci_command(
-            LeSetExtendedAdvertisingLegacyParametersBuilder(
-                advertising_handle,
-                LegacyAdvertisingProperties.ADV_IND,
-                512,
-                768,
-                7,
-                OwnAddressType.RANDOM_DEVICE_ADDRESS,
-                PeerAddressType.PUBLIC_DEVICE_OR_IDENTITY_ADDRESS,
-                'A6:A5:A4:A3:A2:A1',
-                AdvertisingFilterPolicy.ALL_DEVICES,
-                0xF7,
-                1,  # SID
-                Enable.DISABLED  # Scan request notification
-            ))
-
-        self.cert_hal.send_hci_command(
-            LeSetExtendedAdvertisingRandomAddressBuilder(advertising_handle, '0C:05:04:03:02:01'))
-        gap_name = GapData()
-        gap_name.data_type = GapDataType.COMPLETE_LOCAL_NAME
-        gap_name.data = list(bytes(b'Im_A_Cert'))
-
-        self.cert_hal.send_hci_command(
-            LeSetExtendedAdvertisingDataBuilder(advertising_handle, Operation.COMPLETE_ADVERTISEMENT,
-                                                FragmentPreference.CONTROLLER_SHOULD_NOT, [gap_name]))
-
-        gap_short_name = GapData()
-        gap_short_name.data_type = GapDataType.SHORTENED_LOCAL_NAME
-        gap_short_name.data = list(bytes(b'Im_A_C'))
-
-        self.cert_hal.send_hci_command(
-            LeSetExtendedAdvertisingScanResponseBuilder(advertising_handle, Operation.COMPLETE_ADVERTISEMENT,
-                                                        FragmentPreference.CONTROLLER_SHOULD_NOT, [gap_short_name]))
-
-        enabled_set = EnabledSet()
-        enabled_set.advertising_handle = 0
-        enabled_set.duration = 0
-        enabled_set.max_extended_advertising_events = 0
-        self.cert_hal.send_hci_command(LeSetExtendedAdvertisingEnableBuilder(Enable.ENABLED, [enabled_set]))
-
-        assertThat(self.dut_hci.get_le_event_stream()).emits(lambda packet: b'Im_A_Cert' in packet.payload)
-
-        self.cert_hal.send_hci_command(LeSetExtendedAdvertisingEnableBuilder(Enable.DISABLED, [enabled_set]))
-        self.dut_hci.send_command(LeSetExtendedScanEnableBuilder(Enable.DISABLED, FilterDuplicates.DISABLED, 0, 0))
-
-    def _verify_le_connection_complete(self):
-        cert_conn_complete_capture = HalCaptures.LeConnectionCompleteCapture()
-        assertThat(self.cert_hal.get_hci_event_stream()).emits(cert_conn_complete_capture)
-        cert_handle = cert_conn_complete_capture.get().GetConnectionHandle()
-
-        dut_conn_complete_capture = HciCaptures.LeConnectionCompleteCapture()
-        assertThat(self.dut_hci.get_le_event_stream()).emits(dut_conn_complete_capture)
-        dut_handle = dut_conn_complete_capture.get().GetConnectionHandle()
-
-        return (dut_handle, cert_handle)
-
-    @staticmethod
-    def _create_phy_scan_params():
-        phy_scan_params = LeCreateConnPhyScanParameters()
-        phy_scan_params.scan_interval = 0x60
-        phy_scan_params.scan_window = 0x30
-        phy_scan_params.conn_interval_min = 0x18
-        phy_scan_params.conn_interval_max = 0x28
-        phy_scan_params.conn_latency = 0
-        phy_scan_params.supervision_timeout = 0x1f4
-        phy_scan_params.min_ce_length = 0
-        phy_scan_params.max_ce_length = 0
-        return phy_scan_params
-
-    def test_le_connection_dut_advertises(self):
-        self.dut_hci.register_for_le_events(SubeventCode.CONNECTION_COMPLETE, SubeventCode.ADVERTISING_SET_TERMINATED,
-                                            SubeventCode.ENHANCED_CONNECTION_COMPLETE,
-                                            SubeventCode.READ_REMOTE_FEATURES_COMPLETE)
-        # Cert Connects
-        self.cert_hal.send_hci_command(LeSetRandomAddressBuilder('0C:05:04:03:02:01'))
-        phy_scan_params = DirectHciTest._create_phy_scan_params()
-        self.cert_hal.send_hci_command(
-            LeExtendedCreateConnectionBuilder(InitiatorFilterPolicy.USE_PEER_ADDRESS,
-                                              OwnAddressType.RANDOM_DEVICE_ADDRESS, AddressType.RANDOM_DEVICE_ADDRESS,
-                                              '0D:05:04:03:02:01', 1, [phy_scan_params]))
-
-        advertisement = self.dut_hci.create_advertisement(0, '0D:05:04:03:02:01')
-        advertisement.set_data(b'Im_The_DUT')
-        advertisement.set_scan_response(b'Im_The_D')
-        advertisement.start()
-
-        (dut_handle, cert_handle) = self._verify_le_connection_complete()
-
-        self.dut_hci.send_command(LeReadRemoteFeaturesBuilder(dut_handle))
-        assertThat(self.dut_hci.get_le_event_stream()).emits(
-            lambda packet: packet.payload[0] == int(EventCode.LE_META_EVENT) and packet.payload[2] == int(SubeventCode.READ_REMOTE_FEATURES_COMPLETE)
-        )
-
-        # Send ACL Data
-        self.enqueue_acl_data(dut_handle, PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE,
-                              BroadcastFlag.POINT_TO_POINT, bytes(b'Just SomeAclData'))
-        self.cert_hal.send_acl_first(cert_handle, bytes(b'Just SomeMoreAclData'))
-
-        assertThat(self.cert_hal.get_acl_stream()).emits(
-            lambda packet: logging.debug(packet.payload) or b'SomeAclData' in packet.payload)
-        assertThat(self.dut_hci.get_raw_acl_stream()).emits(
-            lambda packet: logging.debug(packet.payload) or b'SomeMoreAclData' in packet.payload)
-
-    def test_le_connect_list_connection_cert_advertises(self):
-        self.dut_hci.register_for_le_events(SubeventCode.CONNECTION_COMPLETE, SubeventCode.ENHANCED_CONNECTION_COMPLETE)
-        # DUT Connects
-        self.dut_hci.send_command(LeSetRandomAddressBuilder('0D:05:04:03:02:01'))
-        self.dut_hci.send_command(LeAddDeviceToConnectListBuilder(ConnectListAddressType.RANDOM, '0C:05:04:03:02:01'))
-        phy_scan_params = DirectHciTest._create_phy_scan_params()
-        self.dut_hci.send_command(
-            LeExtendedCreateConnectionBuilder(InitiatorFilterPolicy.USE_CONNECT_LIST,
-                                              OwnAddressType.RANDOM_DEVICE_ADDRESS, AddressType.RANDOM_DEVICE_ADDRESS,
-                                              'BA:D5:A4:A3:A2:A1', 1, [phy_scan_params]))
-
-        advertisement = self.cert_hal.create_advertisement(
-            1,
-            '0C:05:04:03:02:01',
-            min_interval=512,
-            max_interval=768,
-            peer_address='A6:A5:A4:A3:A2:A1',
-            tx_power=0x7f,
-            sid=0)
-        advertisement.set_data(b'Im_A_Cert')
-        advertisement.start()
-
-        # LeConnectionComplete
-        self._verify_le_connection_complete()
-
-    def test_connection_dut_connects(self):
-        self.dut_hci.send_command(WritePageTimeoutBuilder(0x4000))
-
-        self.cert_hal.enable_inquiry_and_page_scan()
-        address = self.cert_hal.read_own_address()
-
-        self.dut_hci.initiate_connection(address)
-        cert_acl = self.cert_hal.accept_connection()
-        dut_acl = self.dut_hci.complete_connection()
-
-        # Send ACL Data
-        dut_acl.send_first(b'Just SomeAclData')
-        cert_acl.send_first(b'Just SomeMoreAclData')
-
-        assertThat(self.cert_hal.get_acl_stream()).emits(lambda packet: b'SomeAclData' in packet.payload)
-        assertThat(self.dut_hci.get_raw_acl_stream()).emits(lambda packet: b'SomeMoreAclData' in packet.payload)
-
-    def test_connection_cert_connects(self):
-        self.cert_hal.send_hci_command(WritePageTimeoutBuilder(0x4000))
-
-        self.dut_hci.enable_inquiry_and_page_scan()
-        address = self.dut_hci.read_own_address()
-
-        self.cert_hal.initiate_connection(address)
-        dut_acl = self.dut_hci.accept_connection()
-        cert_acl = self.cert_hal.complete_connection()
-
-        # Send ACL Data
-        dut_acl.send_first(b'This is just SomeAclData')
-        cert_acl.send_first(b'This is just SomeMoreAclData')
-
-        assertThat(self.cert_hal.get_acl_stream()).emits(lambda packet: b'SomeAclData' in packet.payload)
-        assertThat(self.dut_hci.get_raw_acl_stream()).emits(lambda packet: b'SomeMoreAclData' in packet.payload)
+        DirectHciTestBase.teardown_test(self)
+        GdBaseTestClass.teardown_test(self)
diff --git a/gd/hci/cert/direct_hci_test_lib.py b/gd/hci/cert/direct_hci_test_lib.py
new file mode 100644
index 0000000..cd6c210
--- /dev/null
+++ b/gd/hci/cert/direct_hci_test_lib.py
@@ -0,0 +1,291 @@
+#!/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.
+
+from datetime import timedelta
+import logging
+
+from cert.captures import HalCaptures, HciCaptures
+from cert.matchers import HciMatchers
+from cert.py_hal import PyHal
+from cert.py_hci import PyHci
+from cert.truth import assertThat
+from hci.facade import hci_facade_pb2 as hci_facade
+from facade import common_pb2 as common
+from bluetooth_packets_python3.hci_packets import EventCode
+from bluetooth_packets_python3.hci_packets import LoopbackMode
+from bluetooth_packets_python3.hci_packets import WriteLoopbackModeBuilder
+from bluetooth_packets_python3.hci_packets import ReadLocalNameBuilder
+from bluetooth_packets_python3.hci_packets import WriteScanEnableBuilder
+from bluetooth_packets_python3.hci_packets import ScanEnable
+from bluetooth_packets_python3.hci_packets import InquiryBuilder
+from bluetooth_packets_python3.hci_packets import SubeventCode
+from bluetooth_packets_python3.hci_packets import LeSetRandomAddressBuilder
+from bluetooth_packets_python3.hci_packets import PhyScanParameters
+from bluetooth_packets_python3.hci_packets import LeScanType
+from bluetooth_packets_python3.hci_packets import LeSetExtendedScanParametersBuilder
+from bluetooth_packets_python3.hci_packets import OwnAddressType
+from bluetooth_packets_python3.hci_packets import LeScanningFilterPolicy
+from bluetooth_packets_python3.hci_packets import Enable
+from bluetooth_packets_python3.hci_packets import FilterDuplicates
+from bluetooth_packets_python3.hci_packets import LeSetExtendedAdvertisingLegacyParametersBuilder
+from bluetooth_packets_python3.hci_packets import LegacyAdvertisingProperties
+from bluetooth_packets_python3.hci_packets import PeerAddressType
+from bluetooth_packets_python3.hci_packets import AdvertisingFilterPolicy
+from bluetooth_packets_python3.hci_packets import LeSetExtendedAdvertisingRandomAddressBuilder
+from bluetooth_packets_python3.hci_packets import GapData
+from bluetooth_packets_python3.hci_packets import GapDataType
+from bluetooth_packets_python3.hci_packets import LeSetExtendedAdvertisingDataBuilder
+from bluetooth_packets_python3.hci_packets import Operation
+from bluetooth_packets_python3.hci_packets import FragmentPreference
+from bluetooth_packets_python3.hci_packets import LeSetExtendedAdvertisingScanResponseBuilder
+from bluetooth_packets_python3.hci_packets import LeSetExtendedAdvertisingEnableBuilder
+from bluetooth_packets_python3.hci_packets import LeSetExtendedScanEnableBuilder
+from bluetooth_packets_python3.hci_packets import EnabledSet
+from bluetooth_packets_python3.hci_packets import LeCreateConnPhyScanParameters
+from bluetooth_packets_python3.hci_packets import LeExtendedCreateConnectionBuilder
+from bluetooth_packets_python3.hci_packets import InitiatorFilterPolicy
+from bluetooth_packets_python3.hci_packets import AddressType
+from bluetooth_packets_python3.hci_packets import BroadcastFlag
+from bluetooth_packets_python3.hci_packets import ConnectListAddressType
+from bluetooth_packets_python3.hci_packets import LeAddDeviceToConnectListBuilder
+from bluetooth_packets_python3.hci_packets import LeSetRandomAddressBuilder
+from bluetooth_packets_python3.hci_packets import LeReadRemoteFeaturesBuilder
+from bluetooth_packets_python3.hci_packets import WritePageTimeoutBuilder
+from bluetooth_packets_python3.hci_packets import ReadBdAddrBuilder
+from bluetooth_packets_python3.hci_packets import CreateConnectionBuilder
+from bluetooth_packets_python3.hci_packets import PageScanRepetitionMode
+from bluetooth_packets_python3.hci_packets import ClockOffsetValid
+from bluetooth_packets_python3.hci_packets import CreateConnectionRoleSwitch
+from bluetooth_packets_python3.hci_packets import AcceptConnectionRequestBuilder
+from bluetooth_packets_python3.hci_packets import AcceptConnectionRequestRole
+from bluetooth_packets_python3.hci_packets import PacketBoundaryFlag
+from bluetooth_packets_python3.hci_packets import ResetBuilder
+from bluetooth_packets_python3.hci_packets import Lap
+from bluetooth_packets_python3.hci_packets import OpCode
+from bluetooth_packets_python3.hci_packets import AclBuilder
+from bluetooth_packets_python3 import RawBuilder
+
+
+class DirectHciTestBase():
+
+    def setup_test(self, dut, cert):
+        self.dut_hci = PyHci(dut, acl_streaming=True)
+        self.cert_hal = PyHal(cert)
+        self.cert_hal.send_hci_command(ResetBuilder())
+
+    def teardown_test(self):
+        self.dut_hci.close()
+        self.cert_hal.close()
+
+    def enqueue_acl_data(self, handle, pb_flag, b_flag, data):
+        acl = AclBuilder(handle, pb_flag, b_flag, RawBuilder(data))
+        self.dut.hci.SendAcl(common.Data(payload=bytes(acl.Serialize())))
+
+    def test_local_hci_cmd_and_event(self):
+        # Loopback mode responds with ACL and SCO connection complete
+        self.dut_hci.register_for_events(EventCode.LOOPBACK_COMMAND)
+        self.dut_hci.send_command(WriteLoopbackModeBuilder(LoopbackMode.ENABLE_LOCAL))
+
+        self.dut_hci.send_command(ReadLocalNameBuilder())
+        assertThat(self.dut_hci.get_event_stream()).emits(HciMatchers.LoopbackOf(ReadLocalNameBuilder()))
+
+    def test_inquiry_from_dut(self):
+        self.dut_hci.register_for_events(EventCode.INQUIRY_RESULT)
+
+        self.cert_hal.enable_inquiry_and_page_scan()
+        lap = Lap()
+        lap.lap = 0x33
+        self.dut_hci.send_command(InquiryBuilder(lap, 0x30, 0xff))
+        assertThat(self.dut_hci.get_event_stream()).emits(HciMatchers.EventWithCode(EventCode.INQUIRY_RESULT))
+
+    def test_le_ad_scan_cert_advertises(self):
+        self.dut_hci.register_for_le_events(SubeventCode.EXTENDED_ADVERTISING_REPORT, SubeventCode.ADVERTISING_REPORT)
+
+        # DUT Scans
+        self.dut_hci.send_command(LeSetRandomAddressBuilder('0D:05:04:03:02:01'))
+        phy_scan_params = PhyScanParameters()
+        phy_scan_params.le_scan_interval = 6553
+        phy_scan_params.le_scan_window = 6553
+        phy_scan_params.le_scan_type = LeScanType.ACTIVE
+
+        self.dut_hci.send_command(
+            LeSetExtendedScanParametersBuilder(OwnAddressType.RANDOM_DEVICE_ADDRESS, LeScanningFilterPolicy.ACCEPT_ALL,
+                                               1, [phy_scan_params]))
+        self.dut_hci.send_command(LeSetExtendedScanEnableBuilder(Enable.ENABLED, FilterDuplicates.DISABLED, 0, 0))
+
+        # CERT Advertises
+        advertising_handle = 0
+        self.cert_hal.send_hci_command(
+            LeSetExtendedAdvertisingLegacyParametersBuilder(
+                advertising_handle,
+                LegacyAdvertisingProperties.ADV_IND,
+                512,
+                768,
+                7,
+                OwnAddressType.RANDOM_DEVICE_ADDRESS,
+                PeerAddressType.PUBLIC_DEVICE_OR_IDENTITY_ADDRESS,
+                'A6:A5:A4:A3:A2:A1',
+                AdvertisingFilterPolicy.ALL_DEVICES,
+                0xF7,
+                1,  # SID
+                Enable.DISABLED  # Scan request notification
+            ))
+
+        self.cert_hal.send_hci_command(
+            LeSetExtendedAdvertisingRandomAddressBuilder(advertising_handle, '0C:05:04:03:02:01'))
+        gap_name = GapData()
+        gap_name.data_type = GapDataType.COMPLETE_LOCAL_NAME
+        gap_name.data = list(bytes(b'Im_A_Cert'))
+
+        self.cert_hal.send_hci_command(
+            LeSetExtendedAdvertisingDataBuilder(advertising_handle, Operation.COMPLETE_ADVERTISEMENT,
+                                                FragmentPreference.CONTROLLER_SHOULD_NOT, [gap_name]))
+
+        gap_short_name = GapData()
+        gap_short_name.data_type = GapDataType.SHORTENED_LOCAL_NAME
+        gap_short_name.data = list(bytes(b'Im_A_C'))
+
+        self.cert_hal.send_hci_command(
+            LeSetExtendedAdvertisingScanResponseBuilder(advertising_handle, Operation.COMPLETE_ADVERTISEMENT,
+                                                        FragmentPreference.CONTROLLER_SHOULD_NOT, [gap_short_name]))
+
+        enabled_set = EnabledSet()
+        enabled_set.advertising_handle = 0
+        enabled_set.duration = 0
+        enabled_set.max_extended_advertising_events = 0
+        self.cert_hal.send_hci_command(LeSetExtendedAdvertisingEnableBuilder(Enable.ENABLED, [enabled_set]))
+
+        assertThat(self.dut_hci.get_le_event_stream()).emits(lambda packet: b'Im_A_Cert' in packet.payload)
+
+        self.cert_hal.send_hci_command(LeSetExtendedAdvertisingEnableBuilder(Enable.DISABLED, [enabled_set]))
+        self.dut_hci.send_command(LeSetExtendedScanEnableBuilder(Enable.DISABLED, FilterDuplicates.DISABLED, 0, 0))
+
+    def _verify_le_connection_complete(self):
+        cert_conn_complete_capture = HalCaptures.LeConnectionCompleteCapture()
+        assertThat(self.cert_hal.get_hci_event_stream()).emits(cert_conn_complete_capture)
+        cert_handle = cert_conn_complete_capture.get().GetConnectionHandle()
+
+        dut_conn_complete_capture = HciCaptures.LeConnectionCompleteCapture()
+        assertThat(self.dut_hci.get_le_event_stream()).emits(dut_conn_complete_capture)
+        dut_handle = dut_conn_complete_capture.get().GetConnectionHandle()
+
+        return (dut_handle, cert_handle)
+
+    @staticmethod
+    def _create_phy_scan_params():
+        phy_scan_params = LeCreateConnPhyScanParameters()
+        phy_scan_params.scan_interval = 0x60
+        phy_scan_params.scan_window = 0x30
+        phy_scan_params.conn_interval_min = 0x18
+        phy_scan_params.conn_interval_max = 0x28
+        phy_scan_params.conn_latency = 0
+        phy_scan_params.supervision_timeout = 0x1f4
+        phy_scan_params.min_ce_length = 0
+        phy_scan_params.max_ce_length = 0
+        return phy_scan_params
+
+    def test_le_connection_dut_advertises(self):
+        self.dut_hci.register_for_le_events(SubeventCode.CONNECTION_COMPLETE, SubeventCode.ADVERTISING_SET_TERMINATED,
+                                            SubeventCode.ENHANCED_CONNECTION_COMPLETE,
+                                            SubeventCode.READ_REMOTE_FEATURES_COMPLETE)
+        # Cert Connects
+        self.cert_hal.send_hci_command(LeSetRandomAddressBuilder('0C:05:04:03:02:01'))
+        phy_scan_params = DirectHciTestBase._create_phy_scan_params()
+        self.cert_hal.send_hci_command(
+            LeExtendedCreateConnectionBuilder(InitiatorFilterPolicy.USE_PEER_ADDRESS,
+                                              OwnAddressType.RANDOM_DEVICE_ADDRESS, AddressType.RANDOM_DEVICE_ADDRESS,
+                                              '0D:05:04:03:02:01', 1, [phy_scan_params]))
+
+        advertisement = self.dut_hci.create_advertisement(0, '0D:05:04:03:02:01')
+        advertisement.set_data(b'Im_The_DUT')
+        advertisement.set_scan_response(b'Im_The_D')
+        advertisement.start()
+
+        (dut_handle, cert_handle) = self._verify_le_connection_complete()
+
+        self.dut_hci.send_command(LeReadRemoteFeaturesBuilder(dut_handle))
+        assertThat(self.dut_hci.get_le_event_stream()).emits(
+            lambda packet: packet.payload[0] == int(EventCode.LE_META_EVENT) and packet.payload[2] == int(SubeventCode.READ_REMOTE_FEATURES_COMPLETE)
+        )
+
+        # Send ACL Data
+        self.enqueue_acl_data(dut_handle, PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE,
+                              BroadcastFlag.POINT_TO_POINT, bytes(b'Just SomeAclData'))
+        self.cert_hal.send_acl_first(cert_handle, bytes(b'Just SomeMoreAclData'))
+
+        assertThat(self.cert_hal.get_acl_stream()).emits(
+            lambda packet: logging.debug(packet.payload) or b'SomeAclData' in packet.payload)
+        assertThat(self.dut_hci.get_raw_acl_stream()).emits(
+            lambda packet: logging.debug(packet.payload) or b'SomeMoreAclData' in packet.payload)
+
+    def test_le_connect_list_connection_cert_advertises(self):
+        self.dut_hci.register_for_le_events(SubeventCode.CONNECTION_COMPLETE, SubeventCode.ENHANCED_CONNECTION_COMPLETE)
+        # DUT Connects
+        self.dut_hci.send_command(LeSetRandomAddressBuilder('0D:05:04:03:02:01'))
+        self.dut_hci.send_command(LeAddDeviceToConnectListBuilder(ConnectListAddressType.RANDOM, '0C:05:04:03:02:01'))
+        phy_scan_params = DirectHciTestBase._create_phy_scan_params()
+        self.dut_hci.send_command(
+            LeExtendedCreateConnectionBuilder(InitiatorFilterPolicy.USE_CONNECT_LIST,
+                                              OwnAddressType.RANDOM_DEVICE_ADDRESS, AddressType.RANDOM_DEVICE_ADDRESS,
+                                              'BA:D5:A4:A3:A2:A1', 1, [phy_scan_params]))
+
+        advertisement = self.cert_hal.create_advertisement(
+            1,
+            '0C:05:04:03:02:01',
+            min_interval=512,
+            max_interval=768,
+            peer_address='A6:A5:A4:A3:A2:A1',
+            tx_power=0x7f,
+            sid=0)
+        advertisement.set_data(b'Im_A_Cert')
+        advertisement.start()
+
+        # LeConnectionComplete
+        self._verify_le_connection_complete()
+
+    def test_connection_dut_connects(self):
+        self.dut_hci.send_command(WritePageTimeoutBuilder(0x4000))
+
+        self.cert_hal.enable_inquiry_and_page_scan()
+        address = self.cert_hal.read_own_address()
+
+        self.dut_hci.initiate_connection(address)
+        cert_acl = self.cert_hal.accept_connection()
+        dut_acl = self.dut_hci.complete_connection()
+
+        # Send ACL Data
+        dut_acl.send_first(b'Just SomeAclData')
+        cert_acl.send_first(b'Just SomeMoreAclData')
+
+        assertThat(self.cert_hal.get_acl_stream()).emits(lambda packet: b'SomeAclData' in packet.payload)
+        assertThat(self.dut_hci.get_raw_acl_stream()).emits(lambda packet: b'SomeMoreAclData' in packet.payload)
+
+    def test_connection_cert_connects(self):
+        self.cert_hal.send_hci_command(WritePageTimeoutBuilder(0x4000))
+
+        self.dut_hci.enable_inquiry_and_page_scan()
+        address = self.dut_hci.read_own_address()
+
+        self.cert_hal.initiate_connection(address)
+        dut_acl = self.dut_hci.accept_connection()
+        cert_acl = self.cert_hal.complete_connection()
+
+        # Send ACL Data
+        dut_acl.send_first(b'This is just SomeAclData')
+        cert_acl.send_first(b'This is just SomeMoreAclData')
+
+        assertThat(self.cert_hal.get_acl_stream()).emits(lambda packet: b'SomeAclData' in packet.payload)
+        assertThat(self.dut_hci.get_raw_acl_stream()).emits(lambda packet: b'SomeMoreAclData' in packet.payload)
diff --git a/gd/hci/cert/le_acl_manager_test.py b/gd/hci/cert/le_acl_manager_test.py
index aa3d75e..d710860 100644
--- a/gd/hci/cert/le_acl_manager_test.py
+++ b/gd/hci/cert/le_acl_manager_test.py
@@ -14,279 +14,19 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-from cert.closable import safeClose
 from cert.gd_base_test import GdBaseTestClass
-from cert.event_stream import EventStream
-from cert.truth import assertThat
-from cert.py_le_acl_manager import PyLeAclManager
-from google.protobuf import empty_pb2 as empty_proto
-from facade import common_pb2 as common
-from hci.facade import le_acl_manager_facade_pb2 as le_acl_manager_facade
-from hci.facade import le_advertising_manager_facade_pb2 as le_advertising_facade
-from hci.facade import le_initiator_address_facade_pb2 as le_initiator_address_facade
-from hci.facade import hci_facade_pb2 as hci_facade
-import bluetooth_packets_python3 as bt_packets
-from bluetooth_packets_python3 import hci_packets
-from bluetooth_packets_python3 import RawBuilder
+from hci.cert.le_acl_manager_test_lib import LeAclManagerTestBase
 
 
-class LeAclManagerTest(GdBaseTestClass):
+class LeAclManagerTest(GdBaseTestClass, LeAclManagerTestBase):
 
     def setup_class(self):
-        super().setup_class(dut_module='HCI_INTERFACES', cert_module='HCI')
+        GdBaseTestClass.setup_class(self, dut_module='HCI_INTERFACES', cert_module='HCI')
 
     def setup_test(self):
-        super().setup_test()
-        self.dut_le_acl_manager = PyLeAclManager(self.dut)
-        self.cert_hci_le_event_stream = EventStream(self.cert.hci.StreamLeSubevents(empty_proto.Empty()))
-        self.cert_acl_data_stream = EventStream(self.cert.hci.StreamAcl(empty_proto.Empty()))
+        GdBaseTestClass.setup_test(self)
+        LeAclManagerTestBase.setup_test(self, self.dut, self.cert)
 
     def teardown_test(self):
-        safeClose(self.cert_hci_le_event_stream)
-        safeClose(self.cert_acl_data_stream)
-        safeClose(self.dut_le_acl_manager)
-        super().teardown_test()
-
-    def set_privacy_policy_static(self):
-        self.dut_address = b'd0:05:04:03:02:01'
-        private_policy = le_initiator_address_facade.PrivacyPolicy(
-            address_policy=le_initiator_address_facade.AddressPolicy.USE_STATIC_ADDRESS,
-            address_with_type=common.BluetoothAddressWithType(
-                address=common.BluetoothAddress(address=bytes(self.dut_address)), type=common.RANDOM_DEVICE_ADDRESS))
-        self.dut.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(private_policy)
-
-    def register_for_event(self, event_code):
-        msg = hci_facade.EventRequest(code=int(event_code))
-        self.cert.hci.RequestEvent(msg)
-
-    def register_for_le_event(self, event_code):
-        msg = hci_facade.EventRequest(code=int(event_code))
-        self.cert.hci.RequestLeSubevent(msg)
-
-    def enqueue_hci_command(self, command):
-        cmd_bytes = bytes(command.Serialize())
-        cmd = common.Data(payload=cmd_bytes)
-        self.cert.hci.SendCommand(cmd)
-
-    def enqueue_acl_data(self, handle, pb_flag, b_flag, data):
-        acl = hci_packets.AclBuilder(handle, pb_flag, b_flag, RawBuilder(data))
-        self.cert.hci.SendAcl(common.Data(payload=bytes(acl.Serialize())))
-
-    def dut_connects(self, check_address):
-        self.register_for_le_event(hci_packets.SubeventCode.CONNECTION_COMPLETE)
-        self.register_for_le_event(hci_packets.SubeventCode.ENHANCED_CONNECTION_COMPLETE)
-
-        # Cert Advertises
-        advertising_handle = 0
-        self.enqueue_hci_command(
-            hci_packets.LeSetExtendedAdvertisingLegacyParametersBuilder(
-                advertising_handle,
-                hci_packets.LegacyAdvertisingProperties.ADV_IND,
-                400,
-                450,
-                7,
-                hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS,
-                hci_packets.PeerAddressType.PUBLIC_DEVICE_OR_IDENTITY_ADDRESS,
-                '00:00:00:00:00:00',
-                hci_packets.AdvertisingFilterPolicy.ALL_DEVICES,
-                0xF8,
-                1,  #SID
-                hci_packets.Enable.DISABLED  # Scan request notification
-            ))
-
-        self.enqueue_hci_command(
-            hci_packets.LeSetExtendedAdvertisingRandomAddressBuilder(advertising_handle, '0C:05:04:03:02:01'))
-
-        gap_name = hci_packets.GapData()
-        gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
-        gap_name.data = list(bytes(b'Im_A_Cert'))
-
-        self.enqueue_hci_command(
-            hci_packets.LeSetExtendedAdvertisingDataBuilder(
-                advertising_handle, hci_packets.Operation.COMPLETE_ADVERTISEMENT,
-                hci_packets.FragmentPreference.CONTROLLER_SHOULD_NOT, [gap_name]))
-
-        gap_short_name = hci_packets.GapData()
-        gap_short_name.data_type = hci_packets.GapDataType.SHORTENED_LOCAL_NAME
-        gap_short_name.data = list(bytes(b'Im_A_C'))
-
-        self.enqueue_hci_command(
-            hci_packets.LeSetExtendedAdvertisingScanResponseBuilder(
-                advertising_handle, hci_packets.Operation.COMPLETE_ADVERTISEMENT,
-                hci_packets.FragmentPreference.CONTROLLER_SHOULD_NOT, [gap_short_name]))
-
-        enabled_set = hci_packets.EnabledSet()
-        enabled_set.advertising_handle = advertising_handle
-        enabled_set.duration = 0
-        enabled_set.max_extended_advertising_events = 0
-        self.enqueue_hci_command(
-            hci_packets.LeSetExtendedAdvertisingEnableBuilder(hci_packets.Enable.ENABLED, [enabled_set]))
-
-        self.dut_le_acl = self.dut_le_acl_manager.connect_to_remote(
-            remote_addr=common.BluetoothAddressWithType(
-                address=common.BluetoothAddress(address=bytes('0C:05:04:03:02:01', 'utf8')),
-                type=int(hci_packets.AddressType.RANDOM_DEVICE_ADDRESS)))
-
-        # Cert gets ConnectionComplete with a handle and sends ACL data
-        handle = 0xfff
-        address = hci_packets.Address()
-
-        def get_handle(packet):
-            packet_bytes = packet.payload
-            nonlocal handle
-            nonlocal address
-            if b'\x3e\x13\x01\x00' in packet_bytes:
-                cc_view = hci_packets.LeConnectionCompleteView(
-                    hci_packets.LeMetaEventView(
-                        hci_packets.EventView(bt_packets.PacketViewLittleEndian(list(packet_bytes)))))
-                handle = cc_view.GetConnectionHandle()
-                address = cc_view.GetPeerAddress()
-                return True
-            if b'\x3e\x13\x0A\x00' in packet_bytes:
-                cc_view = hci_packets.LeEnhancedConnectionCompleteView(
-                    hci_packets.LeMetaEventView(
-                        hci_packets.EventView(bt_packets.PacketViewLittleEndian(list(packet_bytes)))))
-                handle = cc_view.GetConnectionHandle()
-                address = cc_view.GetPeerResolvablePrivateAddress()
-                return True
-            return False
-
-        self.cert_hci_le_event_stream.assert_event_occurs(get_handle)
-        self.cert_handle = handle
-        dut_address_from_complete = address
-        if check_address:
-            assertThat(dut_address_from_complete).isEqualTo(self.dut_address.decode())
-
-    def send_receive_and_check(self):
-        self.enqueue_acl_data(self.cert_handle, hci_packets.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE,
-                              hci_packets.BroadcastFlag.POINT_TO_POINT,
-                              bytes(b'\x19\x00\x07\x00SomeAclData from the Cert'))
-
-        self.dut_le_acl.send(b'\x1C\x00\x07\x00SomeMoreAclData from the DUT')
-        self.cert_acl_data_stream.assert_event_occurs(lambda packet: b'SomeMoreAclData' in packet.payload)
-        assertThat(self.dut_le_acl).emits(lambda packet: b'SomeAclData' in packet.payload)
-
-    def test_dut_connects(self):
-        self.set_privacy_policy_static()
-        self.dut_connects(check_address=True)
-        self.send_receive_and_check()
-
-    def test_dut_connects_resolvable_address(self):
-        privacy_policy = le_initiator_address_facade.PrivacyPolicy(
-            address_policy=le_initiator_address_facade.AddressPolicy.USE_RESOLVABLE_ADDRESS,
-            rotation_irk=b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f',
-            minimum_rotation_time=7 * 60 * 1000,
-            maximum_rotation_time=15 * 60 * 1000)
-        self.dut.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(privacy_policy)
-        self.dut_connects(check_address=False)
-        self.send_receive_and_check()
-
-    def test_dut_connects_non_resolvable_address(self):
-        privacy_policy = le_initiator_address_facade.PrivacyPolicy(
-            address_policy=le_initiator_address_facade.AddressPolicy.USE_NON_RESOLVABLE_ADDRESS,
-            rotation_irk=b'\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f',
-            minimum_rotation_time=8 * 60 * 1000,
-            maximum_rotation_time=14 * 60 * 1000)
-        self.dut.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(privacy_policy)
-        self.dut_connects(check_address=False)
-        self.send_receive_and_check()
-
-    def test_dut_connects_public_address(self):
-        self.dut.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(
-            le_initiator_address_facade.PrivacyPolicy(
-                address_policy=le_initiator_address_facade.AddressPolicy.USE_PUBLIC_ADDRESS))
-        self.dut_connects(check_address=False)
-        self.send_receive_and_check()
-
-    def test_dut_connects_public_address_cancelled(self):
-        self.dut.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(
-            le_initiator_address_facade.PrivacyPolicy(
-                address_policy=le_initiator_address_facade.AddressPolicy.USE_PUBLIC_ADDRESS))
-        self.dut_connects(check_address=False)
-        self.send_receive_and_check()
-
-    def test_cert_connects(self):
-        self.set_privacy_policy_static()
-        self.register_for_le_event(hci_packets.SubeventCode.CONNECTION_COMPLETE)
-
-        self.dut_le_acl_manager.listen_for_incoming_connections()
-
-        # DUT Advertises
-        gap_name = hci_packets.GapData()
-        gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
-        gap_name.data = list(bytes(b'Im_The_DUT'))
-        gap_data = le_advertising_facade.GapDataMsg(data=bytes(gap_name.Serialize()))
-        config = le_advertising_facade.AdvertisingConfig(
-            advertisement=[gap_data],
-            interval_min=512,
-            interval_max=768,
-            advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
-            own_address_type=common.USE_RANDOM_DEVICE_ADDRESS,
-            peer_address_type=common.PUBLIC_DEVICE_OR_IDENTITY_ADDRESS,
-            peer_address=common.BluetoothAddress(address=bytes(b'A6:A5:A4:A3:A2:A1')),
-            channel_map=7,
-            filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES)
-        request = le_advertising_facade.CreateAdvertiserRequest(config=config)
-
-        self.dut.hci_le_advertising_manager.CreateAdvertiser(request)
-
-        # Cert Connects
-        self.enqueue_hci_command(hci_packets.LeSetRandomAddressBuilder('0C:05:04:03:02:01'))
-        phy_scan_params = hci_packets.LeCreateConnPhyScanParameters()
-        phy_scan_params.scan_interval = 0x60
-        phy_scan_params.scan_window = 0x30
-        phy_scan_params.conn_interval_min = 0x18
-        phy_scan_params.conn_interval_max = 0x28
-        phy_scan_params.conn_latency = 0
-        phy_scan_params.supervision_timeout = 0x1f4
-        phy_scan_params.min_ce_length = 0
-        phy_scan_params.max_ce_length = 0
-        self.enqueue_hci_command(
-            hci_packets.LeExtendedCreateConnectionBuilder(hci_packets.InitiatorFilterPolicy.USE_PEER_ADDRESS,
-                                                          hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS,
-                                                          hci_packets.AddressType.RANDOM_DEVICE_ADDRESS,
-                                                          self.dut_address.decode(), 1, [phy_scan_params]))
-
-        # Cert gets ConnectionComplete with a handle and sends ACL data
-        handle = 0xfff
-
-        def get_handle(packet):
-            packet_bytes = packet.payload
-            nonlocal handle
-            if b'\x3e\x13\x01\x00' in packet_bytes:
-                cc_view = hci_packets.LeConnectionCompleteView(
-                    hci_packets.LeMetaEventView(
-                        hci_packets.EventView(bt_packets.PacketViewLittleEndian(list(packet_bytes)))))
-                handle = cc_view.GetConnectionHandle()
-                return True
-            if b'\x3e\x13\x0A\x00' in packet_bytes:
-                cc_view = hci_packets.LeEnhancedConnectionCompleteView(
-                    hci_packets.LeMetaEventView(
-                        hci_packets.EventView(bt_packets.PacketViewLittleEndian(list(packet_bytes)))))
-                handle = cc_view.GetConnectionHandle()
-                return True
-            return False
-
-        self.cert_hci_le_event_stream.assert_event_occurs(get_handle)
-        self.cert_handle = handle
-
-        self.enqueue_acl_data(self.cert_handle, hci_packets.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE,
-                              hci_packets.BroadcastFlag.POINT_TO_POINT,
-                              bytes(b'\x19\x00\x07\x00SomeAclData from the Cert'))
-
-        # DUT gets a connection complete event and sends and receives
-        handle = 0xfff
-        self.dut_le_acl = self.dut_le_acl_manager.complete_incoming_connection()
-
-        self.send_receive_and_check()
-
-    def test_recombination_l2cap_packet(self):
-        self.set_privacy_policy_static()
-        self.dut_connects(check_address=True)
-
-        self.enqueue_acl_data(self.cert_handle, hci_packets.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE,
-                              hci_packets.BroadcastFlag.POINT_TO_POINT, bytes(b'\x06\x00\x07\x00Hello'))
-        self.enqueue_acl_data(self.cert_handle, hci_packets.PacketBoundaryFlag.CONTINUING_FRAGMENT,
-                              hci_packets.BroadcastFlag.POINT_TO_POINT, bytes(b'!'))
-
-        assertThat(self.dut_le_acl).emits(lambda packet: b'Hello!' in packet.payload)
+        LeAclManagerTestBase.teardown_test(self)
+        GdBaseTestClass.teardown_test(self)
diff --git a/gd/hci/cert/le_acl_manager_test_lib.py b/gd/hci/cert/le_acl_manager_test_lib.py
new file mode 100644
index 0000000..39f148b
--- /dev/null
+++ b/gd/hci/cert/le_acl_manager_test_lib.py
@@ -0,0 +1,286 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2020 - 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.
+
+from cert.closable import safeClose
+from cert.event_stream import EventStream
+from cert.truth import assertThat
+from cert.py_le_acl_manager import PyLeAclManager
+from google.protobuf import empty_pb2 as empty_proto
+from facade import common_pb2 as common
+from hci.facade import le_acl_manager_facade_pb2 as le_acl_manager_facade
+from hci.facade import le_advertising_manager_facade_pb2 as le_advertising_facade
+from hci.facade import le_initiator_address_facade_pb2 as le_initiator_address_facade
+from hci.facade import hci_facade_pb2 as hci_facade
+import bluetooth_packets_python3 as bt_packets
+from bluetooth_packets_python3 import hci_packets
+from bluetooth_packets_python3 import RawBuilder
+
+
+class LeAclManagerTestBase():
+
+    def setup_test(self, dut, cert):
+        self.dut_le_acl_manager = PyLeAclManager(dut)
+        self.cert_hci_le_event_stream = EventStream(cert.hci.StreamLeSubevents(empty_proto.Empty()))
+        self.cert_acl_data_stream = EventStream(cert.hci.StreamAcl(empty_proto.Empty()))
+
+    def teardown_test(self):
+        safeClose(self.cert_hci_le_event_stream)
+        safeClose(self.cert_acl_data_stream)
+        safeClose(self.dut_le_acl_manager)
+
+    def set_privacy_policy_static(self):
+        self.dut_address = b'd0:05:04:03:02:01'
+        private_policy = le_initiator_address_facade.PrivacyPolicy(
+            address_policy=le_initiator_address_facade.AddressPolicy.USE_STATIC_ADDRESS,
+            address_with_type=common.BluetoothAddressWithType(
+                address=common.BluetoothAddress(address=bytes(self.dut_address)), type=common.RANDOM_DEVICE_ADDRESS))
+        self.dut.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(private_policy)
+
+    def register_for_event(self, event_code):
+        msg = hci_facade.EventRequest(code=int(event_code))
+        self.cert.hci.RequestEvent(msg)
+
+    def register_for_le_event(self, event_code):
+        msg = hci_facade.EventRequest(code=int(event_code))
+        self.cert.hci.RequestLeSubevent(msg)
+
+    def enqueue_hci_command(self, command):
+        cmd_bytes = bytes(command.Serialize())
+        cmd = common.Data(payload=cmd_bytes)
+        self.cert.hci.SendCommand(cmd)
+
+    def enqueue_acl_data(self, handle, pb_flag, b_flag, data):
+        acl = hci_packets.AclBuilder(handle, pb_flag, b_flag, RawBuilder(data))
+        self.cert.hci.SendAcl(common.Data(payload=bytes(acl.Serialize())))
+
+    def dut_connects(self, check_address):
+        self.register_for_le_event(hci_packets.SubeventCode.CONNECTION_COMPLETE)
+        self.register_for_le_event(hci_packets.SubeventCode.ENHANCED_CONNECTION_COMPLETE)
+
+        # Cert Advertises
+        advertising_handle = 0
+        self.enqueue_hci_command(
+            hci_packets.LeSetExtendedAdvertisingLegacyParametersBuilder(
+                advertising_handle,
+                hci_packets.LegacyAdvertisingProperties.ADV_IND,
+                400,
+                450,
+                7,
+                hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS,
+                hci_packets.PeerAddressType.PUBLIC_DEVICE_OR_IDENTITY_ADDRESS,
+                '00:00:00:00:00:00',
+                hci_packets.AdvertisingFilterPolicy.ALL_DEVICES,
+                0xF8,
+                1,  #SID
+                hci_packets.Enable.DISABLED  # Scan request notification
+            ))
+
+        self.enqueue_hci_command(
+            hci_packets.LeSetExtendedAdvertisingRandomAddressBuilder(advertising_handle, '0C:05:04:03:02:01'))
+
+        gap_name = hci_packets.GapData()
+        gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+        gap_name.data = list(bytes(b'Im_A_Cert'))
+
+        self.enqueue_hci_command(
+            hci_packets.LeSetExtendedAdvertisingDataBuilder(
+                advertising_handle, hci_packets.Operation.COMPLETE_ADVERTISEMENT,
+                hci_packets.FragmentPreference.CONTROLLER_SHOULD_NOT, [gap_name]))
+
+        gap_short_name = hci_packets.GapData()
+        gap_short_name.data_type = hci_packets.GapDataType.SHORTENED_LOCAL_NAME
+        gap_short_name.data = list(bytes(b'Im_A_C'))
+
+        self.enqueue_hci_command(
+            hci_packets.LeSetExtendedAdvertisingScanResponseBuilder(
+                advertising_handle, hci_packets.Operation.COMPLETE_ADVERTISEMENT,
+                hci_packets.FragmentPreference.CONTROLLER_SHOULD_NOT, [gap_short_name]))
+
+        enabled_set = hci_packets.EnabledSet()
+        enabled_set.advertising_handle = advertising_handle
+        enabled_set.duration = 0
+        enabled_set.max_extended_advertising_events = 0
+        self.enqueue_hci_command(
+            hci_packets.LeSetExtendedAdvertisingEnableBuilder(hci_packets.Enable.ENABLED, [enabled_set]))
+
+        self.dut_le_acl = self.dut_le_acl_manager.connect_to_remote(
+            remote_addr=common.BluetoothAddressWithType(
+                address=common.BluetoothAddress(address=bytes('0C:05:04:03:02:01', 'utf8')),
+                type=int(hci_packets.AddressType.RANDOM_DEVICE_ADDRESS)))
+
+        # Cert gets ConnectionComplete with a handle and sends ACL data
+        handle = 0xfff
+        address = hci_packets.Address()
+
+        def get_handle(packet):
+            packet_bytes = packet.payload
+            nonlocal handle
+            nonlocal address
+            if b'\x3e\x13\x01\x00' in packet_bytes:
+                cc_view = hci_packets.LeConnectionCompleteView(
+                    hci_packets.LeMetaEventView(
+                        hci_packets.EventView(bt_packets.PacketViewLittleEndian(list(packet_bytes)))))
+                handle = cc_view.GetConnectionHandle()
+                address = cc_view.GetPeerAddress()
+                return True
+            if b'\x3e\x13\x0A\x00' in packet_bytes:
+                cc_view = hci_packets.LeEnhancedConnectionCompleteView(
+                    hci_packets.LeMetaEventView(
+                        hci_packets.EventView(bt_packets.PacketViewLittleEndian(list(packet_bytes)))))
+                handle = cc_view.GetConnectionHandle()
+                address = cc_view.GetPeerResolvablePrivateAddress()
+                return True
+            return False
+
+        self.cert_hci_le_event_stream.assert_event_occurs(get_handle)
+        self.cert_handle = handle
+        dut_address_from_complete = address
+        if check_address:
+            assertThat(dut_address_from_complete).isEqualTo(self.dut_address.decode())
+
+    def send_receive_and_check(self):
+        self.enqueue_acl_data(self.cert_handle, hci_packets.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE,
+                              hci_packets.BroadcastFlag.POINT_TO_POINT,
+                              bytes(b'\x19\x00\x07\x00SomeAclData from the Cert'))
+
+        self.dut_le_acl.send(b'\x1C\x00\x07\x00SomeMoreAclData from the DUT')
+        self.cert_acl_data_stream.assert_event_occurs(lambda packet: b'SomeMoreAclData' in packet.payload)
+        assertThat(self.dut_le_acl).emits(lambda packet: b'SomeAclData' in packet.payload)
+
+    def test_dut_connects(self):
+        self.set_privacy_policy_static()
+        self.dut_connects(check_address=True)
+        self.send_receive_and_check()
+
+    def test_dut_connects_resolvable_address(self):
+        privacy_policy = le_initiator_address_facade.PrivacyPolicy(
+            address_policy=le_initiator_address_facade.AddressPolicy.USE_RESOLVABLE_ADDRESS,
+            rotation_irk=b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f',
+            minimum_rotation_time=7 * 60 * 1000,
+            maximum_rotation_time=15 * 60 * 1000)
+        self.dut.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(privacy_policy)
+        self.dut_connects(check_address=False)
+        self.send_receive_and_check()
+
+    def test_dut_connects_non_resolvable_address(self):
+        privacy_policy = le_initiator_address_facade.PrivacyPolicy(
+            address_policy=le_initiator_address_facade.AddressPolicy.USE_NON_RESOLVABLE_ADDRESS,
+            rotation_irk=b'\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f',
+            minimum_rotation_time=8 * 60 * 1000,
+            maximum_rotation_time=14 * 60 * 1000)
+        self.dut.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(privacy_policy)
+        self.dut_connects(check_address=False)
+        self.send_receive_and_check()
+
+    def test_dut_connects_public_address(self):
+        self.dut.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(
+            le_initiator_address_facade.PrivacyPolicy(
+                address_policy=le_initiator_address_facade.AddressPolicy.USE_PUBLIC_ADDRESS))
+        self.dut_connects(check_address=False)
+        self.send_receive_and_check()
+
+    def test_dut_connects_public_address_cancelled(self):
+        self.dut.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(
+            le_initiator_address_facade.PrivacyPolicy(
+                address_policy=le_initiator_address_facade.AddressPolicy.USE_PUBLIC_ADDRESS))
+        self.dut_connects(check_address=False)
+        self.send_receive_and_check()
+
+    def test_cert_connects(self):
+        self.set_privacy_policy_static()
+        self.register_for_le_event(hci_packets.SubeventCode.CONNECTION_COMPLETE)
+
+        self.dut_le_acl_manager.listen_for_incoming_connections()
+
+        # DUT Advertises
+        gap_name = hci_packets.GapData()
+        gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+        gap_name.data = list(bytes(b'Im_The_DUT'))
+        gap_data = le_advertising_facade.GapDataMsg(data=bytes(gap_name.Serialize()))
+        config = le_advertising_facade.AdvertisingConfig(
+            advertisement=[gap_data],
+            interval_min=512,
+            interval_max=768,
+            advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
+            own_address_type=common.USE_RANDOM_DEVICE_ADDRESS,
+            peer_address_type=common.PUBLIC_DEVICE_OR_IDENTITY_ADDRESS,
+            peer_address=common.BluetoothAddress(address=bytes(b'A6:A5:A4:A3:A2:A1')),
+            channel_map=7,
+            filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES)
+        request = le_advertising_facade.CreateAdvertiserRequest(config=config)
+
+        self.dut.hci_le_advertising_manager.CreateAdvertiser(request)
+
+        # Cert Connects
+        self.enqueue_hci_command(hci_packets.LeSetRandomAddressBuilder('0C:05:04:03:02:01'))
+        phy_scan_params = hci_packets.LeCreateConnPhyScanParameters()
+        phy_scan_params.scan_interval = 0x60
+        phy_scan_params.scan_window = 0x30
+        phy_scan_params.conn_interval_min = 0x18
+        phy_scan_params.conn_interval_max = 0x28
+        phy_scan_params.conn_latency = 0
+        phy_scan_params.supervision_timeout = 0x1f4
+        phy_scan_params.min_ce_length = 0
+        phy_scan_params.max_ce_length = 0
+        self.enqueue_hci_command(
+            hci_packets.LeExtendedCreateConnectionBuilder(hci_packets.InitiatorFilterPolicy.USE_PEER_ADDRESS,
+                                                          hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS,
+                                                          hci_packets.AddressType.RANDOM_DEVICE_ADDRESS,
+                                                          self.dut_address.decode(), 1, [phy_scan_params]))
+
+        # Cert gets ConnectionComplete with a handle and sends ACL data
+        handle = 0xfff
+
+        def get_handle(packet):
+            packet_bytes = packet.payload
+            nonlocal handle
+            if b'\x3e\x13\x01\x00' in packet_bytes:
+                cc_view = hci_packets.LeConnectionCompleteView(
+                    hci_packets.LeMetaEventView(
+                        hci_packets.EventView(bt_packets.PacketViewLittleEndian(list(packet_bytes)))))
+                handle = cc_view.GetConnectionHandle()
+                return True
+            if b'\x3e\x13\x0A\x00' in packet_bytes:
+                cc_view = hci_packets.LeEnhancedConnectionCompleteView(
+                    hci_packets.LeMetaEventView(
+                        hci_packets.EventView(bt_packets.PacketViewLittleEndian(list(packet_bytes)))))
+                handle = cc_view.GetConnectionHandle()
+                return True
+            return False
+
+        self.cert_hci_le_event_stream.assert_event_occurs(get_handle)
+        self.cert_handle = handle
+
+        self.enqueue_acl_data(self.cert_handle, hci_packets.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE,
+                              hci_packets.BroadcastFlag.POINT_TO_POINT,
+                              bytes(b'\x19\x00\x07\x00SomeAclData from the Cert'))
+
+        # DUT gets a connection complete event and sends and receives
+        handle = 0xfff
+        self.dut_le_acl = self.dut_le_acl_manager.complete_incoming_connection()
+
+        self.send_receive_and_check()
+
+    def test_recombination_l2cap_packet(self):
+        self.set_privacy_policy_static()
+        self.dut_connects(check_address=True)
+
+        self.enqueue_acl_data(self.cert_handle, hci_packets.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE,
+                              hci_packets.BroadcastFlag.POINT_TO_POINT, bytes(b'\x06\x00\x07\x00Hello'))
+        self.enqueue_acl_data(self.cert_handle, hci_packets.PacketBoundaryFlag.CONTINUING_FRAGMENT,
+                              hci_packets.BroadcastFlag.POINT_TO_POINT, bytes(b'!'))
+
+        assertThat(self.dut_le_acl).emits(lambda packet: b'Hello!' in packet.payload)
diff --git a/gd/hci/cert/le_advertising_manager_test.py b/gd/hci/cert/le_advertising_manager_test.py
index aef3710..40a56ad 100644
--- a/gd/hci/cert/le_advertising_manager_test.py
+++ b/gd/hci/cert/le_advertising_manager_test.py
@@ -14,74 +14,19 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import os
-import sys
-import logging
-
 from cert.gd_base_test import GdBaseTestClass
-from cert.event_stream import EventStream
-from google.protobuf import empty_pb2 as empty_proto
-from facade import rootservice_pb2 as facade_rootservice
-from hci.facade import hci_facade_pb2 as hci_facade
-from hci.facade import \
-  le_advertising_manager_facade_pb2 as le_advertising_facade
-from bluetooth_packets_python3 import hci_packets
-from facade import common_pb2 as common
-from cert.py_hci import PyHci
-from cert.truth import assertThat
+from hci.cert.le_advertising_manager_test_lib import LeAdvertisingManagerTestBase
 
 
-class LeAdvertisingManagerTest(GdBaseTestClass):
+class LeAdvertisingManagerTest(GdBaseTestClass, LeAdvertisingManagerTestBase):
 
     def setup_class(self):
-        super().setup_class(dut_module='HCI_INTERFACES', cert_module='HCI')
+        GdBaseTestClass.setup_class(self, dut_module='HCI_INTERFACES', cert_module='HCI')
 
     def setup_test(self):
-        super().setup_test()
-        self.cert_hci = PyHci(self.cert, acl_streaming=True)
+        GdBaseTestClass.setup_test(self)
+        LeAdvertisingManagerTestBase.setup_test(self, self.cert)
 
     def teardown_test(self):
-        self.cert_hci.close()
-        super().teardown_test()
-
-    def test_le_ad_scan_dut_advertises(self):
-        self.cert_hci.register_for_le_events(hci_packets.SubeventCode.ADVERTISING_REPORT,
-                                             hci_packets.SubeventCode.EXTENDED_ADVERTISING_REPORT)
-
-        # CERT Scans
-        self.cert_hci.send_command(hci_packets.LeSetRandomAddressBuilder('0C:05:04:03:02:01'))
-        scan_parameters = hci_packets.PhyScanParameters()
-        scan_parameters.le_scan_type = hci_packets.LeScanType.ACTIVE
-        scan_parameters.le_scan_interval = 40
-        scan_parameters.le_scan_window = 20
-        self.cert_hci.send_command(
-            hci_packets.LeSetExtendedScanParametersBuilder(hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS,
-                                                           hci_packets.LeScanningFilterPolicy.ACCEPT_ALL, 1,
-                                                           [scan_parameters]))
-        self.cert_hci.send_command(
-            hci_packets.LeSetExtendedScanEnableBuilder(hci_packets.Enable.ENABLED,
-                                                       hci_packets.FilterDuplicates.DISABLED, 0, 0))
-
-        # DUT Advertises
-        gap_name = hci_packets.GapData()
-        gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
-        gap_name.data = list(bytes(b'Im_The_DUT'))
-        gap_data = le_advertising_facade.GapDataMsg(data=bytes(gap_name.Serialize()))
-        config = le_advertising_facade.AdvertisingConfig(
-            advertisement=[gap_data],
-            interval_min=512,
-            interval_max=768,
-            advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
-            own_address_type=common.USE_RANDOM_DEVICE_ADDRESS,
-            channel_map=7,
-            filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES)
-        request = le_advertising_facade.CreateAdvertiserRequest(config=config)
-
-        create_response = self.dut.hci_le_advertising_manager.CreateAdvertiser(request)
-
-        assertThat(self.cert_hci.get_le_event_stream()).emits(lambda packet: b'Im_The_DUT' in packet.payload)
-
-        remove_request = le_advertising_facade.RemoveAdvertiserRequest(advertiser_id=create_response.advertiser_id)
-        self.dut.hci_le_advertising_manager.RemoveAdvertiser(remove_request)
-        self.cert_hci.send_command(
-            hci_packets.LeSetScanEnableBuilder(hci_packets.Enable.DISABLED, hci_packets.Enable.DISABLED))
+        LeAdvertisingManagerTestBase.teardown_test(self)
+        GdBaseTestClass.teardown_test(self)
diff --git a/gd/hci/cert/le_advertising_manager_test_lib.py b/gd/hci/cert/le_advertising_manager_test_lib.py
new file mode 100644
index 0000000..eee1c9c
--- /dev/null
+++ b/gd/hci/cert/le_advertising_manager_test_lib.py
@@ -0,0 +1,81 @@
+#!/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.
+
+import os
+import sys
+import logging
+
+from cert.event_stream import EventStream
+from google.protobuf import empty_pb2 as empty_proto
+from facade import rootservice_pb2 as facade_rootservice
+from hci.facade import hci_facade_pb2 as hci_facade
+from hci.facade import \
+  le_advertising_manager_facade_pb2 as le_advertising_facade
+from bluetooth_packets_python3 import hci_packets
+from facade import common_pb2 as common
+from cert.py_hci import PyHci
+from cert.truth import assertThat
+
+
+class LeAdvertisingManagerTestBase():
+
+    def setup_test(self, cert):
+        self.cert_hci = PyHci(cert, acl_streaming=True)
+
+    def teardown_test(self):
+        self.cert_hci.close()
+
+    def test_le_ad_scan_dut_advertises(self):
+        self.cert_hci.register_for_le_events(hci_packets.SubeventCode.ADVERTISING_REPORT,
+                                             hci_packets.SubeventCode.EXTENDED_ADVERTISING_REPORT)
+
+        # CERT Scans
+        self.cert_hci.send_command(hci_packets.LeSetRandomAddressBuilder('0C:05:04:03:02:01'))
+        scan_parameters = hci_packets.PhyScanParameters()
+        scan_parameters.le_scan_type = hci_packets.LeScanType.ACTIVE
+        scan_parameters.le_scan_interval = 40
+        scan_parameters.le_scan_window = 20
+        self.cert_hci.send_command(
+            hci_packets.LeSetExtendedScanParametersBuilder(hci_packets.OwnAddressType.RANDOM_DEVICE_ADDRESS,
+                                                           hci_packets.LeScanningFilterPolicy.ACCEPT_ALL, 1,
+                                                           [scan_parameters]))
+        self.cert_hci.send_command(
+            hci_packets.LeSetExtendedScanEnableBuilder(hci_packets.Enable.ENABLED,
+                                                       hci_packets.FilterDuplicates.DISABLED, 0, 0))
+
+        # DUT Advertises
+        gap_name = hci_packets.GapData()
+        gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+        gap_name.data = list(bytes(b'Im_The_DUT'))
+        gap_data = le_advertising_facade.GapDataMsg(data=bytes(gap_name.Serialize()))
+        config = le_advertising_facade.AdvertisingConfig(
+            advertisement=[gap_data],
+            interval_min=512,
+            interval_max=768,
+            advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
+            own_address_type=common.USE_RANDOM_DEVICE_ADDRESS,
+            channel_map=7,
+            filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES)
+        request = le_advertising_facade.CreateAdvertiserRequest(config=config)
+
+        create_response = self.dut.hci_le_advertising_manager.CreateAdvertiser(request)
+
+        assertThat(self.cert_hci.get_le_event_stream()).emits(lambda packet: b'Im_The_DUT' in packet.payload)
+
+        remove_request = le_advertising_facade.RemoveAdvertiserRequest(advertiser_id=create_response.advertiser_id)
+        self.dut.hci_le_advertising_manager.RemoveAdvertiser(remove_request)
+        self.cert_hci.send_command(
+            hci_packets.LeSetScanEnableBuilder(hci_packets.Enable.DISABLED, hci_packets.Enable.DISABLED))
diff --git a/gd/hci/cert/le_scanning_manager_test.py b/gd/hci/cert/le_scanning_manager_test.py
index b91118e..d9c7e64 100644
--- a/gd/hci/cert/le_scanning_manager_test.py
+++ b/gd/hci/cert/le_scanning_manager_test.py
@@ -14,84 +14,11 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import os
-import sys
-import logging
-
 from cert.gd_base_test import GdBaseTestClass
-from cert.event_stream import EventStream
-from google.protobuf import empty_pb2 as empty_proto
-from facade import rootservice_pb2 as facade_rootservice
-from hci.facade import hci_facade_pb2 as hci_facade
-from hci.facade import le_scanning_manager_facade_pb2 as le_scanning_facade
-from hci.facade import le_advertising_manager_facade_pb2 as le_advertising_facade
-from hci.facade import le_initiator_address_facade_pb2 as le_initiator_address_facade
-from bluetooth_packets_python3 import hci_packets
-from facade import common_pb2 as common
+from hci.cert.le_scanning_manager_test_lib import LeScanningManagerTestBase
 
 
-class LeScanningManagerTest(GdBaseTestClass):
+class LeScanningManagerTest(GdBaseTestClass, LeScanningManagerTestBase):
 
     def setup_class(self):
-        super().setup_class(dut_module='HCI_INTERFACES', cert_module='HCI_INTERFACES')
-
-    def register_for_event(self, event_code):
-        msg = hci_facade.EventCodeMsg(code=int(event_code))
-        self.cert.hci.RegisterEventHandler(msg)
-
-    def register_for_le_event(self, event_code):
-        msg = hci_facade.LeSubeventCodeMsg(code=int(event_code))
-        self.cert.hci.RegisterLeEventHandler(msg)
-
-    def enqueue_hci_command(self, command, expect_complete):
-        cmd_bytes = bytes(command.Serialize())
-        cmd = common.Data(payload=cmd_bytes)
-        if (expect_complete):
-            self.cert.hci.EnqueueCommandWithComplete(cmd)
-        else:
-            self.cert.hci.EnqueueCommandWithStatus(cmd)
-
-    def test_le_ad_scan_dut_scans(self):
-        privacy_policy = le_initiator_address_facade.PrivacyPolicy(
-            address_policy=le_initiator_address_facade.AddressPolicy.USE_STATIC_ADDRESS,
-            address_with_type=common.BluetoothAddressWithType(
-                address=common.BluetoothAddress(address=bytes(b'D0:05:04:03:02:01')),
-                type=common.RANDOM_DEVICE_ADDRESS),
-            rotation_irk=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
-            minimum_rotation_time=0,
-            maximum_rotation_time=0)
-        self.dut.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(privacy_policy)
-        cert_privacy_policy = le_initiator_address_facade.PrivacyPolicy(
-            address_policy=le_initiator_address_facade.AddressPolicy.USE_STATIC_ADDRESS,
-            address_with_type=common.BluetoothAddressWithType(
-                address=common.BluetoothAddress(address=bytes(b'C0:05:04:03:02:01')),
-                type=common.RANDOM_DEVICE_ADDRESS),
-            rotation_irk=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
-            minimum_rotation_time=0,
-            maximum_rotation_time=0)
-        self.cert.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(cert_privacy_policy)
-        with EventStream(
-                # DUT Scans
-                self.dut.hci_le_scanning_manager.StartScan(empty_proto.Empty())) as advertising_event_stream:
-
-            # CERT Advertises
-            gap_name = hci_packets.GapData()
-            gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
-            gap_name.data = list(bytes(b'Im_The_CERT!'))
-            gap_data = le_advertising_facade.GapDataMsg(data=bytes(gap_name.Serialize()))
-            config = le_advertising_facade.AdvertisingConfig(
-                advertisement=[gap_data],
-                interval_min=512,
-                interval_max=768,
-                advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
-                own_address_type=common.USE_RANDOM_DEVICE_ADDRESS,
-                channel_map=7,
-                filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES)
-            request = le_advertising_facade.CreateAdvertiserRequest(config=config)
-
-            create_response = self.cert.hci_le_advertising_manager.CreateAdvertiser(request)
-
-            advertising_event_stream.assert_event_occurs(lambda packet: b'Im_The_CERT' in packet.event)
-
-            remove_request = le_advertising_facade.RemoveAdvertiserRequest(advertiser_id=create_response.advertiser_id)
-            self.cert.hci_le_advertising_manager.RemoveAdvertiser(remove_request)
+        GdBaseTestClass.setup_class(self, dut_module='HCI_INTERFACES', cert_module='HCI_INTERFACES')
diff --git a/gd/hci/cert/le_scanning_manager_test_lib.py b/gd/hci/cert/le_scanning_manager_test_lib.py
new file mode 100644
index 0000000..642e41c
--- /dev/null
+++ b/gd/hci/cert/le_scanning_manager_test_lib.py
@@ -0,0 +1,93 @@
+#!/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.
+
+import os
+import sys
+import logging
+
+from cert.event_stream import EventStream
+from google.protobuf import empty_pb2 as empty_proto
+from facade import rootservice_pb2 as facade_rootservice
+from hci.facade import hci_facade_pb2 as hci_facade
+from hci.facade import le_scanning_manager_facade_pb2 as le_scanning_facade
+from hci.facade import le_advertising_manager_facade_pb2 as le_advertising_facade
+from hci.facade import le_initiator_address_facade_pb2 as le_initiator_address_facade
+from bluetooth_packets_python3 import hci_packets
+from facade import common_pb2 as common
+
+
+class LeScanningManagerTestBase():
+
+    def register_for_event(self, event_code):
+        msg = hci_facade.EventCodeMsg(code=int(event_code))
+        self.cert.hci.RegisterEventHandler(msg)
+
+    def register_for_le_event(self, event_code):
+        msg = hci_facade.LeSubeventCodeMsg(code=int(event_code))
+        self.cert.hci.RegisterLeEventHandler(msg)
+
+    def enqueue_hci_command(self, command, expect_complete):
+        cmd_bytes = bytes(command.Serialize())
+        cmd = common.Data(payload=cmd_bytes)
+        if (expect_complete):
+            self.cert.hci.EnqueueCommandWithComplete(cmd)
+        else:
+            self.cert.hci.EnqueueCommandWithStatus(cmd)
+
+    def test_le_ad_scan_dut_scans(self):
+        privacy_policy = le_initiator_address_facade.PrivacyPolicy(
+            address_policy=le_initiator_address_facade.AddressPolicy.USE_STATIC_ADDRESS,
+            address_with_type=common.BluetoothAddressWithType(
+                address=common.BluetoothAddress(address=bytes(b'D0:05:04:03:02:01')),
+                type=common.RANDOM_DEVICE_ADDRESS),
+            rotation_irk=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
+            minimum_rotation_time=0,
+            maximum_rotation_time=0)
+        self.dut.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(privacy_policy)
+        cert_privacy_policy = le_initiator_address_facade.PrivacyPolicy(
+            address_policy=le_initiator_address_facade.AddressPolicy.USE_STATIC_ADDRESS,
+            address_with_type=common.BluetoothAddressWithType(
+                address=common.BluetoothAddress(address=bytes(b'C0:05:04:03:02:01')),
+                type=common.RANDOM_DEVICE_ADDRESS),
+            rotation_irk=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
+            minimum_rotation_time=0,
+            maximum_rotation_time=0)
+        self.cert.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(cert_privacy_policy)
+        with EventStream(
+                # DUT Scans
+                self.dut.hci_le_scanning_manager.StartScan(empty_proto.Empty())) as advertising_event_stream:
+
+            # CERT Advertises
+            gap_name = hci_packets.GapData()
+            gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+            gap_name.data = list(bytes(b'Im_The_CERT!'))
+            gap_data = le_advertising_facade.GapDataMsg(data=bytes(gap_name.Serialize()))
+            config = le_advertising_facade.AdvertisingConfig(
+                advertisement=[gap_data],
+                interval_min=512,
+                interval_max=768,
+                advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
+                own_address_type=common.USE_RANDOM_DEVICE_ADDRESS,
+                channel_map=7,
+                filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES)
+            request = le_advertising_facade.CreateAdvertiserRequest(config=config)
+
+            create_response = self.cert.hci_le_advertising_manager.CreateAdvertiser(request)
+
+            advertising_event_stream.assert_event_occurs(lambda packet: b'Im_The_CERT' in packet.event)
+
+            remove_request = le_advertising_facade.RemoveAdvertiserRequest(advertiser_id=create_response.advertiser_id)
+            self.cert.hci_le_advertising_manager.RemoveAdvertiser(remove_request)
diff --git a/gd/hci/cert/le_scanning_with_security_test.py b/gd/hci/cert/le_scanning_with_security_test.py
index e481357..c5e1cf2 100644
--- a/gd/hci/cert/le_scanning_with_security_test.py
+++ b/gd/hci/cert/le_scanning_with_security_test.py
@@ -15,72 +15,10 @@
 #   limitations under the License.
 
 from cert.gd_base_test import GdBaseTestClass
-from cert.event_stream import EventStream
-from cert.truth import assertThat
-from google.protobuf import empty_pb2 as empty_proto
-from hci.facade import hci_facade_pb2 as hci_facade
-from hci.facade import le_advertising_manager_facade_pb2 as le_advertising_facade
-from hci.facade import le_initiator_address_facade_pb2 as le_initiator_address_facade
-from bluetooth_packets_python3 import hci_packets
-from facade import common_pb2 as common
+from hci.cert.le_scanning_with_security_test_lib import LeScanningWithSecurityTestBase
 
 
-class LeScanningWithSecurityTest(GdBaseTestClass):
+class LeScanningWithSecurityTest(GdBaseTestClass, LeScanningWithSecurityTestBase):
 
     def setup_class(self):
-        super().setup_class(dut_module='SECURITY', cert_module='HCI_INTERFACES')
-
-    def register_for_event(self, event_code):
-        msg = hci_facade.EventCodeMsg(code=int(event_code))
-        self.cert.hci.RegisterEventHandler(msg)
-
-    def register_for_le_event(self, event_code):
-        msg = hci_facade.LeSubeventCodeMsg(code=int(event_code))
-        self.cert.hci.RegisterLeEventHandler(msg)
-
-    def enqueue_hci_command(self, command, expect_complete):
-        cmd_bytes = bytes(command.Serialize())
-        cmd = common.Data(command=cmd_bytes)
-        if (expect_complete):
-            self.cert.hci.EnqueueCommandWithComplete(cmd)
-        else:
-            self.cert.hci.EnqueueCommandWithStatus(cmd)
-
-    def test_le_ad_scan_dut_scans(self):
-        """
-            Verify that the IUT address policy is correctly initiated by SecurityManager, and we can start a scan.
-        """
-        cert_privacy_policy = le_initiator_address_facade.PrivacyPolicy(
-            address_policy=le_initiator_address_facade.AddressPolicy.USE_STATIC_ADDRESS,
-            address_with_type=common.BluetoothAddressWithType(
-                address=common.BluetoothAddress(address=bytes(b'C0:05:04:03:02:01')),
-                type=common.RANDOM_DEVICE_ADDRESS),
-            rotation_irk=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
-            minimum_rotation_time=0,
-            maximum_rotation_time=0)
-        self.cert.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(cert_privacy_policy)
-        with EventStream(
-                # DUT Scans
-                self.dut.hci_le_scanning_manager.StartScan(empty_proto.Empty())) as advertising_event_stream:
-
-            # CERT Advertises
-            gap_name = hci_packets.GapData()
-            gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
-            gap_name.data = list(bytes(b'Im_The_CERT!'))
-            gap_data = le_advertising_facade.GapDataMsg(data=bytes(gap_name.Serialize()))
-            config = le_advertising_facade.AdvertisingConfig(
-                advertisement=[gap_data],
-                interval_min=512,
-                interval_max=768,
-                advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
-                own_address_type=common.USE_RANDOM_DEVICE_ADDRESS,
-                channel_map=7,
-                filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES)
-            request = le_advertising_facade.CreateAdvertiserRequest(config=config)
-
-            create_response = self.cert.hci_le_advertising_manager.CreateAdvertiser(request)
-
-            assertThat(advertising_event_stream).emits(lambda packet: b'Im_The_CERT' in packet.event)
-
-            remove_request = le_advertising_facade.RemoveAdvertiserRequest(advertiser_id=create_response.advertiser_id)
-            self.cert.hci_le_advertising_manager.RemoveAdvertiser(remove_request)
+        GdBaseTestClass.setup_class(self, dut_module='SECURITY', cert_module='HCI_INTERFACES')
diff --git a/gd/hci/cert/le_scanning_with_security_test_lib.py b/gd/hci/cert/le_scanning_with_security_test_lib.py
new file mode 100644
index 0000000..4e1eef3
--- /dev/null
+++ b/gd/hci/cert/le_scanning_with_security_test_lib.py
@@ -0,0 +1,82 @@
+#!/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.
+
+from cert.event_stream import EventStream
+from cert.truth import assertThat
+from google.protobuf import empty_pb2 as empty_proto
+from hci.facade import hci_facade_pb2 as hci_facade
+from hci.facade import le_advertising_manager_facade_pb2 as le_advertising_facade
+from hci.facade import le_initiator_address_facade_pb2 as le_initiator_address_facade
+from bluetooth_packets_python3 import hci_packets
+from facade import common_pb2 as common
+
+
+class LeScanningWithSecurityTestBase():
+
+    def register_for_event(self, event_code):
+        msg = hci_facade.EventCodeMsg(code=int(event_code))
+        self.cert.hci.RegisterEventHandler(msg)
+
+    def register_for_le_event(self, event_code):
+        msg = hci_facade.LeSubeventCodeMsg(code=int(event_code))
+        self.cert.hci.RegisterLeEventHandler(msg)
+
+    def enqueue_hci_command(self, command, expect_complete):
+        cmd_bytes = bytes(command.Serialize())
+        cmd = common.Data(command=cmd_bytes)
+        if (expect_complete):
+            self.cert.hci.EnqueueCommandWithComplete(cmd)
+        else:
+            self.cert.hci.EnqueueCommandWithStatus(cmd)
+
+    def test_le_ad_scan_dut_scans(self):
+        """
+            Verify that the IUT address policy is correctly initiated by SecurityManager, and we can start a scan.
+        """
+        cert_privacy_policy = le_initiator_address_facade.PrivacyPolicy(
+            address_policy=le_initiator_address_facade.AddressPolicy.USE_STATIC_ADDRESS,
+            address_with_type=common.BluetoothAddressWithType(
+                address=common.BluetoothAddress(address=bytes(b'C0:05:04:03:02:01')),
+                type=common.RANDOM_DEVICE_ADDRESS),
+            rotation_irk=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
+            minimum_rotation_time=0,
+            maximum_rotation_time=0)
+        self.cert.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(cert_privacy_policy)
+        with EventStream(
+                # DUT Scans
+                self.dut.hci_le_scanning_manager.StartScan(empty_proto.Empty())) as advertising_event_stream:
+
+            # CERT Advertises
+            gap_name = hci_packets.GapData()
+            gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+            gap_name.data = list(bytes(b'Im_The_CERT!'))
+            gap_data = le_advertising_facade.GapDataMsg(data=bytes(gap_name.Serialize()))
+            config = le_advertising_facade.AdvertisingConfig(
+                advertisement=[gap_data],
+                interval_min=512,
+                interval_max=768,
+                advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
+                own_address_type=common.USE_RANDOM_DEVICE_ADDRESS,
+                channel_map=7,
+                filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES)
+            request = le_advertising_facade.CreateAdvertiserRequest(config=config)
+
+            create_response = self.cert.hci_le_advertising_manager.CreateAdvertiser(request)
+
+            assertThat(advertising_event_stream).emits(lambda packet: b'Im_The_CERT' in packet.event)
+
+            remove_request = le_advertising_facade.RemoveAdvertiserRequest(advertiser_id=create_response.advertiser_id)
+            self.cert.hci_le_advertising_manager.RemoveAdvertiser(remove_request)
diff --git a/gd/hci/controller.cc b/gd/hci/controller.cc
index dc164c5..cc5c760 100644
--- a/gd/hci/controller.cc
+++ b/gd/hci/controller.cc
@@ -43,13 +43,6 @@
     le_set_event_mask(kDefaultLeEventMask);
     set_event_mask(kDefaultEventMask);
     write_le_host_support(Enable::ENABLED);
-    // SSP is managed by security layer once enabled
-    if (!common::init_flags::gd_security_is_enabled()) {
-      write_simple_pairing_mode(Enable::ENABLED);
-      hci_->EnqueueCommand(
-          WriteSecureConnectionsHostSupportBuilder::Create(Enable::ENABLED),
-          handler->BindOnceOn(this, &Controller::impl::write_secure_connections_host_support_complete_handler));
-    }
     hci_->EnqueueCommand(ReadLocalNameBuilder::Create(),
                          handler->BindOnceOn(this, &Controller::impl::read_local_name_complete_handler));
     hci_->EnqueueCommand(ReadLocalVersionInformationBuilder::Create(),
@@ -110,6 +103,15 @@
       le_maximum_data_length_.supported_max_tx_time_ = 0;
     }
 
+    // SSP is managed by security layer once enabled
+    if (!common::init_flags::gd_security_is_enabled()) {
+      write_simple_pairing_mode(Enable::ENABLED);
+      if (module_.SupportsSecureConnections()) {
+        hci_->EnqueueCommand(
+            WriteSecureConnectionsHostSupportBuilder::Create(Enable::ENABLED),
+            handler->BindOnceOn(this, &Controller::impl::write_secure_connections_host_support_complete_handler));
+      }
+    }
     if (is_supported(OpCode::LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH) && module_.SupportsBlePacketExtension()) {
       hci_->EnqueueCommand(
           LeReadSuggestedDefaultDataLengthBuilder::Create(),
diff --git a/gd/iso/cert/le_iso_test.py b/gd/iso/cert/le_iso_test.py
index c1d3d65..f803144 100644
--- a/gd/iso/cert/le_iso_test.py
+++ b/gd/iso/cert/le_iso_test.py
@@ -13,274 +13,19 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import time
-import logging
-
-from bluetooth_packets_python3 import hci_packets
-from cert.event_stream import EventStream
 from cert.gd_base_test import GdBaseTestClass
-from cert.matchers import HciMatchers, IsoMatchers, L2capMatchers
-from cert.metadata import metadata
-from cert.py_hci import PyHci
-from cert.py_l2cap import PyLeL2cap
-from cert.py_le_iso import PyLeIso
-from cert.py_le_iso import CisTestParameters
-from cert.truth import assertThat
-from datetime import timedelta
-from facade import common_pb2 as common
-from hci.facade import controller_facade_pb2 as controller_facade
-from hci.facade import le_advertising_manager_facade_pb2 as le_advertising_facade
-from hci.facade import le_initiator_address_facade_pb2 as le_initiator_address_facade
-from google.protobuf import empty_pb2 as empty_proto
-from neighbor.facade import facade_pb2 as neighbor_facade
-from l2cap.le.cert.cert_le_l2cap import CertLeL2cap
-from iso.cert.cert_le_iso import CertLeIso
-
-import time
-from bluetooth_packets_python3.hci_packets import OpCode
+from iso.cert.le_iso_test_lib import LeIsoTestBase
 
 
-class LeIsoTest(GdBaseTestClass):
-    """
-        Collection of tests that each sample results from
-        different (unique) combinations of io capabilities, authentication requirements, and oob data.
-    """
+class LeIsoTest(GdBaseTestClass, LeIsoTestBase):
 
     def setup_class(self):
-        super().setup_class(dut_module='L2CAP', cert_module='HCI_INTERFACES')
+        GdBaseTestClass.setup_class(self, dut_module='L2CAP', cert_module='HCI_INTERFACES')
 
     def setup_test(self):
-        super().setup_test()
-
-        self.dut_l2cap = PyLeL2cap(self.dut)
-        self.cert_l2cap = CertLeL2cap(self.cert)
-        self.dut_address = common.BluetoothAddressWithType(
-            address=common.BluetoothAddress(address=bytes(b'D0:05:04:03:02:01')), type=common.RANDOM_DEVICE_ADDRESS)
-        self.cert_address = common.BluetoothAddressWithType(
-            address=common.BluetoothAddress(address=bytes(b'C0:11:FF:AA:33:22')), type=common.RANDOM_DEVICE_ADDRESS)
-        dut_privacy_policy = le_initiator_address_facade.PrivacyPolicy(
-            address_policy=le_initiator_address_facade.AddressPolicy.USE_STATIC_ADDRESS,
-            address_with_type=self.dut_address,
-            rotation_irk=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
-            minimum_rotation_time=0,
-            maximum_rotation_time=0)
-        self.dut_l2cap._device.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(dut_privacy_policy)
-        privacy_policy = le_initiator_address_facade.PrivacyPolicy(
-            address_policy=le_initiator_address_facade.AddressPolicy.USE_STATIC_ADDRESS,
-            address_with_type=self.cert_address,
-            rotation_irk=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
-            minimum_rotation_time=0,
-            maximum_rotation_time=0)
-        self.cert_l2cap._device.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(privacy_policy)
-
-        self.dut_iso = PyLeIso(self.dut)
-        self.cert_iso = CertLeIso(self.cert)
+        GdBaseTestClass.setup_test(self)
+        LeIsoTestBase.setup_test(self, self.dut, self.cert)
 
     def teardown_test(self):
-        self.dut_iso.close()
-        self.cert_iso.close()
-
-        self.cert_l2cap.close()
-        self.dut_l2cap.close()
-        super().teardown_test()
-
-    #cert becomes central of connection, dut peripheral
-    def _setup_link_from_cert(self):
-        # DUT Advertises
-        gap_name = hci_packets.GapData()
-        gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
-        gap_name.data = list(bytes(b'Im_The_DUT'))
-        gap_data = le_advertising_facade.GapDataMsg(data=bytes(gap_name.Serialize()))
-        config = le_advertising_facade.AdvertisingConfig(
-            advertisement=[gap_data],
-            interval_min=512,
-            interval_max=768,
-            advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
-            own_address_type=common.USE_RANDOM_DEVICE_ADDRESS,
-            channel_map=7,
-            filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES)
-        request = le_advertising_facade.CreateAdvertiserRequest(config=config)
-        create_response = self.dut.hci_le_advertising_manager.CreateAdvertiser(request)
-        self.cert_l2cap.connect_le_acl(self.dut_address)
-
-    def _setup_cis_from_cert(self, cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, ft_m_to_s, ft_s_to_m, iso_interval,
-                             peripherals_clock_accuracy, packing, framing, max_transport_latency_m_to_s,
-                             max_transport_latency_s_to_m, cis_configs):
-        self.cert_iso.le_set_cig_parameters_test(cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, ft_m_to_s, ft_s_to_m,
-                                                 iso_interval, peripherals_clock_accuracy, packing, framing,
-                                                 max_transport_latency_m_to_s, max_transport_latency_s_to_m,
-                                                 cis_configs)
-
-        cis_handles = self.cert_iso.wait_le_set_cig_parameters_complete()
-
-        cis_handle = cis_handles[0]
-
-        acl_connection_handle = self.cert_l2cap._le_acl.handle
-        self.cert_iso.le_cretate_cis([(cis_handle, acl_connection_handle)])
-        dut_cis_stream = self.dut_iso.wait_le_cis_established()
-        cert_cis_stream = self.cert_iso.wait_le_cis_established()
-        return (dut_cis_stream, cert_cis_stream)
-
-    @metadata(
-        pts_test_id="IAL/CIS/UNF/SLA/BV-01-C",
-        pts_test_name="connected isochronous stream, unframed data, peripheral role")
-    def test_iso_cis_unf_sla_bv_01_c(self):
-        """
-            Verify that the IUT can send an SDU with length ≤ the Isochronous PDU length.
-        """
-        cig_id = 0x01
-        sdu_interval_m_to_s = 0
-        sdu_interval_s_to_m = 0x186a
-        ft_m_to_s = 0
-        ft_s_to_m = 1
-        iso_interval = 0x0A
-        peripherals_clock_accuracy = 0
-        packing = 0
-        framing = 0
-        max_transport_latency_m_to_s = 0
-        max_transport_latency_s_to_m = 0
-        cis_configs = [
-            CisTestParameters(
-                cis_id=0x01,
-                nse=2,
-                max_sdu_m_to_s=100,
-                max_sdu_s_to_m=100,
-                max_pdu_m_to_s=100,
-                max_pdu_s_to_m=100,
-                phy_m_to_s=0x02,
-                phy_s_to_m=0x00,
-                bn_m_to_s=0,
-                bn_s_to_m=2,
-            )
-        ]
-
-        self._setup_link_from_cert()
-        (dut_cis_stream, cert_cis_stream) = self._setup_cis_from_cert(
-            cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, ft_m_to_s, ft_s_to_m, iso_interval,
-            peripherals_clock_accuracy, packing, framing, max_transport_latency_m_to_s, max_transport_latency_s_to_m,
-            cis_configs)
-        dut_cis_stream.send(b'abcdefgh' * 10)
-        assertThat(cert_cis_stream).emits(IsoMatchers.Data(b'abcdefgh' * 10))
-
-    @metadata(
-        pts_test_id="IAL/CIS/UNF/SLA/BV-25-C",
-        pts_test_name="connected isochronous stream, unframed data, peripheral role")
-    def test_iso_cis_unf_sla_bv_25_c(self):
-        """
-            Verify that the IUT can send an SDU with length ≤ the Isochronous PDU length.
-        """
-        cig_id = 0x01
-        sdu_interval_m_to_s = 0x7530
-        sdu_interval_s_to_m = 0x7530
-        ft_m_to_s = 3
-        ft_s_to_m = 2
-        iso_interval = 0x18
-        peripherals_clock_accuracy = 0
-        packing = 0
-        framing = 0
-        max_transport_latency_m_to_s = 0
-        max_transport_latency_s_to_m = 0
-        cis_configs = [
-            CisTestParameters(
-                cis_id=0x01,
-                nse=5,
-                max_sdu_m_to_s=100,
-                max_sdu_s_to_m=100,
-                max_pdu_m_to_s=100,
-                max_pdu_s_to_m=100,
-                phy_m_to_s=0x02,
-                phy_s_to_m=0x00,
-                bn_m_to_s=3,
-                bn_s_to_m=1,
-            )
-        ]
-
-        self._setup_link_from_cert()
-        (dut_cis_stream, cert_cis_stream) = self._setup_cis_from_cert(
-            cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, ft_m_to_s, ft_s_to_m, iso_interval,
-            peripherals_clock_accuracy, packing, framing, max_transport_latency_m_to_s, max_transport_latency_s_to_m,
-            cis_configs)
-        dut_cis_stream.send(b'abcdefgh' * 10)
-        assertThat(cert_cis_stream).emits(IsoMatchers.Data(b'abcdefgh' * 10))
-
-    @metadata(
-        pts_test_id="IAL/CIS/FRA/SLA/BV-03-C",
-        pts_test_name="connected isochronous stream, framed data, peripheral role")
-    def test_iso_cis_fra_sla_bv_03_c(self):
-        """
-            Verify that the IUT can send an SDU with length ≤ the Isochronous PDU length.
-        """
-        cig_id = 0x01
-        sdu_interval_m_to_s = 0x0000
-        sdu_interval_s_to_m = 0x4e30
-        ft_m_to_s = 0
-        ft_s_to_m = 2
-        iso_interval = 0x14
-        peripherals_clock_accuracy = 0
-        packing = 0
-        framing = 1
-        max_transport_latency_m_to_s = 0
-        max_transport_latency_s_to_m = 0
-        cis_configs = [
-            CisTestParameters(
-                cis_id=0x01,
-                nse=4,
-                max_sdu_m_to_s=100,
-                max_sdu_s_to_m=100,
-                max_pdu_m_to_s=100,
-                max_pdu_s_to_m=100,
-                phy_m_to_s=0x02,
-                phy_s_to_m=0x00,
-                bn_m_to_s=0,
-                bn_s_to_m=2,
-            )
-        ]
-
-        self._setup_link_from_cert()
-        (dut_cis_stream, cert_cis_stream) = self._setup_cis_from_cert(
-            cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, ft_m_to_s, ft_s_to_m, iso_interval,
-            peripherals_clock_accuracy, packing, framing, max_transport_latency_m_to_s, max_transport_latency_s_to_m,
-            cis_configs)
-        dut_cis_stream.send(b'abcdefgh' * 10)
-        assertThat(cert_cis_stream).emits(IsoMatchers.Data(b'abcdefgh' * 10))
-
-    @metadata(
-        pts_test_id="IAL/CIS/FRA/SLA/BV-26-C",
-        pts_test_name="connected isochronous stream, framed data, peripheral role")
-    def test_iso_cis_fra_sla_bv_26_c(self):
-        """
-            Verify that the IUT can send an SDU with length ≤ the Isochronous PDU length.
-        """
-        cig_id = 0x01
-        sdu_interval_m_to_s = 0x14D5
-        sdu_interval_s_to_m = 0x14D5
-        ft_m_to_s = 1
-        ft_s_to_m = 1
-        iso_interval = 0x08
-        peripherals_clock_accuracy = 0
-        packing = 0
-        framing = 1
-        max_transport_latency_m_to_s = 0
-        max_transport_latency_s_to_m = 0
-        cis_configs = [
-            CisTestParameters(
-                cis_id=0x01,
-                nse=2,
-                max_sdu_m_to_s=100,
-                max_sdu_s_to_m=100,
-                max_pdu_m_to_s=100,
-                max_pdu_s_to_m=100,
-                phy_m_to_s=0x02,
-                phy_s_to_m=0x00,
-                bn_m_to_s=1,
-                bn_s_to_m=1,
-            )
-        ]
-
-        self._setup_link_from_cert()
-        (dut_cis_stream, cert_cis_stream) = self._setup_cis_from_cert(
-            cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, ft_m_to_s, ft_s_to_m, iso_interval,
-            peripherals_clock_accuracy, packing, framing, max_transport_latency_m_to_s, max_transport_latency_s_to_m,
-            cis_configs)
-        dut_cis_stream.send(b'abcdefgh' * 10)
-        assertThat(cert_cis_stream).emits(IsoMatchers.Data(b'abcdefgh' * 10))
+        LeIsoTestBase.teardown_test(self)
+        GdBaseTestClass.teardown_test(self)
diff --git a/gd/iso/cert/le_iso_test_lib.py b/gd/iso/cert/le_iso_test_lib.py
new file mode 100644
index 0000000..3fa93be
--- /dev/null
+++ b/gd/iso/cert/le_iso_test_lib.py
@@ -0,0 +1,282 @@
+#
+#   Copyright 2021 - 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.
+
+import time
+import logging
+
+from bluetooth_packets_python3 import hci_packets
+from cert.event_stream import EventStream
+from cert.matchers import HciMatchers, IsoMatchers, L2capMatchers
+from cert.metadata import metadata
+from cert.py_hci import PyHci
+from cert.py_l2cap import PyLeL2cap
+from cert.py_le_iso import PyLeIso
+from cert.py_le_iso import CisTestParameters
+from cert.truth import assertThat
+from datetime import timedelta
+from facade import common_pb2 as common
+from hci.facade import controller_facade_pb2 as controller_facade
+from hci.facade import le_advertising_manager_facade_pb2 as le_advertising_facade
+from hci.facade import le_initiator_address_facade_pb2 as le_initiator_address_facade
+from google.protobuf import empty_pb2 as empty_proto
+from neighbor.facade import facade_pb2 as neighbor_facade
+from l2cap.le.cert.cert_le_l2cap import CertLeL2cap
+from iso.cert.cert_le_iso import CertLeIso
+
+import time
+from bluetooth_packets_python3.hci_packets import OpCode
+
+
+class LeIsoTestBase():
+    """
+        Collection of tests that each sample results from
+        different (unique) combinations of io capabilities, authentication requirements, and oob data.
+    """
+
+    def setup_test(self, dut, cert):
+        self.dut = dut
+        self.cert = cert
+
+        self.dut_l2cap = PyLeL2cap(self.dut)
+        self.cert_l2cap = CertLeL2cap(self.cert)
+        self.dut_address = common.BluetoothAddressWithType(
+            address=common.BluetoothAddress(address=bytes(b'D0:05:04:03:02:01')), type=common.RANDOM_DEVICE_ADDRESS)
+        self.cert_address = common.BluetoothAddressWithType(
+            address=common.BluetoothAddress(address=bytes(b'C0:11:FF:AA:33:22')), type=common.RANDOM_DEVICE_ADDRESS)
+        dut_privacy_policy = le_initiator_address_facade.PrivacyPolicy(
+            address_policy=le_initiator_address_facade.AddressPolicy.USE_STATIC_ADDRESS,
+            address_with_type=self.dut_address,
+            rotation_irk=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
+            minimum_rotation_time=0,
+            maximum_rotation_time=0)
+        self.dut_l2cap._device.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(dut_privacy_policy)
+        privacy_policy = le_initiator_address_facade.PrivacyPolicy(
+            address_policy=le_initiator_address_facade.AddressPolicy.USE_STATIC_ADDRESS,
+            address_with_type=self.cert_address,
+            rotation_irk=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
+            minimum_rotation_time=0,
+            maximum_rotation_time=0)
+        self.cert_l2cap._device.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(privacy_policy)
+
+        self.dut_iso = PyLeIso(self.dut)
+        self.cert_iso = CertLeIso(self.cert)
+
+    def teardown_test(self):
+        self.dut_iso.close()
+        self.cert_iso.close()
+
+        self.cert_l2cap.close()
+        self.dut_l2cap.close()
+
+    #cert becomes central of connection, dut peripheral
+    def _setup_link_from_cert(self):
+        # DUT Advertises
+        gap_name = hci_packets.GapData()
+        gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+        gap_name.data = list(bytes(b'Im_The_DUT'))
+        gap_data = le_advertising_facade.GapDataMsg(data=bytes(gap_name.Serialize()))
+        config = le_advertising_facade.AdvertisingConfig(
+            advertisement=[gap_data],
+            interval_min=512,
+            interval_max=768,
+            advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
+            own_address_type=common.USE_RANDOM_DEVICE_ADDRESS,
+            channel_map=7,
+            filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES)
+        request = le_advertising_facade.CreateAdvertiserRequest(config=config)
+        create_response = self.dut.hci_le_advertising_manager.CreateAdvertiser(request)
+        self.cert_l2cap.connect_le_acl(self.dut_address)
+
+    def _setup_cis_from_cert(self, cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, ft_m_to_s, ft_s_to_m, iso_interval,
+                             peripherals_clock_accuracy, packing, framing, max_transport_latency_m_to_s,
+                             max_transport_latency_s_to_m, cis_configs):
+        self.cert_iso.le_set_cig_parameters_test(cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, ft_m_to_s, ft_s_to_m,
+                                                 iso_interval, peripherals_clock_accuracy, packing, framing,
+                                                 max_transport_latency_m_to_s, max_transport_latency_s_to_m,
+                                                 cis_configs)
+
+        cis_handles = self.cert_iso.wait_le_set_cig_parameters_complete()
+
+        cis_handle = cis_handles[0]
+
+        acl_connection_handle = self.cert_l2cap._le_acl.handle
+        self.cert_iso.le_cretate_cis([(cis_handle, acl_connection_handle)])
+        dut_cis_stream = self.dut_iso.wait_le_cis_established()
+        cert_cis_stream = self.cert_iso.wait_le_cis_established()
+        return (dut_cis_stream, cert_cis_stream)
+
+    @metadata(
+        pts_test_id="IAL/CIS/UNF/SLA/BV-01-C",
+        pts_test_name="connected isochronous stream, unframed data, peripheral role")
+    def test_iso_cis_unf_sla_bv_01_c(self):
+        """
+            Verify that the IUT can send an SDU with length ≤ the Isochronous PDU length.
+        """
+        cig_id = 0x01
+        sdu_interval_m_to_s = 0
+        sdu_interval_s_to_m = 0x186a
+        ft_m_to_s = 0
+        ft_s_to_m = 1
+        iso_interval = 0x0A
+        peripherals_clock_accuracy = 0
+        packing = 0
+        framing = 0
+        max_transport_latency_m_to_s = 0
+        max_transport_latency_s_to_m = 0
+        cis_configs = [
+            CisTestParameters(
+                cis_id=0x01,
+                nse=2,
+                max_sdu_m_to_s=100,
+                max_sdu_s_to_m=100,
+                max_pdu_m_to_s=100,
+                max_pdu_s_to_m=100,
+                phy_m_to_s=0x02,
+                phy_s_to_m=0x00,
+                bn_m_to_s=0,
+                bn_s_to_m=2,
+            )
+        ]
+
+        self._setup_link_from_cert()
+        (dut_cis_stream, cert_cis_stream) = self._setup_cis_from_cert(
+            cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, ft_m_to_s, ft_s_to_m, iso_interval,
+            peripherals_clock_accuracy, packing, framing, max_transport_latency_m_to_s, max_transport_latency_s_to_m,
+            cis_configs)
+        dut_cis_stream.send(b'abcdefgh' * 10)
+        assertThat(cert_cis_stream).emits(IsoMatchers.Data(b'abcdefgh' * 10))
+
+    @metadata(
+        pts_test_id="IAL/CIS/UNF/SLA/BV-25-C",
+        pts_test_name="connected isochronous stream, unframed data, peripheral role")
+    def test_iso_cis_unf_sla_bv_25_c(self):
+        """
+            Verify that the IUT can send an SDU with length ≤ the Isochronous PDU length.
+        """
+        cig_id = 0x01
+        sdu_interval_m_to_s = 0x7530
+        sdu_interval_s_to_m = 0x7530
+        ft_m_to_s = 3
+        ft_s_to_m = 2
+        iso_interval = 0x18
+        peripherals_clock_accuracy = 0
+        packing = 0
+        framing = 0
+        max_transport_latency_m_to_s = 0
+        max_transport_latency_s_to_m = 0
+        cis_configs = [
+            CisTestParameters(
+                cis_id=0x01,
+                nse=5,
+                max_sdu_m_to_s=100,
+                max_sdu_s_to_m=100,
+                max_pdu_m_to_s=100,
+                max_pdu_s_to_m=100,
+                phy_m_to_s=0x02,
+                phy_s_to_m=0x00,
+                bn_m_to_s=3,
+                bn_s_to_m=1,
+            )
+        ]
+
+        self._setup_link_from_cert()
+        (dut_cis_stream, cert_cis_stream) = self._setup_cis_from_cert(
+            cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, ft_m_to_s, ft_s_to_m, iso_interval,
+            peripherals_clock_accuracy, packing, framing, max_transport_latency_m_to_s, max_transport_latency_s_to_m,
+            cis_configs)
+        dut_cis_stream.send(b'abcdefgh' * 10)
+        assertThat(cert_cis_stream).emits(IsoMatchers.Data(b'abcdefgh' * 10))
+
+    @metadata(
+        pts_test_id="IAL/CIS/FRA/SLA/BV-03-C",
+        pts_test_name="connected isochronous stream, framed data, peripheral role")
+    def test_iso_cis_fra_sla_bv_03_c(self):
+        """
+            Verify that the IUT can send an SDU with length ≤ the Isochronous PDU length.
+        """
+        cig_id = 0x01
+        sdu_interval_m_to_s = 0x0000
+        sdu_interval_s_to_m = 0x4e30
+        ft_m_to_s = 0
+        ft_s_to_m = 2
+        iso_interval = 0x14
+        peripherals_clock_accuracy = 0
+        packing = 0
+        framing = 1
+        max_transport_latency_m_to_s = 0
+        max_transport_latency_s_to_m = 0
+        cis_configs = [
+            CisTestParameters(
+                cis_id=0x01,
+                nse=4,
+                max_sdu_m_to_s=100,
+                max_sdu_s_to_m=100,
+                max_pdu_m_to_s=100,
+                max_pdu_s_to_m=100,
+                phy_m_to_s=0x02,
+                phy_s_to_m=0x00,
+                bn_m_to_s=0,
+                bn_s_to_m=2,
+            )
+        ]
+
+        self._setup_link_from_cert()
+        (dut_cis_stream, cert_cis_stream) = self._setup_cis_from_cert(
+            cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, ft_m_to_s, ft_s_to_m, iso_interval,
+            peripherals_clock_accuracy, packing, framing, max_transport_latency_m_to_s, max_transport_latency_s_to_m,
+            cis_configs)
+        dut_cis_stream.send(b'abcdefgh' * 10)
+        assertThat(cert_cis_stream).emits(IsoMatchers.Data(b'abcdefgh' * 10))
+
+    @metadata(
+        pts_test_id="IAL/CIS/FRA/SLA/BV-26-C",
+        pts_test_name="connected isochronous stream, framed data, peripheral role")
+    def test_iso_cis_fra_sla_bv_26_c(self):
+        """
+            Verify that the IUT can send an SDU with length ≤ the Isochronous PDU length.
+        """
+        cig_id = 0x01
+        sdu_interval_m_to_s = 0x14D5
+        sdu_interval_s_to_m = 0x14D5
+        ft_m_to_s = 1
+        ft_s_to_m = 1
+        iso_interval = 0x08
+        peripherals_clock_accuracy = 0
+        packing = 0
+        framing = 1
+        max_transport_latency_m_to_s = 0
+        max_transport_latency_s_to_m = 0
+        cis_configs = [
+            CisTestParameters(
+                cis_id=0x01,
+                nse=2,
+                max_sdu_m_to_s=100,
+                max_sdu_s_to_m=100,
+                max_pdu_m_to_s=100,
+                max_pdu_s_to_m=100,
+                phy_m_to_s=0x02,
+                phy_s_to_m=0x00,
+                bn_m_to_s=1,
+                bn_s_to_m=1,
+            )
+        ]
+
+        self._setup_link_from_cert()
+        (dut_cis_stream, cert_cis_stream) = self._setup_cis_from_cert(
+            cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, ft_m_to_s, ft_s_to_m, iso_interval,
+            peripherals_clock_accuracy, packing, framing, max_transport_latency_m_to_s, max_transport_latency_s_to_m,
+            cis_configs)
+        dut_cis_stream.send(b'abcdefgh' * 10)
+        assertThat(cert_cis_stream).emits(IsoMatchers.Data(b'abcdefgh' * 10))
diff --git a/gd/l2cap/classic/cert/l2cap_performance_test.py b/gd/l2cap/classic/cert/l2cap_performance_test.py
index 9c387ee..70a8477 100644
--- a/gd/l2cap/classic/cert/l2cap_performance_test.py
+++ b/gd/l2cap/classic/cert/l2cap_performance_test.py
@@ -13,175 +13,19 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-from datetime import datetime, timedelta
-
-from bluetooth_packets_python3 import RawBuilder
-from cert.matchers import L2capMatchers
-from cert.truth import assertThat
-from cert.performance_test_logger import PerformanceTestLogger
-from l2cap.classic.cert.cert_l2cap import CertL2cap
-from l2cap.classic.cert.l2cap_test import L2capTestBase
-from l2cap.classic.facade_pb2 import RetransmissionFlowControlMode
-from bluetooth_packets_python3.l2cap_packets import FcsType
-from bluetooth_packets_python3.l2cap_packets import SupervisoryFunction
+from cert.gd_base_test import GdBaseTestClass
+from l2cap.classic.cert.l2cap_performance_test_lib import L2capPerformanceTestBase
 
 
-class L2capPerformanceTest(L2capTestBase):
+class L2capPerformanceTest(GdBaseTestClass, L2capPerformanceTestBase):
+
+    def setup_class(self):
+        GdBaseTestClass.setup_class(self, dut_module='L2CAP', cert_module='HCI_INTERFACES')
 
     def setup_test(self):
-        super().setup_test()
-        self.performance_test_logger = PerformanceTestLogger()
+        GdBaseTestClass.setup_test(self)
+        L2capPerformanceTestBase.setup_test(self, self.dut, self.cert)
 
     def teardown_test(self):
-        super().teardown_test()
-
-    def _basic_mode_tx(self, mtu, packets):
-        """
-        Send the specified number of packets and return the time interval in ms.
-        """
-        self._setup_link_from_cert()
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert()
-        self.performance_test_logger.start_interval("TX")
-        for _ in range(packets):
-            dut_channel.send(b'a' * mtu)
-        assertThat(cert_channel).emits(
-            L2capMatchers.Data(b'a' * mtu), at_least_times=packets, timeout=timedelta(seconds=60))
-        self.performance_test_logger.end_interval("TX")
-
-        duration = self.performance_test_logger.get_duration_of_intervals("TX")[0]
-        self.log.info("Duration: %s" % str(duration))
-
-        return duration
-
-    def _basic_mode_tx_fixed_interval(self, mtu, interval=timedelta(seconds=10), batch_size=20):
-        """
-        Send packets as much as possible over a certain interval, and return the
-        number of packets sent
-        """
-        self._setup_link_from_cert()
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert()
-        start_time = datetime.now()
-        end_time = start_time + interval
-        packets_sent = 0
-        while datetime.now() < end_time:
-            for _ in range(batch_size):
-                dut_channel.send(b'a' * mtu)
-            packets_sent += batch_size
-            assertThat(cert_channel).emits(L2capMatchers.Data(b'a' * mtu), at_least_times=batch_size)
-
-        return packets_sent
-
-    def _basic_mode_rx(self, mtu, packets):
-        self._setup_link_from_cert()
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert()
-        self.performance_test_logger.start_interval("RX")
-        data = b"a" * mtu
-        data_packet = RawBuilder([x for x in data])
-        for _ in range(packets):
-            cert_channel.send(data_packet)
-        assertThat(dut_channel).emits(
-            L2capMatchers.PacketPayloadRawData(data), at_least_times=packets, timeout=timedelta(seconds=60))
-        self.performance_test_logger.end_interval("RX")
-
-        duration = self.performance_test_logger.get_duration_of_intervals("RX")[0]
-        self.log.info("Duration: %s" % str(duration))
-
-    def _ertm_mode_tx(self, mtu, packets, tx_window_size=10):
-        """
-        Send the specified number of packets and return the time interval in ms.
-        """
-        # Make sure that number of packets is a multiple of tx_window_size
-        packets = packets // tx_window_size * tx_window_size
-        # For ERTM TX test, we have to do it sequentially because cert needs to ack
-        self._setup_link_from_cert()
-
-        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, tx_window_size=tx_window_size)
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM,
-            fcs=FcsType.NO_FCS,
-            req_config_options=config,
-            rsp_config_options=config)
-
-        self.performance_test_logger.start_interval("TX")
-        for i in range(packets):
-            dut_channel.send(b'a' * mtu)
-            if i % tx_window_size == tx_window_size - 1:
-                assertThat(cert_channel).emits(L2capMatchers.IFrame(payload=b'a' * mtu), at_least_times=tx_window_size)
-                cert_channel.send_s_frame(req_seq=(i + 1) % 64, s=SupervisoryFunction.RECEIVER_READY)
-
-        self.performance_test_logger.end_interval("TX")
-
-        duration = self.performance_test_logger.get_duration_of_intervals("TX")[0]
-        self.log.info("Duration: %s" % str(duration))
-
-        return duration
-
-    def _ertm_mode_rx(self, mtu, packets, tx_window_size=10):
-        # Make sure that number of packets is a multiple of tx_window_size
-        packets = packets // tx_window_size * tx_window_size
-
-        self._setup_link_from_cert()
-
-        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, tx_window_size=tx_window_size)
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM,
-            fcs=FcsType.NO_FCS,
-            req_config_options=config,
-            rsp_config_options=config)
-
-        data = b"a" * mtu
-        data_packet = RawBuilder([x for x in data])
-        self.performance_test_logger.start_interval("RX")
-        for i in range(packets):
-            cert_channel.send_i_frame(tx_seq=i % 64, req_seq=0, payload=data_packet)
-            if i % tx_window_size == (tx_window_size - 1):
-                assertThat(cert_channel).emits(L2capMatchers.SFrame(req_seq=(i + 1) % 64))
-        self.performance_test_logger.end_interval("RX")
-
-        duration = self.performance_test_logger.get_duration_of_intervals("RX")[0]
-        self.log.info("Duration: %s" % str(duration))
-
-    def test_basic_mode_tx_672_100(self):
-        duration = self._basic_mode_tx(672, 100)
-        assertThat(duration).isWithin(timedelta(seconds=2))
-
-    def test_basic_mode_tx_100_100(self):
-        duration = self._basic_mode_tx(100, 100)
-        assertThat(duration).isWithin(timedelta(seconds=2))
-
-    def test_ertm_mode_tx_672_100(self):
-        duration = self._ertm_mode_tx(672, 100)
-        assertThat(duration).isWithin(timedelta(seconds=5))
-
-    def test_basic_mode_rx_672_100(self):
-        self._basic_mode_rx(672, 100)
-
-    def test_ertm_mode_rx_672_100(self):
-        self._ertm_mode_rx(672, 100)
-
-    def test_basic_mode_end_to_end_latency(self):
-        self._setup_link_from_cert()
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert()
-
-        data = b"a" * 100
-        data_packet = RawBuilder([x for x in data])
-        for i in range(100):
-            self.performance_test_logger.start_interval("RX")
-            cert_channel.send(data_packet)
-            assertThat(dut_channel).emits(L2capMatchers.PacketPayloadRawData(data))
-            self.performance_test_logger.end_interval("RX")
-        duration = self.performance_test_logger.get_duration_of_intervals("RX")
-        mean = sum(duration, timedelta()) / len(duration)
-        self.log.info("Mean: %s" % str(mean))
-
-    def test_basic_mode_number_of_packets_10_seconds_672(self):
-        number_packets = self._basic_mode_tx_fixed_interval(672)
-        # Requiring that 500 packets (20ms period on average) are sent
-        self.log.info("Packets sent: %d" % number_packets)
-        assertThat(number_packets > 500).isTrue()
+        L2capPerformanceTestBase.teardown_test(self)
+        GdBaseTestClass.teardown_test(self)
diff --git a/gd/l2cap/classic/cert/l2cap_performance_test_lib.py b/gd/l2cap/classic/cert/l2cap_performance_test_lib.py
new file mode 100644
index 0000000..5b0aa23
--- /dev/null
+++ b/gd/l2cap/classic/cert/l2cap_performance_test_lib.py
@@ -0,0 +1,187 @@
+#
+#   Copyright 2020 - 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.
+
+from datetime import datetime, timedelta
+
+from bluetooth_packets_python3 import RawBuilder
+from cert.matchers import L2capMatchers
+from cert.truth import assertThat
+from cert.performance_test_logger import PerformanceTestLogger
+from l2cap.classic.cert.cert_l2cap import CertL2cap
+from l2cap.classic.cert.l2cap_test_lib import GeneralL2capTestBase
+from l2cap.classic.facade_pb2 import RetransmissionFlowControlMode
+from bluetooth_packets_python3.l2cap_packets import FcsType
+from bluetooth_packets_python3.l2cap_packets import SupervisoryFunction
+
+
+class L2capPerformanceTestBase(GeneralL2capTestBase):
+
+    def setup_test(self, dut, cert):
+        GeneralL2capTestBase.setup_test(self, dut, cert)
+        self.performance_test_logger = PerformanceTestLogger()
+
+    def teardown_test(self):
+        GeneralL2capTestBase.teardown_test(self)
+
+    def _basic_mode_tx(self, mtu, packets):
+        """
+        Send the specified number of packets and return the time interval in ms.
+        """
+        self._setup_link_from_cert()
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert()
+        self.performance_test_logger.start_interval("TX")
+        for _ in range(packets):
+            dut_channel.send(b'a' * mtu)
+        assertThat(cert_channel).emits(
+            L2capMatchers.Data(b'a' * mtu), at_least_times=packets, timeout=timedelta(seconds=60))
+        self.performance_test_logger.end_interval("TX")
+
+        duration = self.performance_test_logger.get_duration_of_intervals("TX")[0]
+        self.log.info("Duration: %s" % str(duration))
+
+        return duration
+
+    def _basic_mode_tx_fixed_interval(self, mtu, interval=timedelta(seconds=10), batch_size=20):
+        """
+        Send packets as much as possible over a certain interval, and return the
+        number of packets sent
+        """
+        self._setup_link_from_cert()
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert()
+        start_time = datetime.now()
+        end_time = start_time + interval
+        packets_sent = 0
+        while datetime.now() < end_time:
+            for _ in range(batch_size):
+                dut_channel.send(b'a' * mtu)
+            packets_sent += batch_size
+            assertThat(cert_channel).emits(L2capMatchers.Data(b'a' * mtu), at_least_times=batch_size)
+
+        return packets_sent
+
+    def _basic_mode_rx(self, mtu, packets):
+        self._setup_link_from_cert()
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert()
+        self.performance_test_logger.start_interval("RX")
+        data = b"a" * mtu
+        data_packet = RawBuilder([x for x in data])
+        for _ in range(packets):
+            cert_channel.send(data_packet)
+        assertThat(dut_channel).emits(
+            L2capMatchers.PacketPayloadRawData(data), at_least_times=packets, timeout=timedelta(seconds=60))
+        self.performance_test_logger.end_interval("RX")
+
+        duration = self.performance_test_logger.get_duration_of_intervals("RX")[0]
+        self.log.info("Duration: %s" % str(duration))
+
+    def _ertm_mode_tx(self, mtu, packets, tx_window_size=10):
+        """
+        Send the specified number of packets and return the time interval in ms.
+        """
+        # Make sure that number of packets is a multiple of tx_window_size
+        packets = packets // tx_window_size * tx_window_size
+        # For ERTM TX test, we have to do it sequentially because cert needs to ack
+        self._setup_link_from_cert()
+
+        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, tx_window_size=tx_window_size)
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM,
+            fcs=FcsType.NO_FCS,
+            req_config_options=config,
+            rsp_config_options=config)
+
+        self.performance_test_logger.start_interval("TX")
+        for i in range(packets):
+            dut_channel.send(b'a' * mtu)
+            if i % tx_window_size == tx_window_size - 1:
+                assertThat(cert_channel).emits(L2capMatchers.IFrame(payload=b'a' * mtu), at_least_times=tx_window_size)
+                cert_channel.send_s_frame(req_seq=(i + 1) % 64, s=SupervisoryFunction.RECEIVER_READY)
+
+        self.performance_test_logger.end_interval("TX")
+
+        duration = self.performance_test_logger.get_duration_of_intervals("TX")[0]
+        self.log.info("Duration: %s" % str(duration))
+
+        return duration
+
+    def _ertm_mode_rx(self, mtu, packets, tx_window_size=10):
+        # Make sure that number of packets is a multiple of tx_window_size
+        packets = packets // tx_window_size * tx_window_size
+
+        self._setup_link_from_cert()
+
+        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, tx_window_size=tx_window_size)
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM,
+            fcs=FcsType.NO_FCS,
+            req_config_options=config,
+            rsp_config_options=config)
+
+        data = b"a" * mtu
+        data_packet = RawBuilder([x for x in data])
+        self.performance_test_logger.start_interval("RX")
+        for i in range(packets):
+            cert_channel.send_i_frame(tx_seq=i % 64, req_seq=0, payload=data_packet)
+            if i % tx_window_size == (tx_window_size - 1):
+                assertThat(cert_channel).emits(L2capMatchers.SFrame(req_seq=(i + 1) % 64))
+        self.performance_test_logger.end_interval("RX")
+
+        duration = self.performance_test_logger.get_duration_of_intervals("RX")[0]
+        self.log.info("Duration: %s" % str(duration))
+
+    def test_basic_mode_tx_672_100(self):
+        duration = self._basic_mode_tx(672, 100)
+        assertThat(duration).isWithin(timedelta(seconds=2))
+
+    def test_basic_mode_tx_100_100(self):
+        duration = self._basic_mode_tx(100, 100)
+        assertThat(duration).isWithin(timedelta(seconds=2))
+
+    def test_ertm_mode_tx_672_100(self):
+        duration = self._ertm_mode_tx(672, 100)
+        assertThat(duration).isWithin(timedelta(seconds=5))
+
+    def test_basic_mode_rx_672_100(self):
+        self._basic_mode_rx(672, 100)
+
+    def test_ertm_mode_rx_672_100(self):
+        self._ertm_mode_rx(672, 100)
+
+    def test_basic_mode_end_to_end_latency(self):
+        self._setup_link_from_cert()
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert()
+
+        data = b"a" * 100
+        data_packet = RawBuilder([x for x in data])
+        for i in range(100):
+            self.performance_test_logger.start_interval("RX")
+            cert_channel.send(data_packet)
+            assertThat(dut_channel).emits(L2capMatchers.PacketPayloadRawData(data))
+            self.performance_test_logger.end_interval("RX")
+        duration = self.performance_test_logger.get_duration_of_intervals("RX")
+        mean = sum(duration, timedelta()) / len(duration)
+        self.log.info("Mean: %s" % str(mean))
+
+    def test_basic_mode_number_of_packets_10_seconds_672(self):
+        number_packets = self._basic_mode_tx_fixed_interval(672)
+        # Requiring that 500 packets (20ms period on average) are sent
+        self.log.info("Packets sent: %d" % number_packets)
+        assertThat(number_packets > 500).isTrue()
diff --git a/gd/l2cap/classic/cert/l2cap_test.py b/gd/l2cap/classic/cert/l2cap_test.py
index 081ee7b..b795734 100644
--- a/gd/l2cap/classic/cert/l2cap_test.py
+++ b/gd/l2cap/classic/cert/l2cap_test.py
@@ -13,1276 +13,19 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-from datetime import timedelta
-
-from mobly import asserts
-
-from bluetooth_packets_python3 import l2cap_packets
-from bluetooth_packets_python3 import RawBuilder
-from bluetooth_packets_python3.l2cap_packets import FcsType
-from bluetooth_packets_python3.l2cap_packets import Final
-from bluetooth_packets_python3.l2cap_packets import Poll
-from bluetooth_packets_python3.l2cap_packets import SegmentationAndReassembly
-from bluetooth_packets_python3.l2cap_packets import SupervisoryFunction
-from cert.behavior import when, anything, wait_until
-from cert.event_stream import EventStream
 from cert.gd_base_test import GdBaseTestClass
-from cert.matchers import L2capMatchers
-from cert.metadata import metadata
-from cert.py_l2cap import PyL2cap
-from cert.truth import assertThat
-from facade import common_pb2
-from google.protobuf import empty_pb2 as empty_proto
-from l2cap.classic.cert.cert_l2cap import CertL2cap
-from l2cap.classic.facade_pb2 import RetransmissionFlowControlMode
-from neighbor.facade import facade_pb2 as neighbor_facade
-
-# Assemble a sample packet.
-SAMPLE_PACKET_DATA = b"\x19\x26\x08\x17"
-SAMPLE_PACKET = RawBuilder([x for x in SAMPLE_PACKET_DATA])
+from l2cap.classic.cert.l2cap_test_lib import L2capTestBase
 
 
-class L2capTestBase(GdBaseTestClass):
+class L2capTest(GdBaseTestClass, L2capTestBase):
 
     def setup_class(self):
-        super().setup_class(dut_module='L2CAP', cert_module='HCI_INTERFACES')
+        GdBaseTestClass.setup_class(self, dut_module='L2CAP', cert_module='HCI_INTERFACES')
 
     def setup_test(self):
-        super().setup_test()
-
-        self.dut_address = self.dut.hci_controller.GetMacAddressSimple()
-        cert_address = common_pb2.BluetoothAddress(
-            address=self.cert.controller_read_only_property.ReadLocalAddress(empty_proto.Empty()).address)
-
-        self.dut_l2cap = PyL2cap(self.dut, cert_address)
-        self.cert_l2cap = CertL2cap(self.cert)
+        GdBaseTestClass.setup_test(self)
+        L2capTestBase.setup_test(self, self.dut, self.cert)
 
     def teardown_test(self):
-        self.cert_l2cap.close()
-        self.dut_l2cap.close()
-        super().teardown_test()
-
-    def _setup_link_from_cert(self):
-        self.dut.neighbor.EnablePageScan(neighbor_facade.EnableMsg(enabled=True))
-        self.cert_l2cap.connect_acl(self.dut_address)
-
-    def _open_unconfigured_channel_from_cert(self,
-                                             signal_id=1,
-                                             scid=0x0101,
-                                             psm=0x33,
-                                             mode=RetransmissionFlowControlMode.BASIC,
-                                             fcs=None):
-
-        dut_channel = self.dut_l2cap.register_dynamic_channel(psm, mode)
-        cert_channel = self.cert_l2cap.open_channel(signal_id, psm, scid, fcs=fcs)
-
-        return (dut_channel, cert_channel)
-
-    def _open_channel_from_cert(self,
-                                signal_id=1,
-                                scid=0x0101,
-                                psm=0x33,
-                                mode=RetransmissionFlowControlMode.BASIC,
-                                fcs=None,
-                                req_config_options=None,
-                                rsp_config_options=None):
-        request_matcher = L2capMatchers.ConfigurationRequestView(scid)
-        if rsp_config_options is not None:
-            when(self.cert_l2cap).on_config_req(request_matcher).then().send_configuration_response(
-                options=rsp_config_options)
-        if rsp_config_options is None and fcs is not None:
-            when(self.cert_l2cap).on_config_req(request_matcher).then().send_configuration_response(
-                options=CertL2cap.config_option_ertm(fcs=fcs))
-
-        (dut_channel, cert_channel) = self._open_unconfigured_channel_from_cert(signal_id, scid, psm, mode, fcs)
-        if req_config_options is None:
-            req_config_options = CertL2cap.config_option_ertm(
-                fcs=fcs) if mode == RetransmissionFlowControlMode.ERTM else []
-
-        cert_channel.send_configure_request(req_config_options)
-
-        cert_channel.verify_configuration_response()
-
-        wait_until(self.cert_l2cap).on_config_req(request_matcher).times(1)
-
-        assertThat(cert_channel.is_configured()).isTrue()
-
-        return (dut_channel, cert_channel)
-
-    def _open_channel_from_dut(self, psm=0x33, mode=RetransmissionFlowControlMode.BASIC):
-        dut_channel_future = self.dut_l2cap.connect_dynamic_channel_to_cert(psm, mode)
-        cert_channel = self.cert_l2cap.verify_and_respond_open_channel_from_remote(psm)
-        dut_channel = dut_channel_future.get_channel()
-
-        cert_channel.verify_configuration_request_and_respond()
-        outgoing_config = []
-        if mode == RetransmissionFlowControlMode.ERTM:
-            outgoing_config = CertL2cap.config_option_ertm()
-        cert_channel.send_configure_request(outgoing_config)
-        cert_channel.verify_configuration_response()
-
-        return (dut_channel, cert_channel)
-
-
-class L2capTest(L2capTestBase):
-
-    def test_connect_dynamic_channel_and_send_data(self):
-        self._setup_link_from_cert()
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert()
-
-        dut_channel.send(b'abc')
-        assertThat(cert_channel).emits(L2capMatchers.Data(b'abc'))
-
-    def test_receive_packet_from_unknown_channel(self):
-        self._setup_link_from_cert()
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert(scid=0x41, psm=0x33)
-
-        i_frame = l2cap_packets.EnhancedInformationFrameBuilder(
-            0x99, 0, Final.NOT_SET, 1, l2cap_packets.SegmentationAndReassembly.UNSEGMENTED, SAMPLE_PACKET)
-        self.cert_l2cap.send_acl(i_frame)
-        assertThat(cert_channel).emitsNone(L2capMatchers.SFrame(req_seq=4), timeout=timedelta(seconds=1))
-
-    def test_open_two_channels(self):
-        self._setup_link_from_cert()
-
-        self._open_channel_from_cert(signal_id=1, scid=0x41, psm=0x41)
-        self._open_channel_from_cert(signal_id=2, scid=0x43, psm=0x43)
-
-    def test_connect_and_send_data_ertm_no_segmentation(self):
-        self._setup_link_from_cert()
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM, fcs=FcsType.NO_FCS)
-
-        dut_channel.send(b'abc' * 34)
-        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=0, payload=b'abc' * 34))
-
-        cert_channel.send_i_frame(tx_seq=0, req_seq=1, payload=SAMPLE_PACKET)
-        # todo verify received?
-
-    @metadata(pts_test_id="L2CAP/COS/CED/BV-01-C", pts_test_name="Request Connection")
-    def test_basic_operation_request_connection(self):
-        """
-        Verify that the IUT is able to request the connection establishment for
-        an L2CAP data channel and initiate the configuration procedure.
-        """
-        self._setup_link_from_cert()
-        (dut_channel, cert_channel) = self._open_channel_from_dut()
-
-    @metadata(pts_test_id="L2CAP/COS/CED/BV-03-C", pts_test_name="Send data")
-    def test_send_data(self):
-        """
-        Verify that the IUT is able to send DATA
-        """
-        self._setup_link_from_cert()
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert()
-        dut_channel.send(b'hello')
-        assertThat(cert_channel).emits(L2capMatchers.Data(b'hello'))
-
-    @metadata(pts_test_id="L2CAP/COS/CED/BV-04-C", pts_test_name="Disconnect")
-    def test_disconnect(self):
-        """
-        Verify that the IUT is able to disconnect the data channel
-        """
-        self._setup_link_from_cert()
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert()
-        dut_channel.close_channel()
-        cert_channel.verify_disconnect_request()
-
-    @metadata(pts_test_id="L2CAP/COS/CED/BV-05-C", pts_test_name="Accept connection")
-    def test_accept_connection(self):
-        """
-        Also verify that DUT can send 48 bytes PDU (minimal MTU)
-        """
-        self._setup_link_from_cert()
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert()
-        dut_channel.send(b'a' * 48)
-        assertThat(cert_channel).emits(L2capMatchers.Data(b'a' * 48))
-
-    @metadata(pts_test_id="L2CAP/COS/CED/BV-07-C", pts_test_name="Accept Disconnect")
-    def test_accept_disconnect(self):
-        """
-        Verify that the IUT is able to respond to the request to disconnect the
-        data channel
-        """
-        self._setup_link_from_cert()
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert()
-        cert_channel.disconnect_and_verify()
-
-    @metadata(pts_test_id="L2CAP/COS/CED/BV-08-C", pts_test_name="Disconnect on Timeout")
-    def test_disconnect_on_timeout(self):
-        """
-        Verify that the IUT disconnects the data channel and shuts down this
-        channel if no response occurs
-        """
-        self._setup_link_from_cert()
-
-        (dut_channel, cert_channel) = self._open_unconfigured_channel_from_cert()
-
-        assertThat(self.cert_l2cap.get_control_channel()).emitsNone(L2capMatchers.ConfigurationResponse())
-        # TODO: Verify that IUT sends disconnect request (not mandated)
-
-    @metadata(pts_test_id="L2CAP/COS/CED/BV-09-C", pts_test_name="Receive Multi-Command Packet")
-    def test_receive_multi_command_packet(self):
-        """
-        Verify that the IUT is able to receive more than one signaling command in one L2CAP
-        packet.
-        """
-        self._setup_link_from_cert()
-
-        psm = 0x33
-        self.dut_l2cap.connect_dynamic_channel_to_cert(psm)
-        self.cert_l2cap.verify_and_respond_open_channel_from_remote_and_send_config_req(psm)
-
-        assertThat(self.cert_l2cap.get_control_channel()).emits(L2capMatchers.ConfigurationResponse())
-
-    @metadata(pts_test_id="L2CAP/COS/CED/BV-11-C", pts_test_name="Configure MTU size")
-    def test_configure_mtu_size(self):
-        """
-        Verify that the IUT is able to configure the supported MTU size
-        """
-        self._setup_link_from_cert()
-        (dut_channel, cert_channel) = self._open_unconfigured_channel_from_cert()
-        cert_channel.send_configure_request(CertL2cap.config_option_mtu_explicit(672))
-        cert_channel.verify_configuration_request_and_respond()
-        # TODO: Probably remove verify_configuration_request_and_respond
-
-    @metadata(pts_test_id="L2CAP/COS/CFD/BV-01-C", pts_test_name="Continuation Flag")
-    def test_continuation_flag(self):
-        """
-        Verify the IUT is able to receive configuration requests that have the
-        continuation flag set
-        """
-        self._setup_link_from_cert()
-
-        (dut_channel, cert_channel) = self._open_unconfigured_channel_from_cert()
-
-        # Send configuration request with CONTINUE
-        mtu_opt = l2cap_packets.MtuConfigurationOption()
-        mtu_opt.mtu = 0x1234
-        cert_channel.send_configure_request([mtu_opt], 2, l2cap_packets.Continuation.CONTINUE)
-
-        flush_timeout_option = l2cap_packets.FlushTimeoutConfigurationOption()
-        flush_timeout_option.flush_timeout = 65535
-        cert_channel.send_configure_request([flush_timeout_option], 3, l2cap_packets.Continuation.END)
-
-        assertThat(self.cert_l2cap.get_control_channel()).emits(L2capMatchers.ConfigurationResponse(), at_least_times=2)
-
-    @metadata(pts_test_id="L2CAP/COS/CFD/BV-02-C", pts_test_name="Negotiation with Reject")
-    def test_retry_config_after_rejection(self):
-        """
-        Verify that the IUT is able to perform negotiation while the Lower
-        Tester rejects the proposed configuration parameter values
-        """
-        self._setup_link_from_cert()
-        scid = 0x41
-        when(self.cert_l2cap).on_config_req(
-            L2capMatchers.ConfigurationRequestView(scid)).then().send_configuration_response(
-                result=l2cap_packets.ConfigurationResponseResult.UNACCEPTABLE_PARAMETERS,
-                options=CertL2cap.config_option_mtu_explicit(200)).send_configuration_response(options=[])
-        (dut_channel, cert_channel) = self._open_unconfigured_channel_from_cert(scid=scid)
-
-        assertThat(self.cert_l2cap.get_control_channel()).emits(L2capMatchers.ConfigurationRequest(), at_least_times=2)
-
-    @metadata(pts_test_id="L2CAP/COS/CFD/BV-03-C", pts_test_name="Send Requested Options")
-    def test_send_requested_options(self):
-        """
-        Verify that the IUT can receive a configuration request with no options
-        and send the requested options to the Lower Tester
-        """
-        self._setup_link_from_cert()
-        (dut_channel, cert_channel) = self._open_channel_from_cert(scid=0x41, psm=0x33)
-
-        # TODO(hsz) implement me!
-
-    @metadata(pts_test_id="L2CAP/COS/CFD/BV-08-C", pts_test_name="Non-blocking Config Response")
-    def test_non_blocking_config_response(self):
-        """
-        Verify that the IUT does not block transmitting L2CAP_ConfigRsp while
-        waiting for L2CAP_ConfigRsp from the Lower Tester
-        """
-        self._setup_link_from_cert()
-
-        (dut_channel, cert_channel) = self._open_unconfigured_channel_from_cert()
-
-        cert_channel.send_configure_request([])
-        cert_channel.verify_configuration_response()
-        cert_channel.verify_configuration_request_and_respond()
-
-        # TODO(hsz) implement me!
-
-    @metadata(pts_test_id="L2CAP/COS/CFD/BV-09-C", pts_test_name="Mandatory 48 Byte MTU")
-    def test_mandatory_48_byte_mtu(self):
-        """
-        Verify that the IUT can support mandatory 48 byte MTU
-        """
-        self._setup_link_from_cert()
-        (dut_channel,
-         cert_channel) = self._open_channel_from_cert(req_config_options=CertL2cap.config_option_mtu_explicit(48))
-
-        dut_channel.send(b"a" * 44)
-        assertThat(cert_channel).emits(L2capMatchers.Data(b"a" * 44))
-
-    @metadata(pts_test_id="L2CAP/COS/CFD/BV-11-C", pts_test_name="Negotiation of Unsupported Parameter")
-    def test_negotiation_of_unsupported_parameter(self):
-        """
-        Verify that the IUT can negotiate when the Lower Tester proposes an unsupported configuration
-        parameter value.
-        """
-        self._setup_link_from_cert()
-        (dut_channel, cert_channel) = self._open_unconfigured_channel_from_cert()
-
-        cert_channel.send_configure_request(CertL2cap.config_option_mtu_explicit(20))
-        # Invalid because minimum is 48
-
-        cert_channel.verify_configuration_response(l2cap_packets.ConfigurationResponseResult.UNACCEPTABLE_PARAMETERS)
-
-    @metadata(pts_test_id="L2CAP/COS/CFD/BV-12-C", pts_test_name="Unknown Option Response")
-    def test_config_unknown_options_with_hint(self):
-        """
-        Verify that the IUT can give the appropriate error code when the Lower
-        Tester proposes any number of unknown options that are optional
-        NOTE: In GD stack, ExtendedWindowSizeOption in unsupported
-        """
-        self._setup_link_from_cert()
-
-        (dut_channel, cert_channel) = self._open_unconfigured_channel_from_cert()
-
-        unknown_opt_hint = l2cap_packets.ExtendedWindowSizeOption()
-        unknown_opt_hint.max_window_size = 20
-        unknown_opt_hint.is_hint = l2cap_packets.ConfigurationOptionIsHint.OPTION_IS_A_HINT
-
-        for i in range(10):
-            cert_channel.send_configure_request([unknown_opt_hint] * i)
-            cert_channel.verify_configuration_response(l2cap_packets.ConfigurationResponseResult.SUCCESS)
-
-    @metadata(pts_test_id="L2CAP/COS/CFD/BV-14-C", pts_test_name="Unknown Mandatory Options Request")
-    def test_unknown_mandatory_options_request(self):
-        """
-        Verify that the IUT can give the appropriate error code when the Lower
-        Tester proposes any number of unknown options where at least one is
-        mandatory.
-        Note: GD stack doesn't support extended window size. For other stacks,
-              we may need to use some other config option
-        """
-        self._setup_link_from_cert()
-
-        (dut_channel, cert_channel) = self._open_unconfigured_channel_from_cert(scid=0x41, psm=0x33)
-
-        unknown_opt = l2cap_packets.ExtendedWindowSizeOption()
-        unknown_opt.max_window_size = 20
-
-        unknown_opt_hint = l2cap_packets.ExtendedWindowSizeOption()
-        unknown_opt_hint.max_window_size = 20
-        unknown_opt_hint.is_hint = l2cap_packets.ConfigurationOptionIsHint.OPTION_IS_A_HINT
-
-        configuration_option_attempts = [[unknown_opt], [unknown_opt, unknown_opt_hint], [
-            unknown_opt, unknown_opt, unknown_opt
-        ], [unknown_opt, unknown_opt_hint, unknown_opt_hint,
-            unknown_opt], [unknown_opt_hint, unknown_opt_hint, unknown_opt_hint, unknown_opt_hint, unknown_opt], [
-                unknown_opt, unknown_opt_hint, unknown_opt_hint, unknown_opt, unknown_opt_hint, unknown_opt_hint
-            ], [unknown_opt, unknown_opt, unknown_opt, unknown_opt, unknown_opt, unknown_opt, unknown_opt], [
-                unknown_opt_hint, unknown_opt_hint, unknown_opt_hint, unknown_opt_hint, unknown_opt_hint,
-                unknown_opt_hint, unknown_opt_hint, unknown_opt
-            ], [
-                unknown_opt_hint, unknown_opt_hint, unknown_opt_hint, unknown_opt_hint, unknown_opt_hint,
-                unknown_opt_hint, unknown_opt_hint, unknown_opt, unknown_opt
-            ], [
-                unknown_opt_hint, unknown_opt_hint, unknown_opt_hint, unknown_opt_hint, unknown_opt_hint,
-                unknown_opt_hint, unknown_opt_hint, unknown_opt_hint, unknown_opt_hint, unknown_opt
-            ]]
-
-        for option_list in configuration_option_attempts:
-            cert_channel.send_configure_request(option_list)
-            cert_channel.verify_configuration_response(l2cap_packets.ConfigurationResponseResult.UNKNOWN_OPTIONS)
-
-    @metadata(pts_test_id="L2CAP/COS/ECH/BV-01-C", pts_test_name="Respond to Echo Request")
-    def test_respond_to_echo_request(self):
-        """
-        Verify that the IUT responds to an echo request.
-        """
-        self._setup_link_from_cert()
-        echo_request = l2cap_packets.EchoRequestBuilder(100, RawBuilder([1, 2, 3]))
-        self.cert_l2cap.get_control_channel().send(echo_request)
-
-        assertThat(self.cert_l2cap.get_control_channel()).emits(L2capMatchers.EchoResponse())
-
-    @metadata(pts_test_id="L2CAP/COS/CED/BI-01-C", pts_test_name="Reject Unknown Command")
-    def test_reject_unknown_command(self):
-        """
-        Verify that the IUT rejects an unknown signaling command
-        """
-        self._setup_link_from_cert()
-
-        # Command code ff, Signal id 01, size 0000
-        invalid_command_packet = RawBuilder([0xff, 0x01, 0x00, 0x00])
-        self.cert_l2cap.get_control_channel().send(invalid_command_packet)
-
-        assertThat(self.cert_l2cap.get_control_channel()).emits(L2capMatchers.CommandReject())
-
-    @metadata(pts_test_id="L2CAP/COS/IEX/BV-01-C", pts_test_name="Query for 1.2 Features")
-    def test_query_for_1_2_features(self):
-        """
-        Verify that the IUT transmits an information request command to solicit
-        if the remote device supports Specification 1.2 features.
-        """
-        self._setup_link_from_cert()
-        assertThat(self.cert_l2cap.get_control_channel()).emits(
-            L2capMatchers.InformationRequestWithType(
-                l2cap_packets.InformationRequestInfoType.EXTENDED_FEATURES_SUPPORTED))
-
-    @metadata(pts_test_id="L2CAP/COS/IEX/BV-02-C", pts_test_name="Respond with 1.2 Features")
-    def test_respond_with_1_2_features(self):
-        """
-        Verify that the IUT responds to an information request command
-        soliciting for Specification 1.2 features
-        """
-        self._setup_link_from_cert()
-        control_channel = self.cert_l2cap.get_control_channel()
-
-        control_channel.send_extended_features_request()
-
-        assertThat(control_channel).emits(L2capMatchers.InformationResponseExtendedFeatures())
-
-    @metadata(
-        pts_test_id="L2CAP/EXF/BV-01-C",
-        pts_test_name="Extended Features Information Response for "
-        "Enhanced Retransmission Mode")
-    def test_extended_feature_info_response_ertm(self):
-        """
-        Verify the IUT can format an Information Response for the information
-        type of Extended Features that correctly identifies that Enhanced
-        Retransmission Mode is locally supported
-        """
-        self._setup_link_from_cert()
-        control_channel = self.cert_l2cap.get_control_channel()
-
-        control_channel.send_extended_features_request()
-
-        assertThat(control_channel).emits(L2capMatchers.InformationResponseExtendedFeatures(supports_ertm=True))
-
-    @metadata(
-        pts_test_id="L2CAP/EXF/BV-02-C", pts_test_name="Extended Features Information Response for "
-        "Streaming Mode")
-    def test_extended_feature_info_response_streaming(self):
-        """
-        Verify the IUT can format an Information Response for the information
-        type of Extended Features that correctly identifies that Streaming Mode
-        is locally supported
-        """
-        asserts.skip("Streaming not supported")
-        self._setup_link_from_cert()
-        control_channel = self.cert_l2cap.get_control_channel()
-
-        control_channel.send_extended_features_request()
-
-        assertThat(control_channel).emits(L2capMatchers.InformationResponseExtendedFeatures(supports_streaming=True))
-
-    @metadata(pts_test_id="L2CAP/EXF/BV-03-C", pts_test_name="Extended Features Information Response for FCS " "Option")
-    def test_extended_feature_info_response_fcs(self):
-        """
-        Verify the IUT can format an Information Response for the information
-        type of Extended Features that correctly identifies that the FCS Option
-        is locally supported.
-
-        Note: This is not mandated by L2CAP Spec
-        """
-        self._setup_link_from_cert()
-        control_channel = self.cert_l2cap.get_control_channel()
-
-        control_channel.send_extended_features_request()
-
-        assertThat(control_channel).emits(L2capMatchers.InformationResponseExtendedFeatures(supports_fcs=True))
-
-    @metadata(
-        pts_test_id="L2CAP/EXF/BV-05-C", pts_test_name="Extended Features Information Response for Fixed "
-        "Channels")
-    def test_extended_feature_info_response_fixed_channels(self):
-        """
-        Verify the IUT can format an Information Response for the information
-        type of Extended Features that correctly identifies that the Fixed
-        Channels option is locally supported
-
-        Note: This is not mandated by L2CAP Spec
-        """
-        self._setup_link_from_cert()
-        control_channel = self.cert_l2cap.get_control_channel()
-
-        control_channel.send_extended_features_request()
-
-        assertThat(control_channel).emits(
-            L2capMatchers.InformationResponseExtendedFeatures(supports_fixed_channels=True))
-
-    @metadata(pts_test_id="L2CAP/FIX/BV-01-C", pts_test_name="Fixed Channels Supported Information Request")
-    def test_fixed_channels_supported_information_request(self):
-        """
-        Verify that the IUT can send an Information Request for the information
-        type of Fixed Channels Supported.
-        """
-        self._setup_link_from_cert()
-        assertThat(self.cert_l2cap.get_control_channel()).emits(
-            L2capMatchers.InformationRequestWithType(l2cap_packets.InformationRequestInfoType.FIXED_CHANNELS_SUPPORTED))
-
-    @metadata(pts_test_id="L2CAP/FOC/BV-01-C", pts_test_name="IUT Initiated Configuration of the FCS Option")
-    def test_config_channel_not_use_FCS(self):
-        """
-        Verify the IUT can configure a channel to not use FCS in I/S-frames.
-        """
-        self._setup_link_from_cert()
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM, fcs=FcsType.NO_FCS)
-
-        dut_channel.send(b'abc')
-        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=0, payload=b'abc'))
-
-    @metadata(pts_test_id="L2CAP/FOC/BV-02-C", pts_test_name="Lower Tester Explicitly Requests FCS should be " "Used")
-    def test_explicitly_request_use_FCS(self):
-        """
-        Verify the IUT will include the FCS in I/S-frames if the Lower Tester
-        explicitly requests that FCS should be used
-        """
-        self._setup_link_from_cert()
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM, fcs=FcsType.DEFAULT)
-
-        dut_channel.send(b'abc')
-        assertThat(cert_channel).emits(L2capMatchers.IFrameWithFcs(payload=b"abc"))
-
-    @metadata(pts_test_id="L2CAP/FOC/BV-03-C", pts_test_name="Lower Tester Implicitly Requests FCS should be " "Used")
-    def test_implicitly_request_use_FCS(self):
-        """
-        Verify the IUT will include the FCS in I/S-frames if the Lower Tester
-        implicitly requests that FCS should be used.
-        """
-        self._setup_link_from_cert()
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM,
-            fcs=FcsType.DEFAULT,
-            req_config_options=CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS))
-
-        dut_channel.send(b'abc')
-        assertThat(cert_channel).emits(L2capMatchers.IFrameWithFcs(payload=b"abc"))
-
-    @metadata(pts_test_id="L2CAP/OFS/BV-01-C", pts_test_name="Sending I-Frames without FCS for ERTM")
-    def test_sending_i_frames_without_fcs_for_ertm(self):
-        """
-        Verify the IUT does not include the FCS in I-frames.
-        """
-        self._setup_link_from_cert()
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM, fcs=FcsType.NO_FCS)
-
-        dut_channel.send(b'abc')
-        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=0, payload=b"abc"))
-
-    @metadata(pts_test_id="L2CAP/OFS/BV-02-C", pts_test_name="Receiving I-Frames without FCS for ERTM")
-    def test_receiving_i_frames_without_fcs_for_ertm(self):
-        """
-        Verify the IUT can handle I-frames that do not contain the FCS.
-        """
-        self._setup_link_from_cert()
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM, fcs=FcsType.NO_FCS)
-
-        cert_channel.send_i_frame(tx_seq=0, req_seq=0, payload=SAMPLE_PACKET)
-        assertThat(dut_channel).emits(L2capMatchers.PacketPayloadRawData(SAMPLE_PACKET_DATA))
-
-    @metadata(pts_test_id="L2CAP/OFS/BV-05-C", pts_test_name="Sending I-Frames with FCS for ERTM")
-    def test_sending_i_frames_with_fcs_for_ertm(self):
-        """
-        Verify the IUT does include the FCS in I-frames.
-        """
-        self._setup_link_from_cert()
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM, fcs=FcsType.DEFAULT)
-
-        dut_channel.send(b'abc')
-        assertThat(cert_channel).emits(L2capMatchers.IFrameWithFcs(tx_seq=0, payload=b"abc"))
-
-    @metadata(pts_test_id="L2CAP/OFS/BV-06-C", pts_test_name="Receiving I-Frames with FCS for ERTM")
-    def test_receiving_i_frames_with_fcs_for_ertm(self):
-        """
-        Verify the IUT can handle I-frames that do contain the FCS.
-        """
-        self._setup_link_from_cert()
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM, fcs=FcsType.DEFAULT)
-
-        cert_channel.send_i_frame(tx_seq=0, req_seq=0, payload=SAMPLE_PACKET, fcs=FcsType.DEFAULT)
-        assertThat(dut_channel).emits(L2capMatchers.PacketPayloadRawData(SAMPLE_PACKET_DATA))
-
-    @metadata(pts_test_id="L2CAP/ERM/BV-01-C", pts_test_name="Transmit I-frames")
-    def test_transmit_i_frames(self):
-        """
-        Verify the IUT can send correctly formatted sequential I-frames with
-        valid values for the enhanced control fields (SAR, F-bit, ReqSeq,
-        TxSeq)
-        """
-        self._setup_link_from_cert()
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM, fcs=FcsType.NO_FCS)
-
-        dut_channel.send(b'abc')
-        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=0, payload=b"abc"))
-
-        cert_channel.send_i_frame(tx_seq=0, req_seq=1, payload=SAMPLE_PACKET)
-
-        dut_channel.send(b'abc')
-        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=1, payload=b"abc"))
-
-        cert_channel.send_i_frame(tx_seq=1, req_seq=2, payload=SAMPLE_PACKET)
-
-        dut_channel.send(b'abc')
-        assertThat(cert_channel).emits(L2capMatchers.PartialData(b"abc"))
-
-        cert_channel.send_i_frame(tx_seq=2, req_seq=3, payload=SAMPLE_PACKET)
-
-    @metadata(pts_test_id="L2CAP/ERM/BV-02-C", pts_test_name="Receive I-Frames")
-    def test_receive_i_frames(self):
-        """
-        Verify the IUT can receive in-sequence valid I-frames and deliver L2CAP
-        SDUs to the Upper Tester
-        """
-        self._setup_link_from_cert()
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM, fcs=FcsType.NO_FCS)
-
-        for i in range(3):
-            cert_channel.send_i_frame(tx_seq=i, req_seq=0, payload=SAMPLE_PACKET)
-            assertThat(cert_channel).emits(L2capMatchers.SFrame(req_seq=i + 1))
-
-        cert_channel.send_i_frame(tx_seq=3, req_seq=0, sar=SegmentationAndReassembly.START, payload=SAMPLE_PACKET)
-        assertThat(cert_channel).emits(L2capMatchers.SFrame(req_seq=4))
-
-        cert_channel.send_i_frame(
-            tx_seq=4, req_seq=0, sar=SegmentationAndReassembly.CONTINUATION, payload=SAMPLE_PACKET)
-        assertThat(cert_channel).emits(L2capMatchers.SFrame(req_seq=5))
-
-        cert_channel.send_i_frame(tx_seq=5, req_seq=0, sar=SegmentationAndReassembly.END, payload=SAMPLE_PACKET)
-        assertThat(cert_channel).emits(L2capMatchers.SFrame(req_seq=6))
-
-    @metadata(pts_test_id="L2CAP/ERM/BV-03-C", pts_test_name="Acknowledging Received I-Frames")
-    def test_acknowledging_received_i_frames(self):
-        """
-        Verify the IUT sends S-frame [RR] with the Poll bit not set to
-        acknowledge data received from the Lower Tester
-        """
-        self._setup_link_from_cert()
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM, fcs=FcsType.NO_FCS)
-
-        for i in range(3):
-            cert_channel.send_i_frame(tx_seq=i, req_seq=0, payload=SAMPLE_PACKET)
-            assertThat(cert_channel).emits(L2capMatchers.SFrame(req_seq=i + 1))
-
-        assertThat(cert_channel).emitsNone(L2capMatchers.SFrame(req_seq=4), timeout=timedelta(seconds=1))
-
-    @metadata(
-        pts_test_id="L2CAP/ERM/BV-05-C",
-        pts_test_name="Resume Transmitting I-Frames when an S-Frame [RR] "
-        "is Received")
-    def test_resume_transmitting_when_received_rr(self):
-        """
-        Verify the IUT will cease transmission of I-frames when the negotiated
-        TxWindow is full. Verify the IUT will resume transmission of I-frames
-        when an S-frame [RR] is received that acknowledges previously sent
-        I-frames
-        """
-        self._setup_link_from_cert()
-
-        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, tx_window_size=1)
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM, req_config_options=config, rsp_config_options=config)
-
-        dut_channel.send(b'abc')
-        dut_channel.send(b'def')
-
-        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=0, payload=b'abc'))
-        assertThat(cert_channel).emitsNone(L2capMatchers.IFrame(tx_seq=1, payload=b'def'))
-
-        cert_channel.send_s_frame(req_seq=1, f=Final.POLL_RESPONSE)
-        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=1))
-
-    @metadata(
-        pts_test_id="L2CAP/ERM/BV-06-C", pts_test_name="Resume Transmitting I-Frames when an I-Frame is "
-        "Received")
-    def test_resume_transmitting_when_acknowledge_previously_sent(self):
-        """
-        Verify the IUT will cease transmission of I-frames when the negotiated
-        TxWindow is full. Verify the IUT will resume transmission of I-frames
-        when an I-frame is received that acknowledges previously sent I-frames
-        """
-        self._setup_link_from_cert()
-
-        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, tx_window_size=1)
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM, req_config_options=config, rsp_config_options=config)
-
-        dut_channel.send(b'abc')
-        dut_channel.send(b'def')
-
-        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=0, payload=b'abc'))
-        assertThat(cert_channel).emitsNone(
-            L2capMatchers.IFrame(tx_seq=1, payload=b'abc'), timeout=timedelta(seconds=0.5))
-
-        cert_channel.send_i_frame(tx_seq=0, req_seq=1, payload=SAMPLE_PACKET)
-
-        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=1, payload=b'def'))
-
-        cert_channel.send_i_frame(tx_seq=1, req_seq=2, payload=SAMPLE_PACKET)
-
-    @metadata(pts_test_id="L2CAP/ERM/BV-07-C", pts_test_name="Send S-Frame [RNR]")
-    def test_send_s_frame_rnr(self):
-        """
-        Verify the IUT sends an S-frame [RNR] when it detects local busy condition
-        NOTE: In GD stack, we enter local busy condition if client doesn't dequeue
-        and packets are accumulating in buffer
-        """
-        self._setup_link_from_cert()
-
-        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, tx_window_size=10)
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM,
-            fcs=FcsType.NO_FCS,
-            req_config_options=config,
-            rsp_config_options=config)
-
-        dut_channel.send(b'abc')
-        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=0, payload=b'abc'))
-        cert_channel.send_i_frame(tx_seq=0, req_seq=1, payload=SAMPLE_PACKET)
-
-        dut_channel.set_traffic_paused(True)
-
-        # Allow 1 additional packet in channel queue buffer
-        buffer_size = self.dut_l2cap.get_channel_queue_buffer_size() + 1
-
-        for i in range(buffer_size):
-            cert_channel.send_i_frame(tx_seq=i + 1, req_seq=1, payload=SAMPLE_PACKET)
-            assertThat(cert_channel).emits(L2capMatchers.SFrame(s=l2cap_packets.SupervisoryFunction.RECEIVER_READY))
-
-        cert_channel.send_i_frame(tx_seq=buffer_size + 1, req_seq=1, payload=SAMPLE_PACKET)
-        assertThat(cert_channel).emits(L2capMatchers.SFrame(s=l2cap_packets.SupervisoryFunction.RECEIVER_NOT_READY))
-
-    @metadata(pts_test_id="L2CAP/ERM/BV-08-C", pts_test_name="Send S-Frame [RR] with Poll Bit Set")
-    def test_transmit_s_frame_rr_with_poll_bit_set(self):
-        """
-        Verify the IUT sends an S-frame [RR] with the Poll bit set when its
-        retransmission timer expires.
-        """
-        self._setup_link_from_cert()
-
-        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, retransmission_time_out=1500)
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM,
-            fcs=FcsType.NO_FCS,
-            req_config_options=config,
-            rsp_config_options=config)
-
-        dut_channel.send(b'abc')
-        assertThat(cert_channel).emits(L2capMatchers.SFrame(p=l2cap_packets.Poll.POLL))
-
-    @metadata(pts_test_id="L2CAP/ERM/BV-09-C", pts_test_name="Send S-Frame [RR] with Final Bit Set")
-    def test_transmit_s_frame_rr_with_final_bit_set(self):
-        """
-        Verify the IUT responds with an S-frame [RR] with the Final bit set
-        after receiving an S-frame [RR] with the Poll bit set
-        """
-        self._setup_link_from_cert()
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM, fcs=FcsType.NO_FCS)
-
-        cert_channel.send_s_frame(req_seq=0, p=Poll.POLL)
-        assertThat(cert_channel).emits(L2capMatchers.SFrame(f=Final.POLL_RESPONSE))
-
-    @metadata(pts_test_id="L2CAP/ERM/BV-10-C", pts_test_name="Retransmit S-Frame [RR] with Final Bit Set")
-    def test_retransmit_s_frame_rr_with_poll_bit_set(self):
-        """
-        Verify the IUT will retransmit the S-frame [RR] with the Poll bit set
-        when the Monitor Timer expires
-        """
-        self._setup_link_from_cert()
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM, fcs=FcsType.NO_FCS)
-        dut_channel.send(b'abc')
-
-        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=0, payload=b'abc'))
-        assertThat(cert_channel).emits(L2capMatchers.SFrame(req_seq=0, p=Poll.POLL, f=Final.NOT_SET))
-        cert_channel.send_s_frame(req_seq=1, f=Final.POLL_RESPONSE)
-
-    @metadata(pts_test_id="L2CAP/ERM/BV-11-C", pts_test_name="S-Frame Transmissions Exceed MaxTransmit")
-    def test_s_frame_transmissions_exceed_max_transmit(self):
-        """
-        Verify the IUT will close the channel when the Monitor Timer expires.
-        """
-        self._setup_link_from_cert()
-
-        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, tx_window_size=1, max_transmit=1, monitor_time_out=10)
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM, req_config_options=config, rsp_config_options=config)
-
-        dut_channel.send(b'abc')
-        cert_channel.verify_disconnect_request()
-
-    @metadata(pts_test_id="L2CAP/ERM/BV-12-C", pts_test_name="I-Frame Transmissions Exceed MaxTransmit")
-    def test_i_frame_transmissions_exceed_max_transmit(self):
-        """
-        Verify the IUT will close the channel when it receives an S-frame [RR]
-        with the final bit set that does not acknowledge the previous I-frame
-        sent by the IUT
-        """
-        self._setup_link_from_cert()
-
-        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, tx_window_size=1, max_transmit=1)
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM, req_config_options=config, rsp_config_options=config)
-
-        dut_channel.send(b'abc')
-        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=0), L2capMatchers.SFrame(p=Poll.POLL)).inOrder()
-
-        cert_channel.send_s_frame(req_seq=0, f=Final.POLL_RESPONSE)
-        cert_channel.verify_disconnect_request()
-
-    @metadata(pts_test_id="L2CAP/ERM/BV-13-C", pts_test_name="Respond to S-Frame [REJ]")
-    def test_respond_to_rej(self):
-        """
-        Verify the IUT retransmits I-frames starting from the sequence number
-        specified in the S-frame [REJ]
-        """
-        self._setup_link_from_cert()
-
-        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, tx_window_size=2, max_transmit=2)
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM, req_config_options=config, rsp_config_options=config)
-
-        dut_channel.send(b'abc')
-        dut_channel.send(b'abc')
-        assertThat(cert_channel).emits(
-            L2capMatchers.IFrame(tx_seq=0, payload=b'abc'), L2capMatchers.IFrame(tx_seq=1, payload=b'abc')).inOrder()
-
-        cert_channel.send_s_frame(req_seq=0, s=SupervisoryFunction.REJECT)
-
-        assertThat(cert_channel).emits(
-            L2capMatchers.IFrame(tx_seq=0, payload=b'abc'), L2capMatchers.IFrame(tx_seq=1, payload=b'abc')).inOrder()
-
-    @metadata(pts_test_id="L2CAP/ERM/BV-14-C", pts_test_name="Respond to S-Frame [SREJ] POLL Bit Set")
-    def test_respond_to_srej_p_set(self):
-        """
-        Verify the IUT responds with the correct I-frame when sent an SREJ
-        frame. Verify that the IUT processes the acknowledgment of previously
-        unacknowledged I-frames
-        """
-        self._setup_link_from_cert()
-
-        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, max_transmit=2, tx_window_size=3)
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM, req_config_options=config, rsp_config_options=config)
-
-        for _ in range(4):
-            dut_channel.send(b'abc')
-        assertThat(cert_channel).emits(
-            L2capMatchers.IFrame(tx_seq=0, payload=b'abc'), L2capMatchers.IFrame(tx_seq=1, payload=b'abc'),
-            L2capMatchers.IFrame(tx_seq=2, payload=b'abc')).inOrder()
-
-        cert_channel.send_s_frame(req_seq=1, p=Poll.POLL, s=SupervisoryFunction.SELECT_REJECT)
-
-        assertThat(cert_channel).emits(
-            L2capMatchers.IFrame(tx_seq=1, payload=b'abc', f=Final.POLL_RESPONSE),
-            L2capMatchers.IFrame(tx_seq=3, payload=b'abc')).inOrder()
-
-    @metadata(pts_test_id="L2CAP/ERM/BV-15-C", pts_test_name="Respond to S-Frame [SREJ] POLL Bit Clear")
-    def test_respond_to_srej_p_clear(self):
-        """
-        Verify the IUT responds with the correct I-frame when sent an SREJ frame
-        """
-        self._setup_link_from_cert()
-
-        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, max_transmit=2, tx_window_size=3)
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM, req_config_options=config, rsp_config_options=config)
-
-        for _ in range(4):
-            dut_channel.send(b'abc')
-        assertThat(cert_channel).emits(
-            L2capMatchers.IFrame(tx_seq=0, payload=b'abc'), L2capMatchers.IFrame(tx_seq=1, payload=b'abc'),
-            L2capMatchers.IFrame(tx_seq=2, payload=b'abc')).inOrder()
-
-        cert_channel.send_s_frame(req_seq=1, s=SupervisoryFunction.SELECT_REJECT)
-        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=1, payload=b'abc', f=Final.NOT_SET))
-        cert_channel.send_s_frame(req_seq=3, s=SupervisoryFunction.RECEIVER_READY)
-        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=3, payload=b'abc', f=Final.NOT_SET))
-
-    @metadata(pts_test_id="L2CAP/ERM/BV-16-C", pts_test_name="Send S-Frame [REJ]")
-    def test_send_s_frame_rej(self):
-        """
-        Verify the IUT can send an S-Frame [REJ] after receiving out of sequence
-        I-Frames
-        """
-        self._setup_link_from_cert()
-        tx_window_size = 4
-
-        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, tx_window_size=tx_window_size)
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM, req_config_options=config, rsp_config_options=config)
-
-        cert_channel.send_i_frame(tx_seq=0, req_seq=0, f=Final.NOT_SET, payload=SAMPLE_PACKET)
-        cert_channel.send_i_frame(tx_seq=2, req_seq=0, f=Final.NOT_SET, payload=SAMPLE_PACKET)
-
-        assertThat(cert_channel).emits(
-            L2capMatchers.SFrame(req_seq=1, f=Final.NOT_SET, s=SupervisoryFunction.REJECT, p=Poll.NOT_SET))
-
-        for i in range(1, tx_window_size):
-            cert_channel.send_i_frame(tx_seq=i, req_seq=0, f=Final.NOT_SET, payload=SAMPLE_PACKET)
-        assertThat(cert_channel).emits(
-            L2capMatchers.SFrame(req_seq=i + 1, f=Final.NOT_SET, s=SupervisoryFunction.RECEIVER_READY))
-
-    @metadata(pts_test_id="L2CAP/ERM/BV-18-C", pts_test_name="Receive S-Frame [RR] Final Bit = 1")
-    def test_receive_s_frame_rr_final_bit_set(self):
-        """
-        Verify the IUT will retransmit any previously sent I-frames
-        unacknowledged by receipt of an S-Frame [RR] with the Final Bit set
-        """
-        self._setup_link_from_cert()
-
-        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, retransmission_time_out=1500)
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM, req_config_options=config, rsp_config_options=config)
-
-        dut_channel.send(b'abc')
-
-        assertThat(cert_channel).emits(L2capMatchers.SFrame(p=l2cap_packets.Poll.POLL))
-
-        cert_channel.send_s_frame(req_seq=0, f=Final.POLL_RESPONSE)
-        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=0))
-
-    @metadata(pts_test_id="L2CAP/ERM/BV-19-C", pts_test_name="Receive I-Frame Final Bit = 1")
-    def test_receive_i_frame_final_bit_set(self):
-        """
-        Verify the IUT will retransmit any previously sent I-frames
-        unacknowledged by receipt of an I-frame with the final bit set
-        """
-        self._setup_link_from_cert()
-
-        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, retransmission_time_out=1500)
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM, req_config_options=config, rsp_config_options=config)
-
-        dut_channel.send(b'abc')
-        assertThat(cert_channel).emits(L2capMatchers.SFrame(p=Poll.POLL))
-
-        cert_channel.send_i_frame(tx_seq=0, req_seq=0, f=Final.POLL_RESPONSE, payload=SAMPLE_PACKET)
-        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=0))
-
-    @metadata(pts_test_id="L2CAP/ERM/BV-20-C", pts_test_name="Enter Remote Busy Condition")
-    def test_receive_rnr(self):
-        """
-        Verify the IUT will not retransmit any I-frames when it receives a
-        remote busy indication from the Lower Tester (S-frame [RNR])
-        """
-        self._setup_link_from_cert()
-
-        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, retransmission_time_out=1500)
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM, req_config_options=config, rsp_config_options=config)
-
-        dut_channel.send(b'abc')
-        assertThat(cert_channel).emits(L2capMatchers.SFrame(p=l2cap_packets.Poll.POLL))
-
-        cert_channel.send_s_frame(req_seq=0, s=SupervisoryFunction.RECEIVER_NOT_READY, f=Final.POLL_RESPONSE)
-        assertThat(cert_channel).emitsNone(L2capMatchers.IFrame(tx_seq=0))
-
-    @metadata(pts_test_id="L2CAP/ERM/BV-22-C", pts_test_name="Exit Local Busy Condition")
-    def test_exit_local_busy_condition(self):
-        """
-        Verify the IUT sends an S-frame [RR] Poll = 1 when the local busy condition is cleared
-        NOTE: In GD stack, we enter local busy condition if client doesn't dequeue
-        and packets are accumulating in buffer
-        """
-        self._setup_link_from_cert()
-
-        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, tx_window_size=10)
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM,
-            fcs=FcsType.NO_FCS,
-            req_config_options=config,
-            rsp_config_options=config)
-
-        dut_channel.send(b'abc')
-        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=0, payload=b'abc'))
-        cert_channel.send_i_frame(tx_seq=0, req_seq=1, payload=SAMPLE_PACKET)
-
-        dut_channel.set_traffic_paused(True)
-
-        # Allow 1 additional packet in channel queue buffer
-        buffer_size = self.dut_l2cap.get_channel_queue_buffer_size() + 1
-
-        for i in range(buffer_size):
-            cert_channel.send_i_frame(tx_seq=i + 1, req_seq=1, payload=SAMPLE_PACKET)
-            assertThat(cert_channel).emits(L2capMatchers.SFrame(s=l2cap_packets.SupervisoryFunction.RECEIVER_READY))
-
-        cert_channel.send_i_frame(tx_seq=buffer_size + 1, req_seq=1, payload=SAMPLE_PACKET)
-        assertThat(cert_channel).emits(L2capMatchers.SFrame(s=l2cap_packets.SupervisoryFunction.RECEIVER_NOT_READY))
-
-        dut_channel.set_traffic_paused(False)
-        assertThat(cert_channel).emits(
-            L2capMatchers.SFrame(s=l2cap_packets.SupervisoryFunction.RECEIVER_READY, p=l2cap_packets.Poll.POLL))
-        cert_channel.send_s_frame(1, f=l2cap_packets.Final.POLL_RESPONSE)
-
-    @metadata(pts_test_id="L2CAP/ERM/BV-23-C", pts_test_name="Transmit I-Frames using SAR")
-    def test_transmit_i_frames_using_sar(self):
-        """
-        Verify the IUT can send correctly formatted sequential I-frames with
-        valid values for the enhanced control fields (SAR, F-bit, ReqSeq,
-        TxSeq) when performing SAR.
-        """
-        self._setup_link_from_cert()
-
-        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, mps=11)
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM, req_config_options=config, rsp_config_options=config)
-
-        dut_channel.send(b'abcabcabc')
-        # First IFrame should contain SDU size after control field
-        assertThat(cert_channel).emits(
-            L2capMatchers.IFrameStart(tx_seq=0, payload=b'abc'), L2capMatchers.IFrame(tx_seq=1, payload=b'abc'),
-            L2capMatchers.IFrame(tx_seq=2, payload=b'abc')).inOrder()
-
-        cert_channel.send_s_frame(req_seq=3, s=SupervisoryFunction.RECEIVER_READY)
-
-        dut_channel.send(b'defdefdef')
-        # First IFrame should contain SDU size after control field
-        assertThat(cert_channel).emits(
-            L2capMatchers.IFrameStart(tx_seq=3, payload=b'def'), L2capMatchers.IFrame(tx_seq=4, payload=b'def'),
-            L2capMatchers.IFrame(tx_seq=5, payload=b'def')).inOrder()
-
-    @metadata(pts_test_id="L2CAP/ERM/BI-01-C", pts_test_name="S-Frame [REJ] Lost or Corrupted")
-    def test_sent_rej_lost(self):
-        """
-        Verify the IUT can handle receipt of an S-=frame [RR] Poll = 1 if the
-        S-frame [REJ] sent from the IUT is lost
-        """
-        self._setup_link_from_cert()
-        ertm_tx_window_size = 5
-
-        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, tx_window_size=ertm_tx_window_size)
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM, req_config_options=config, rsp_config_options=config)
-
-        cert_channel.send_i_frame(tx_seq=0, req_seq=0, payload=SAMPLE_PACKET)
-        assertThat(cert_channel).emits(L2capMatchers.SFrame(req_seq=1))
-
-        cert_channel.send_i_frame(tx_seq=ertm_tx_window_size - 1, req_seq=0, payload=SAMPLE_PACKET)
-        assertThat(cert_channel).emits(L2capMatchers.SFrame(s=SupervisoryFunction.REJECT))
-
-        cert_channel.send_s_frame(req_seq=0, p=Poll.POLL)
-
-        assertThat(cert_channel).emits(L2capMatchers.SFrame(req_seq=1, f=l2cap_packets.Final.POLL_RESPONSE))
-        for i in range(1, ertm_tx_window_size):
-            cert_channel.send_i_frame(tx_seq=i, req_seq=0, payload=SAMPLE_PACKET)
-            assertThat(cert_channel).emits(L2capMatchers.SFrame(req_seq=i + 1))
-
-    @metadata(pts_test_id="L2CAP/ERM/BI-03-C", pts_test_name="Handle Duplicate S-Frame [SREJ]")
-    def test_handle_duplicate_srej(self):
-        """
-        Verify the IUT will only retransmit the requested I-frame once after
-        receiving a duplicate SREJ
-        """
-        self._setup_link_from_cert()
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM, fcs=FcsType.NO_FCS)
-
-        dut_channel.send(b'abc')
-        dut_channel.send(b'abc')
-        assertThat(cert_channel).emits(
-            L2capMatchers.IFrame(tx_seq=0), L2capMatchers.IFrame(tx_seq=1),
-            L2capMatchers.SFrame(p=Poll.POLL)).inOrder()
-
-        cert_channel.send_s_frame(req_seq=0, s=SupervisoryFunction.SELECT_REJECT)
-        assertThat(cert_channel).emitsNone(timeout=timedelta(seconds=0.5))
-
-        cert_channel.send_s_frame(req_seq=0, s=SupervisoryFunction.SELECT_REJECT, f=Final.POLL_RESPONSE)
-        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=0))
-
-    @metadata(
-        pts_test_id="L2CAP/ERM/BI-04-C",
-        pts_test_name="Handle Receipt of S-Frame [REJ] and S-Frame "
-        "[RR, F=1] that Both Require Retransmission of the "
-        "Same I-Frames")
-    def test_handle_receipt_rej_and_rr_with_f_set(self):
-        """
-        Verify the IUT will only retransmit the requested I-frames once after
-        receiving an S-frame [REJ] followed by an S-frame [RR] with the Final
-        bit set that indicates the same I-frames should be retransmitted
-        """
-        self._setup_link_from_cert()
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM, fcs=FcsType.NO_FCS)
-
-        dut_channel.send(b'abc')
-        dut_channel.send(b'abc')
-        assertThat(cert_channel).emits(
-            L2capMatchers.IFrame(tx_seq=0),
-            L2capMatchers.IFrame(tx_seq=1),
-            L2capMatchers.SFrame(p=l2cap_packets.Poll.POLL)).inOrder()
-
-        cert_channel.send_s_frame(req_seq=0, s=SupervisoryFunction.REJECT)
-        assertThat(cert_channel).emitsNone(timeout=timedelta(seconds=0.5))
-
-        # Send RR with F set
-        cert_channel.send_s_frame(req_seq=0, s=SupervisoryFunction.REJECT, f=Final.POLL_RESPONSE)
-        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=0))
-        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=1))
-
-    @metadata(
-        pts_test_id="L2CAP/ERM/BI-05-C",
-        pts_test_name="Handle receipt of S-Frame [REJ] and I-Frame [F=1] "
-        "that Both Require Retransmission of the Same "
-        "I-Frames")
-    def test_handle_rej_and_i_frame_with_f_set(self):
-        """
-        Verify the IUT will only retransmit the requested I-frames once after
-        receiving an S-frame [REJ] followed by an I-frame with the Final bit
-        set that indicates the same I-frames should be retransmitted
-        """
-        self._setup_link_from_cert()
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM, fcs=FcsType.NO_FCS)
-
-        dut_channel.send(b'abc')
-        dut_channel.send(b'abc')
-        assertThat(cert_channel).emits(
-            L2capMatchers.IFrame(tx_seq=0),
-            L2capMatchers.IFrame(tx_seq=1),
-            L2capMatchers.SFrame(p=l2cap_packets.Poll.POLL)).inOrder()
-
-        # Send SREJ with F not set
-        cert_channel.send_s_frame(req_seq=0, s=SupervisoryFunction.SELECT_REJECT)
-        assertThat(cert_channel).emitsNone(timeout=timedelta(seconds=0.5))
-
-        cert_channel.send_i_frame(tx_seq=0, req_seq=0, f=Final.POLL_RESPONSE, payload=SAMPLE_PACKET)
-
-        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=0))
-        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=1))
-
-    @metadata(
-        pts_test_id="L2CAP/CMC/BV-01-C", pts_test_name="IUT Initiated Configuration of Enhanced "
-        "Retransmission Mode")
-    def test_initiated_configuration_request_ertm(self):
-        """
-        Verify the IUT can send a Configuration Request command containing the
-        F&EC option that specifies Enhanced Retransmission Mode
-        """
-        self._setup_link_from_cert()
-
-        self._open_unconfigured_channel_from_cert(scid=0x41, psm=0x33, mode=RetransmissionFlowControlMode.ERTM)
-
-        assertThat(self.cert_l2cap.get_control_channel()).emits(L2capMatchers.ConfigurationRequestWithErtm())
-
-    @metadata(
-        pts_test_id="L2CAP/CMC/BV-02-C",
-        pts_test_name="Lower Tester Initiated Configuration of Enhanced "
-        "Retransmission Mode")
-    def test_respond_configuration_request_ertm(self):
-        """
-        Verify the IUT can accept a Configuration Request from the Lower Tester
-        containing an F&EC option that specifies Enhanced Retransmission Mode
-        """
-        self._setup_link_from_cert()
-
-        self._open_channel_from_dut(psm=0x33, mode=RetransmissionFlowControlMode.ERTM)
-
-    @metadata(
-        pts_test_id="L2CAP/CMC/BV-12-C",
-        pts_test_name="ERTM Not Supported by Lower Tester for Mandatory "
-        "ERTM channel")
-    def test_respond_not_support_ertm_when_using_mandatory_ertm(self):
-        """
-        The IUT is initiating connection of an L2CAP channel that mandates use
-        of ERTM. Verify the IUT will not attempt to configure the connection to
-        ERTM if the Lower Tester has not indicated support for ERTM in the
-        Information Response [Extended Features]
-        """
-        self._setup_link_from_cert()
-        self.cert_l2cap.claim_ertm_unsupported()
-        dut_channel_future = self.dut_l2cap.connect_dynamic_channel_to_cert(
-            psm=0x33, mode=RetransmissionFlowControlMode.ERTM)
-        assertThat(self.cert_l2cap.get_control_channel()).emitsNone(L2capMatchers.ConnectionRequest(0x33))
-
-    @metadata(
-        pts_test_id="L2CAP/CMC/BI-01-C",
-        pts_test_name="Failed Configuration of Enhanced Retransmission "
-        "Mode when use of the Mode is Mandatory]")
-    def test_config_respond_basic_mode_when_using_mandatory_ertm(self):
-        """
-        When creating a connection for a PSM that mandates the use of ERTM
-        verify the IUT can handle receipt (close the channel in accordance with
-        the specification) of a Configure Response indicating the peer L2CAP
-        entity doesn’t wish to use Enhanced Retransmission Mode (Configure
-        Response Result = Reject Unacceptable Parameters)
-        """
-
-        self._setup_link_from_cert()
-
-        (dut_channel, cert_channel) = self._open_channel_from_cert(
-            mode=RetransmissionFlowControlMode.ERTM,
-            req_config_options=CertL2cap.config_option_ertm(),
-            rsp_config_options=CertL2cap.config_option_basic_explicit())
-
-        cert_channel.verify_disconnect_request()
-
-    @metadata(
-        pts_test_id="L2CAP/CMC/BI-02-C",
-        pts_test_name="Configuration Mode mismatch when use of Enhanced "
-        "Retransmission Mode is Mandatory")
-    def test_config_request_basic_mode_when_using_mandatory_ertm(self):
-        """
-        When creating a connection for a PSM that mandates the use of ERTM,
-        verify the IUT will close the channel if the Lower Tester attempts to
-        configure Basic Mode.
-        """
-        self._setup_link_from_cert()
-
-        (dut_channel, cert_channel) = self._open_unconfigured_channel_from_cert(mode=RetransmissionFlowControlMode.ERTM)
-        cert_channel.send_configure_request(CertL2cap.config_option_basic_explicit())
-        cert_channel.verify_disconnect_request()
-
-    def test_initiate_connection_for_security(self):
-        """
-        This will test the PyL2cap API for initiating a connection for security
-        via the security api
-        """
-        self.dut.neighbor.EnablePageScan(neighbor_facade.EnableMsg(enabled=True))
-        self.cert.neighbor.EnablePageScan(neighbor_facade.EnableMsg(enabled=True))
-        self.dut_l2cap.initiate_connection_for_security()
-        self.cert_l2cap.accept_incoming_connection()
-        self.dut_l2cap.verify_security_connection()
+        L2capTestBase.teardown_test(self)
+        GdBaseTestClass.teardown_test(self)
diff --git a/gd/l2cap/classic/cert/l2cap_test_lib.py b/gd/l2cap/classic/cert/l2cap_test_lib.py
new file mode 100644
index 0000000..7c1ec22
--- /dev/null
+++ b/gd/l2cap/classic/cert/l2cap_test_lib.py
@@ -0,0 +1,1284 @@
+#
+#   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.
+
+from datetime import timedelta
+
+from mobly import asserts
+
+from bluetooth_packets_python3 import l2cap_packets
+from bluetooth_packets_python3 import RawBuilder
+from bluetooth_packets_python3.l2cap_packets import FcsType
+from bluetooth_packets_python3.l2cap_packets import Final
+from bluetooth_packets_python3.l2cap_packets import Poll
+from bluetooth_packets_python3.l2cap_packets import SegmentationAndReassembly
+from bluetooth_packets_python3.l2cap_packets import SupervisoryFunction
+from cert.behavior import when, anything, wait_until
+from cert.event_stream import EventStream
+from cert.matchers import L2capMatchers
+from cert.metadata import metadata
+from cert.py_l2cap import PyL2cap
+from cert.truth import assertThat
+from facade import common_pb2
+from google.protobuf import empty_pb2 as empty_proto
+from l2cap.classic.cert.cert_l2cap import CertL2cap
+from l2cap.classic.facade_pb2 import RetransmissionFlowControlMode
+from neighbor.facade import facade_pb2 as neighbor_facade
+
+# Assemble a sample packet.
+SAMPLE_PACKET_DATA = b"\x19\x26\x08\x17"
+SAMPLE_PACKET = RawBuilder([x for x in SAMPLE_PACKET_DATA])
+
+
+class GeneralL2capTestBase():
+
+    def setup_test(self, dut, cert):
+        self.dut = dut
+        self.cert = cert
+
+        self.dut_address = self.dut.hci_controller.GetMacAddressSimple()
+        cert_address = common_pb2.BluetoothAddress(
+            address=self.cert.controller_read_only_property.ReadLocalAddress(empty_proto.Empty()).address)
+
+        self.dut_l2cap = PyL2cap(self.dut, cert_address)
+        self.cert_l2cap = CertL2cap(self.cert)
+
+    def teardown_test(self):
+        self.cert_l2cap.close()
+        self.dut_l2cap.close()
+
+    def _setup_link_from_cert(self):
+        self.dut.neighbor.EnablePageScan(neighbor_facade.EnableMsg(enabled=True))
+        self.cert_l2cap.connect_acl(self.dut_address)
+
+    def _open_unconfigured_channel_from_cert(self,
+                                             signal_id=1,
+                                             scid=0x0101,
+                                             psm=0x33,
+                                             mode=RetransmissionFlowControlMode.BASIC,
+                                             fcs=None):
+
+        dut_channel = self.dut_l2cap.register_dynamic_channel(psm, mode)
+        cert_channel = self.cert_l2cap.open_channel(signal_id, psm, scid, fcs=fcs)
+
+        return (dut_channel, cert_channel)
+
+    def _open_channel_from_cert(self,
+                                signal_id=1,
+                                scid=0x0101,
+                                psm=0x33,
+                                mode=RetransmissionFlowControlMode.BASIC,
+                                fcs=None,
+                                req_config_options=None,
+                                rsp_config_options=None):
+        request_matcher = L2capMatchers.ConfigurationRequestView(scid)
+        if rsp_config_options is not None:
+            when(self.cert_l2cap).on_config_req(request_matcher).then().send_configuration_response(
+                options=rsp_config_options)
+        if rsp_config_options is None and fcs is not None:
+            when(self.cert_l2cap).on_config_req(request_matcher).then().send_configuration_response(
+                options=CertL2cap.config_option_ertm(fcs=fcs))
+
+        (dut_channel, cert_channel) = self._open_unconfigured_channel_from_cert(signal_id, scid, psm, mode, fcs)
+        if req_config_options is None:
+            req_config_options = CertL2cap.config_option_ertm(
+                fcs=fcs) if mode == RetransmissionFlowControlMode.ERTM else []
+
+        cert_channel.send_configure_request(req_config_options)
+
+        cert_channel.verify_configuration_response()
+
+        wait_until(self.cert_l2cap).on_config_req(request_matcher).times(1)
+
+        assertThat(cert_channel.is_configured()).isTrue()
+
+        return (dut_channel, cert_channel)
+
+    def _open_channel_from_dut(self, psm=0x33, mode=RetransmissionFlowControlMode.BASIC):
+        dut_channel_future = self.dut_l2cap.connect_dynamic_channel_to_cert(psm, mode)
+        cert_channel = self.cert_l2cap.verify_and_respond_open_channel_from_remote(psm)
+        dut_channel = dut_channel_future.get_channel()
+
+        cert_channel.verify_configuration_request_and_respond()
+        outgoing_config = []
+        if mode == RetransmissionFlowControlMode.ERTM:
+            outgoing_config = CertL2cap.config_option_ertm()
+        cert_channel.send_configure_request(outgoing_config)
+        cert_channel.verify_configuration_response()
+
+        return (dut_channel, cert_channel)
+
+
+class L2capTestBase(GeneralL2capTestBase):
+
+    def test_connect_dynamic_channel_and_send_data(self):
+        self._setup_link_from_cert()
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert()
+
+        dut_channel.send(b'abc')
+        assertThat(cert_channel).emits(L2capMatchers.Data(b'abc'))
+
+    def test_receive_packet_from_unknown_channel(self):
+        self._setup_link_from_cert()
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert(scid=0x41, psm=0x33)
+
+        i_frame = l2cap_packets.EnhancedInformationFrameBuilder(
+            0x99, 0, Final.NOT_SET, 1, l2cap_packets.SegmentationAndReassembly.UNSEGMENTED, SAMPLE_PACKET)
+        self.cert_l2cap.send_acl(i_frame)
+        assertThat(cert_channel).emitsNone(L2capMatchers.SFrame(req_seq=4), timeout=timedelta(seconds=1))
+
+    def test_open_two_channels(self):
+        self._setup_link_from_cert()
+
+        self._open_channel_from_cert(signal_id=1, scid=0x41, psm=0x41)
+        self._open_channel_from_cert(signal_id=2, scid=0x43, psm=0x43)
+
+    def test_connect_and_send_data_ertm_no_segmentation(self):
+        self._setup_link_from_cert()
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM, fcs=FcsType.NO_FCS)
+
+        dut_channel.send(b'abc' * 34)
+        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=0, payload=b'abc' * 34))
+
+        cert_channel.send_i_frame(tx_seq=0, req_seq=1, payload=SAMPLE_PACKET)
+        # todo verify received?
+
+    @metadata(pts_test_id="L2CAP/COS/CED/BV-01-C", pts_test_name="Request Connection")
+    def test_basic_operation_request_connection(self):
+        """
+        Verify that the IUT is able to request the connection establishment for
+        an L2CAP data channel and initiate the configuration procedure.
+        """
+        self._setup_link_from_cert()
+        (dut_channel, cert_channel) = self._open_channel_from_dut()
+
+    @metadata(pts_test_id="L2CAP/COS/CED/BV-03-C", pts_test_name="Send data")
+    def test_send_data(self):
+        """
+        Verify that the IUT is able to send DATA
+        """
+        self._setup_link_from_cert()
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert()
+        dut_channel.send(b'hello')
+        assertThat(cert_channel).emits(L2capMatchers.Data(b'hello'))
+
+    @metadata(pts_test_id="L2CAP/COS/CED/BV-04-C", pts_test_name="Disconnect")
+    def test_disconnect(self):
+        """
+        Verify that the IUT is able to disconnect the data channel
+        """
+        self._setup_link_from_cert()
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert()
+        dut_channel.close_channel()
+        cert_channel.verify_disconnect_request()
+
+    @metadata(pts_test_id="L2CAP/COS/CED/BV-05-C", pts_test_name="Accept connection")
+    def test_accept_connection(self):
+        """
+        Also verify that DUT can send 48 bytes PDU (minimal MTU)
+        """
+        self._setup_link_from_cert()
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert()
+        dut_channel.send(b'a' * 48)
+        assertThat(cert_channel).emits(L2capMatchers.Data(b'a' * 48))
+
+    @metadata(pts_test_id="L2CAP/COS/CED/BV-07-C", pts_test_name="Accept Disconnect")
+    def test_accept_disconnect(self):
+        """
+        Verify that the IUT is able to respond to the request to disconnect the
+        data channel
+        """
+        self._setup_link_from_cert()
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert()
+        cert_channel.disconnect_and_verify()
+
+    @metadata(pts_test_id="L2CAP/COS/CED/BV-08-C", pts_test_name="Disconnect on Timeout")
+    def test_disconnect_on_timeout(self):
+        """
+        Verify that the IUT disconnects the data channel and shuts down this
+        channel if no response occurs
+        """
+        self._setup_link_from_cert()
+
+        (dut_channel, cert_channel) = self._open_unconfigured_channel_from_cert()
+
+        assertThat(self.cert_l2cap.get_control_channel()).emitsNone(L2capMatchers.ConfigurationResponse())
+        # TODO: Verify that IUT sends disconnect request (not mandated)
+
+    @metadata(pts_test_id="L2CAP/COS/CED/BV-09-C", pts_test_name="Receive Multi-Command Packet")
+    def test_receive_multi_command_packet(self):
+        """
+        Verify that the IUT is able to receive more than one signaling command in one L2CAP
+        packet.
+        """
+        self._setup_link_from_cert()
+
+        psm = 0x33
+        self.dut_l2cap.connect_dynamic_channel_to_cert(psm)
+        self.cert_l2cap.verify_and_respond_open_channel_from_remote_and_send_config_req(psm)
+
+        assertThat(self.cert_l2cap.get_control_channel()).emits(L2capMatchers.ConfigurationResponse())
+
+    @metadata(pts_test_id="L2CAP/COS/CED/BV-11-C", pts_test_name="Configure MTU size")
+    def test_configure_mtu_size(self):
+        """
+        Verify that the IUT is able to configure the supported MTU size
+        """
+        self._setup_link_from_cert()
+        (dut_channel, cert_channel) = self._open_unconfigured_channel_from_cert()
+        cert_channel.send_configure_request(CertL2cap.config_option_mtu_explicit(672))
+        cert_channel.verify_configuration_request_and_respond()
+        # TODO: Probably remove verify_configuration_request_and_respond
+
+    @metadata(pts_test_id="L2CAP/COS/CFD/BV-01-C", pts_test_name="Continuation Flag")
+    def test_continuation_flag(self):
+        """
+        Verify the IUT is able to receive configuration requests that have the
+        continuation flag set
+        """
+        self._setup_link_from_cert()
+
+        (dut_channel, cert_channel) = self._open_unconfigured_channel_from_cert()
+
+        # Send configuration request with CONTINUE
+        mtu_opt = l2cap_packets.MtuConfigurationOption()
+        mtu_opt.mtu = 0x1234
+        cert_channel.send_configure_request([mtu_opt], 2, l2cap_packets.Continuation.CONTINUE)
+
+        flush_timeout_option = l2cap_packets.FlushTimeoutConfigurationOption()
+        flush_timeout_option.flush_timeout = 65535
+        cert_channel.send_configure_request([flush_timeout_option], 3, l2cap_packets.Continuation.END)
+
+        assertThat(self.cert_l2cap.get_control_channel()).emits(L2capMatchers.ConfigurationResponse(), at_least_times=2)
+
+    @metadata(pts_test_id="L2CAP/COS/CFD/BV-02-C", pts_test_name="Negotiation with Reject")
+    def test_retry_config_after_rejection(self):
+        """
+        Verify that the IUT is able to perform negotiation while the Lower
+        Tester rejects the proposed configuration parameter values
+        """
+        self._setup_link_from_cert()
+        scid = 0x41
+        when(self.cert_l2cap).on_config_req(
+            L2capMatchers.ConfigurationRequestView(scid)).then().send_configuration_response(
+                result=l2cap_packets.ConfigurationResponseResult.UNACCEPTABLE_PARAMETERS,
+                options=CertL2cap.config_option_mtu_explicit(200)).send_configuration_response(options=[])
+        (dut_channel, cert_channel) = self._open_unconfigured_channel_from_cert(scid=scid)
+
+        assertThat(self.cert_l2cap.get_control_channel()).emits(L2capMatchers.ConfigurationRequest(), at_least_times=2)
+
+    @metadata(pts_test_id="L2CAP/COS/CFD/BV-03-C", pts_test_name="Send Requested Options")
+    def test_send_requested_options(self):
+        """
+        Verify that the IUT can receive a configuration request with no options
+        and send the requested options to the Lower Tester
+        """
+        self._setup_link_from_cert()
+        (dut_channel, cert_channel) = self._open_channel_from_cert(scid=0x41, psm=0x33)
+
+        # TODO(hsz) implement me!
+
+    @metadata(pts_test_id="L2CAP/COS/CFD/BV-08-C", pts_test_name="Non-blocking Config Response")
+    def test_non_blocking_config_response(self):
+        """
+        Verify that the IUT does not block transmitting L2CAP_ConfigRsp while
+        waiting for L2CAP_ConfigRsp from the Lower Tester
+        """
+        self._setup_link_from_cert()
+
+        (dut_channel, cert_channel) = self._open_unconfigured_channel_from_cert()
+
+        cert_channel.send_configure_request([])
+        cert_channel.verify_configuration_response()
+        cert_channel.verify_configuration_request_and_respond()
+
+        # TODO(hsz) implement me!
+
+    @metadata(pts_test_id="L2CAP/COS/CFD/BV-09-C", pts_test_name="Mandatory 48 Byte MTU")
+    def test_mandatory_48_byte_mtu(self):
+        """
+        Verify that the IUT can support mandatory 48 byte MTU
+        """
+        self._setup_link_from_cert()
+        (dut_channel,
+         cert_channel) = self._open_channel_from_cert(req_config_options=CertL2cap.config_option_mtu_explicit(48))
+
+        dut_channel.send(b"a" * 44)
+        assertThat(cert_channel).emits(L2capMatchers.Data(b"a" * 44))
+
+    @metadata(pts_test_id="L2CAP/COS/CFD/BV-11-C", pts_test_name="Negotiation of Unsupported Parameter")
+    def test_negotiation_of_unsupported_parameter(self):
+        """
+        Verify that the IUT can negotiate when the Lower Tester proposes an unsupported configuration
+        parameter value.
+        """
+        self._setup_link_from_cert()
+        (dut_channel, cert_channel) = self._open_unconfigured_channel_from_cert()
+
+        cert_channel.send_configure_request(CertL2cap.config_option_mtu_explicit(20))
+        # Invalid because minimum is 48
+
+        cert_channel.verify_configuration_response(l2cap_packets.ConfigurationResponseResult.UNACCEPTABLE_PARAMETERS)
+
+    @metadata(pts_test_id="L2CAP/COS/CFD/BV-12-C", pts_test_name="Unknown Option Response")
+    def test_config_unknown_options_with_hint(self):
+        """
+        Verify that the IUT can give the appropriate error code when the Lower
+        Tester proposes any number of unknown options that are optional
+        NOTE: In GD stack, ExtendedWindowSizeOption in unsupported
+        """
+        self._setup_link_from_cert()
+
+        (dut_channel, cert_channel) = self._open_unconfigured_channel_from_cert()
+
+        unknown_opt_hint = l2cap_packets.ExtendedWindowSizeOption()
+        unknown_opt_hint.max_window_size = 20
+        unknown_opt_hint.is_hint = l2cap_packets.ConfigurationOptionIsHint.OPTION_IS_A_HINT
+
+        for i in range(10):
+            cert_channel.send_configure_request([unknown_opt_hint] * i)
+            cert_channel.verify_configuration_response(l2cap_packets.ConfigurationResponseResult.SUCCESS)
+
+    @metadata(pts_test_id="L2CAP/COS/CFD/BV-14-C", pts_test_name="Unknown Mandatory Options Request")
+    def test_unknown_mandatory_options_request(self):
+        """
+        Verify that the IUT can give the appropriate error code when the Lower
+        Tester proposes any number of unknown options where at least one is
+        mandatory.
+        Note: GD stack doesn't support extended window size. For other stacks,
+              we may need to use some other config option
+        """
+        self._setup_link_from_cert()
+
+        (dut_channel, cert_channel) = self._open_unconfigured_channel_from_cert(scid=0x41, psm=0x33)
+
+        unknown_opt = l2cap_packets.ExtendedWindowSizeOption()
+        unknown_opt.max_window_size = 20
+
+        unknown_opt_hint = l2cap_packets.ExtendedWindowSizeOption()
+        unknown_opt_hint.max_window_size = 20
+        unknown_opt_hint.is_hint = l2cap_packets.ConfigurationOptionIsHint.OPTION_IS_A_HINT
+
+        configuration_option_attempts = [[unknown_opt], [unknown_opt, unknown_opt_hint], [
+            unknown_opt, unknown_opt, unknown_opt
+        ], [unknown_opt, unknown_opt_hint, unknown_opt_hint,
+            unknown_opt], [unknown_opt_hint, unknown_opt_hint, unknown_opt_hint, unknown_opt_hint, unknown_opt], [
+                unknown_opt, unknown_opt_hint, unknown_opt_hint, unknown_opt, unknown_opt_hint, unknown_opt_hint
+            ], [unknown_opt, unknown_opt, unknown_opt, unknown_opt, unknown_opt, unknown_opt, unknown_opt], [
+                unknown_opt_hint, unknown_opt_hint, unknown_opt_hint, unknown_opt_hint, unknown_opt_hint,
+                unknown_opt_hint, unknown_opt_hint, unknown_opt
+            ], [
+                unknown_opt_hint, unknown_opt_hint, unknown_opt_hint, unknown_opt_hint, unknown_opt_hint,
+                unknown_opt_hint, unknown_opt_hint, unknown_opt, unknown_opt
+            ], [
+                unknown_opt_hint, unknown_opt_hint, unknown_opt_hint, unknown_opt_hint, unknown_opt_hint,
+                unknown_opt_hint, unknown_opt_hint, unknown_opt_hint, unknown_opt_hint, unknown_opt
+            ]]
+
+        for option_list in configuration_option_attempts:
+            cert_channel.send_configure_request(option_list)
+            cert_channel.verify_configuration_response(l2cap_packets.ConfigurationResponseResult.UNKNOWN_OPTIONS)
+
+    @metadata(pts_test_id="L2CAP/COS/ECH/BV-01-C", pts_test_name="Respond to Echo Request")
+    def test_respond_to_echo_request(self):
+        """
+        Verify that the IUT responds to an echo request.
+        """
+        self._setup_link_from_cert()
+        echo_request = l2cap_packets.EchoRequestBuilder(100, RawBuilder([1, 2, 3]))
+        self.cert_l2cap.get_control_channel().send(echo_request)
+
+        assertThat(self.cert_l2cap.get_control_channel()).emits(L2capMatchers.EchoResponse())
+
+    @metadata(pts_test_id="L2CAP/COS/CED/BI-01-C", pts_test_name="Reject Unknown Command")
+    def test_reject_unknown_command(self):
+        """
+        Verify that the IUT rejects an unknown signaling command
+        """
+        self._setup_link_from_cert()
+
+        # Command code ff, Signal id 01, size 0000
+        invalid_command_packet = RawBuilder([0xff, 0x01, 0x00, 0x00])
+        self.cert_l2cap.get_control_channel().send(invalid_command_packet)
+
+        assertThat(self.cert_l2cap.get_control_channel()).emits(L2capMatchers.CommandReject())
+
+    @metadata(pts_test_id="L2CAP/COS/IEX/BV-01-C", pts_test_name="Query for 1.2 Features")
+    def test_query_for_1_2_features(self):
+        """
+        Verify that the IUT transmits an information request command to solicit
+        if the remote device supports Specification 1.2 features.
+        """
+        self._setup_link_from_cert()
+        assertThat(self.cert_l2cap.get_control_channel()).emits(
+            L2capMatchers.InformationRequestWithType(
+                l2cap_packets.InformationRequestInfoType.EXTENDED_FEATURES_SUPPORTED))
+
+    @metadata(pts_test_id="L2CAP/COS/IEX/BV-02-C", pts_test_name="Respond with 1.2 Features")
+    def test_respond_with_1_2_features(self):
+        """
+        Verify that the IUT responds to an information request command
+        soliciting for Specification 1.2 features
+        """
+        self._setup_link_from_cert()
+        control_channel = self.cert_l2cap.get_control_channel()
+
+        control_channel.send_extended_features_request()
+
+        assertThat(control_channel).emits(L2capMatchers.InformationResponseExtendedFeatures())
+
+    @metadata(
+        pts_test_id="L2CAP/EXF/BV-01-C",
+        pts_test_name="Extended Features Information Response for "
+        "Enhanced Retransmission Mode")
+    def test_extended_feature_info_response_ertm(self):
+        """
+        Verify the IUT can format an Information Response for the information
+        type of Extended Features that correctly identifies that Enhanced
+        Retransmission Mode is locally supported
+        """
+        self._setup_link_from_cert()
+        control_channel = self.cert_l2cap.get_control_channel()
+
+        control_channel.send_extended_features_request()
+
+        assertThat(control_channel).emits(L2capMatchers.InformationResponseExtendedFeatures(supports_ertm=True))
+
+    @metadata(
+        pts_test_id="L2CAP/EXF/BV-02-C", pts_test_name="Extended Features Information Response for "
+        "Streaming Mode")
+    def test_extended_feature_info_response_streaming(self):
+        """
+        Verify the IUT can format an Information Response for the information
+        type of Extended Features that correctly identifies that Streaming Mode
+        is locally supported
+        """
+        asserts.skip("Streaming not supported")
+        self._setup_link_from_cert()
+        control_channel = self.cert_l2cap.get_control_channel()
+
+        control_channel.send_extended_features_request()
+
+        assertThat(control_channel).emits(L2capMatchers.InformationResponseExtendedFeatures(supports_streaming=True))
+
+    @metadata(pts_test_id="L2CAP/EXF/BV-03-C", pts_test_name="Extended Features Information Response for FCS " "Option")
+    def test_extended_feature_info_response_fcs(self):
+        """
+        Verify the IUT can format an Information Response for the information
+        type of Extended Features that correctly identifies that the FCS Option
+        is locally supported.
+
+        Note: This is not mandated by L2CAP Spec
+        """
+        self._setup_link_from_cert()
+        control_channel = self.cert_l2cap.get_control_channel()
+
+        control_channel.send_extended_features_request()
+
+        assertThat(control_channel).emits(L2capMatchers.InformationResponseExtendedFeatures(supports_fcs=True))
+
+    @metadata(
+        pts_test_id="L2CAP/EXF/BV-05-C", pts_test_name="Extended Features Information Response for Fixed "
+        "Channels")
+    def test_extended_feature_info_response_fixed_channels(self):
+        """
+        Verify the IUT can format an Information Response for the information
+        type of Extended Features that correctly identifies that the Fixed
+        Channels option is locally supported
+
+        Note: This is not mandated by L2CAP Spec
+        """
+        self._setup_link_from_cert()
+        control_channel = self.cert_l2cap.get_control_channel()
+
+        control_channel.send_extended_features_request()
+
+        assertThat(control_channel).emits(
+            L2capMatchers.InformationResponseExtendedFeatures(supports_fixed_channels=True))
+
+    @metadata(pts_test_id="L2CAP/FIX/BV-01-C", pts_test_name="Fixed Channels Supported Information Request")
+    def test_fixed_channels_supported_information_request(self):
+        """
+        Verify that the IUT can send an Information Request for the information
+        type of Fixed Channels Supported.
+        """
+        self._setup_link_from_cert()
+        assertThat(self.cert_l2cap.get_control_channel()).emits(
+            L2capMatchers.InformationRequestWithType(l2cap_packets.InformationRequestInfoType.FIXED_CHANNELS_SUPPORTED))
+
+    @metadata(pts_test_id="L2CAP/FOC/BV-01-C", pts_test_name="IUT Initiated Configuration of the FCS Option")
+    def test_config_channel_not_use_FCS(self):
+        """
+        Verify the IUT can configure a channel to not use FCS in I/S-frames.
+        """
+        self._setup_link_from_cert()
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM, fcs=FcsType.NO_FCS)
+
+        dut_channel.send(b'abc')
+        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=0, payload=b'abc'))
+
+    @metadata(pts_test_id="L2CAP/FOC/BV-02-C", pts_test_name="Lower Tester Explicitly Requests FCS should be " "Used")
+    def test_explicitly_request_use_FCS(self):
+        """
+        Verify the IUT will include the FCS in I/S-frames if the Lower Tester
+        explicitly requests that FCS should be used
+        """
+        self._setup_link_from_cert()
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM, fcs=FcsType.DEFAULT)
+
+        dut_channel.send(b'abc')
+        assertThat(cert_channel).emits(L2capMatchers.IFrameWithFcs(payload=b"abc"))
+
+    @metadata(pts_test_id="L2CAP/FOC/BV-03-C", pts_test_name="Lower Tester Implicitly Requests FCS should be " "Used")
+    def test_implicitly_request_use_FCS(self):
+        """
+        Verify the IUT will include the FCS in I/S-frames if the Lower Tester
+        implicitly requests that FCS should be used.
+        """
+        self._setup_link_from_cert()
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM,
+            fcs=FcsType.DEFAULT,
+            req_config_options=CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS))
+
+        dut_channel.send(b'abc')
+        assertThat(cert_channel).emits(L2capMatchers.IFrameWithFcs(payload=b"abc"))
+
+    @metadata(pts_test_id="L2CAP/OFS/BV-01-C", pts_test_name="Sending I-Frames without FCS for ERTM")
+    def test_sending_i_frames_without_fcs_for_ertm(self):
+        """
+        Verify the IUT does not include the FCS in I-frames.
+        """
+        self._setup_link_from_cert()
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM, fcs=FcsType.NO_FCS)
+
+        dut_channel.send(b'abc')
+        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=0, payload=b"abc"))
+
+    @metadata(pts_test_id="L2CAP/OFS/BV-02-C", pts_test_name="Receiving I-Frames without FCS for ERTM")
+    def test_receiving_i_frames_without_fcs_for_ertm(self):
+        """
+        Verify the IUT can handle I-frames that do not contain the FCS.
+        """
+        self._setup_link_from_cert()
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM, fcs=FcsType.NO_FCS)
+
+        cert_channel.send_i_frame(tx_seq=0, req_seq=0, payload=SAMPLE_PACKET)
+        assertThat(dut_channel).emits(L2capMatchers.PacketPayloadRawData(SAMPLE_PACKET_DATA))
+
+    @metadata(pts_test_id="L2CAP/OFS/BV-05-C", pts_test_name="Sending I-Frames with FCS for ERTM")
+    def test_sending_i_frames_with_fcs_for_ertm(self):
+        """
+        Verify the IUT does include the FCS in I-frames.
+        """
+        self._setup_link_from_cert()
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM, fcs=FcsType.DEFAULT)
+
+        dut_channel.send(b'abc')
+        assertThat(cert_channel).emits(L2capMatchers.IFrameWithFcs(tx_seq=0, payload=b"abc"))
+
+    @metadata(pts_test_id="L2CAP/OFS/BV-06-C", pts_test_name="Receiving I-Frames with FCS for ERTM")
+    def test_receiving_i_frames_with_fcs_for_ertm(self):
+        """
+        Verify the IUT can handle I-frames that do contain the FCS.
+        """
+        self._setup_link_from_cert()
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM, fcs=FcsType.DEFAULT)
+
+        cert_channel.send_i_frame(tx_seq=0, req_seq=0, payload=SAMPLE_PACKET, fcs=FcsType.DEFAULT)
+        assertThat(dut_channel).emits(L2capMatchers.PacketPayloadRawData(SAMPLE_PACKET_DATA))
+
+    @metadata(pts_test_id="L2CAP/ERM/BV-01-C", pts_test_name="Transmit I-frames")
+    def test_transmit_i_frames(self):
+        """
+        Verify the IUT can send correctly formatted sequential I-frames with
+        valid values for the enhanced control fields (SAR, F-bit, ReqSeq,
+        TxSeq)
+        """
+        self._setup_link_from_cert()
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM, fcs=FcsType.NO_FCS)
+
+        dut_channel.send(b'abc')
+        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=0, payload=b"abc"))
+
+        cert_channel.send_i_frame(tx_seq=0, req_seq=1, payload=SAMPLE_PACKET)
+
+        dut_channel.send(b'abc')
+        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=1, payload=b"abc"))
+
+        cert_channel.send_i_frame(tx_seq=1, req_seq=2, payload=SAMPLE_PACKET)
+
+        dut_channel.send(b'abc')
+        assertThat(cert_channel).emits(L2capMatchers.PartialData(b"abc"))
+
+        cert_channel.send_i_frame(tx_seq=2, req_seq=3, payload=SAMPLE_PACKET)
+
+    @metadata(pts_test_id="L2CAP/ERM/BV-02-C", pts_test_name="Receive I-Frames")
+    def test_receive_i_frames(self):
+        """
+        Verify the IUT can receive in-sequence valid I-frames and deliver L2CAP
+        SDUs to the Upper Tester
+        """
+        self._setup_link_from_cert()
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM, fcs=FcsType.NO_FCS)
+
+        for i in range(3):
+            cert_channel.send_i_frame(tx_seq=i, req_seq=0, payload=SAMPLE_PACKET)
+            assertThat(cert_channel).emits(L2capMatchers.SFrame(req_seq=i + 1))
+
+        cert_channel.send_i_frame(tx_seq=3, req_seq=0, sar=SegmentationAndReassembly.START, payload=SAMPLE_PACKET)
+        assertThat(cert_channel).emits(L2capMatchers.SFrame(req_seq=4))
+
+        cert_channel.send_i_frame(
+            tx_seq=4, req_seq=0, sar=SegmentationAndReassembly.CONTINUATION, payload=SAMPLE_PACKET)
+        assertThat(cert_channel).emits(L2capMatchers.SFrame(req_seq=5))
+
+        cert_channel.send_i_frame(tx_seq=5, req_seq=0, sar=SegmentationAndReassembly.END, payload=SAMPLE_PACKET)
+        assertThat(cert_channel).emits(L2capMatchers.SFrame(req_seq=6))
+
+    @metadata(pts_test_id="L2CAP/ERM/BV-03-C", pts_test_name="Acknowledging Received I-Frames")
+    def test_acknowledging_received_i_frames(self):
+        """
+        Verify the IUT sends S-frame [RR] with the Poll bit not set to
+        acknowledge data received from the Lower Tester
+        """
+        self._setup_link_from_cert()
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM, fcs=FcsType.NO_FCS)
+
+        for i in range(3):
+            cert_channel.send_i_frame(tx_seq=i, req_seq=0, payload=SAMPLE_PACKET)
+            assertThat(cert_channel).emits(L2capMatchers.SFrame(req_seq=i + 1))
+
+        assertThat(cert_channel).emitsNone(L2capMatchers.SFrame(req_seq=4), timeout=timedelta(seconds=1))
+
+    @metadata(
+        pts_test_id="L2CAP/ERM/BV-05-C",
+        pts_test_name="Resume Transmitting I-Frames when an S-Frame [RR] "
+        "is Received")
+    def test_resume_transmitting_when_received_rr(self):
+        """
+        Verify the IUT will cease transmission of I-frames when the negotiated
+        TxWindow is full. Verify the IUT will resume transmission of I-frames
+        when an S-frame [RR] is received that acknowledges previously sent
+        I-frames
+        """
+        self._setup_link_from_cert()
+
+        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, tx_window_size=1)
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM, req_config_options=config, rsp_config_options=config)
+
+        dut_channel.send(b'abc')
+        dut_channel.send(b'def')
+
+        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=0, payload=b'abc'))
+        assertThat(cert_channel).emitsNone(L2capMatchers.IFrame(tx_seq=1, payload=b'def'))
+
+        cert_channel.send_s_frame(req_seq=1, f=Final.POLL_RESPONSE)
+        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=1))
+
+    @metadata(
+        pts_test_id="L2CAP/ERM/BV-06-C", pts_test_name="Resume Transmitting I-Frames when an I-Frame is "
+        "Received")
+    def test_resume_transmitting_when_acknowledge_previously_sent(self):
+        """
+        Verify the IUT will cease transmission of I-frames when the negotiated
+        TxWindow is full. Verify the IUT will resume transmission of I-frames
+        when an I-frame is received that acknowledges previously sent I-frames
+        """
+        self._setup_link_from_cert()
+
+        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, tx_window_size=1)
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM, req_config_options=config, rsp_config_options=config)
+
+        dut_channel.send(b'abc')
+        dut_channel.send(b'def')
+
+        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=0, payload=b'abc'))
+        assertThat(cert_channel).emitsNone(
+            L2capMatchers.IFrame(tx_seq=1, payload=b'abc'), timeout=timedelta(seconds=0.5))
+
+        cert_channel.send_i_frame(tx_seq=0, req_seq=1, payload=SAMPLE_PACKET)
+
+        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=1, payload=b'def'))
+
+        cert_channel.send_i_frame(tx_seq=1, req_seq=2, payload=SAMPLE_PACKET)
+
+    @metadata(pts_test_id="L2CAP/ERM/BV-07-C", pts_test_name="Send S-Frame [RNR]")
+    def test_send_s_frame_rnr(self):
+        """
+        Verify the IUT sends an S-frame [RNR] when it detects local busy condition
+        NOTE: In GD stack, we enter local busy condition if client doesn't dequeue
+        and packets are accumulating in buffer
+        """
+        self._setup_link_from_cert()
+
+        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, tx_window_size=10)
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM,
+            fcs=FcsType.NO_FCS,
+            req_config_options=config,
+            rsp_config_options=config)
+
+        dut_channel.send(b'abc')
+        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=0, payload=b'abc'))
+        cert_channel.send_i_frame(tx_seq=0, req_seq=1, payload=SAMPLE_PACKET)
+
+        dut_channel.set_traffic_paused(True)
+
+        # Allow 1 additional packet in channel queue buffer
+        buffer_size = self.dut_l2cap.get_channel_queue_buffer_size() + 1
+
+        for i in range(buffer_size):
+            cert_channel.send_i_frame(tx_seq=i + 1, req_seq=1, payload=SAMPLE_PACKET)
+            assertThat(cert_channel).emits(L2capMatchers.SFrame(s=l2cap_packets.SupervisoryFunction.RECEIVER_READY))
+
+        cert_channel.send_i_frame(tx_seq=buffer_size + 1, req_seq=1, payload=SAMPLE_PACKET)
+        assertThat(cert_channel).emits(L2capMatchers.SFrame(s=l2cap_packets.SupervisoryFunction.RECEIVER_NOT_READY))
+
+    @metadata(pts_test_id="L2CAP/ERM/BV-08-C", pts_test_name="Send S-Frame [RR] with Poll Bit Set")
+    def test_transmit_s_frame_rr_with_poll_bit_set(self):
+        """
+        Verify the IUT sends an S-frame [RR] with the Poll bit set when its
+        retransmission timer expires.
+        """
+        self._setup_link_from_cert()
+
+        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, retransmission_time_out=1500)
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM,
+            fcs=FcsType.NO_FCS,
+            req_config_options=config,
+            rsp_config_options=config)
+
+        dut_channel.send(b'abc')
+        assertThat(cert_channel).emits(L2capMatchers.SFrame(p=l2cap_packets.Poll.POLL))
+
+    @metadata(pts_test_id="L2CAP/ERM/BV-09-C", pts_test_name="Send S-Frame [RR] with Final Bit Set")
+    def test_transmit_s_frame_rr_with_final_bit_set(self):
+        """
+        Verify the IUT responds with an S-frame [RR] with the Final bit set
+        after receiving an S-frame [RR] with the Poll bit set
+        """
+        self._setup_link_from_cert()
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM, fcs=FcsType.NO_FCS)
+
+        cert_channel.send_s_frame(req_seq=0, p=Poll.POLL)
+        assertThat(cert_channel).emits(L2capMatchers.SFrame(f=Final.POLL_RESPONSE))
+
+    @metadata(pts_test_id="L2CAP/ERM/BV-10-C", pts_test_name="Retransmit S-Frame [RR] with Final Bit Set")
+    def test_retransmit_s_frame_rr_with_poll_bit_set(self):
+        """
+        Verify the IUT will retransmit the S-frame [RR] with the Poll bit set
+        when the Monitor Timer expires
+        """
+        self._setup_link_from_cert()
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM, fcs=FcsType.NO_FCS)
+        dut_channel.send(b'abc')
+
+        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=0, payload=b'abc'))
+        assertThat(cert_channel).emits(L2capMatchers.SFrame(req_seq=0, p=Poll.POLL, f=Final.NOT_SET))
+        cert_channel.send_s_frame(req_seq=1, f=Final.POLL_RESPONSE)
+
+    @metadata(pts_test_id="L2CAP/ERM/BV-11-C", pts_test_name="S-Frame Transmissions Exceed MaxTransmit")
+    def test_s_frame_transmissions_exceed_max_transmit(self):
+        """
+        Verify the IUT will close the channel when the Monitor Timer expires.
+        """
+        self._setup_link_from_cert()
+
+        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, tx_window_size=1, max_transmit=1, monitor_time_out=10)
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM, req_config_options=config, rsp_config_options=config)
+
+        dut_channel.send(b'abc')
+        cert_channel.verify_disconnect_request()
+
+    @metadata(pts_test_id="L2CAP/ERM/BV-12-C", pts_test_name="I-Frame Transmissions Exceed MaxTransmit")
+    def test_i_frame_transmissions_exceed_max_transmit(self):
+        """
+        Verify the IUT will close the channel when it receives an S-frame [RR]
+        with the final bit set that does not acknowledge the previous I-frame
+        sent by the IUT
+        """
+        self._setup_link_from_cert()
+
+        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, tx_window_size=1, max_transmit=1)
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM, req_config_options=config, rsp_config_options=config)
+
+        dut_channel.send(b'abc')
+        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=0), L2capMatchers.SFrame(p=Poll.POLL)).inOrder()
+
+        cert_channel.send_s_frame(req_seq=0, f=Final.POLL_RESPONSE)
+        cert_channel.verify_disconnect_request()
+
+    @metadata(pts_test_id="L2CAP/ERM/BV-13-C", pts_test_name="Respond to S-Frame [REJ]")
+    def test_respond_to_rej(self):
+        """
+        Verify the IUT retransmits I-frames starting from the sequence number
+        specified in the S-frame [REJ]
+        """
+        self._setup_link_from_cert()
+
+        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, tx_window_size=2, max_transmit=2)
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM, req_config_options=config, rsp_config_options=config)
+
+        dut_channel.send(b'abc')
+        dut_channel.send(b'abc')
+        assertThat(cert_channel).emits(
+            L2capMatchers.IFrame(tx_seq=0, payload=b'abc'), L2capMatchers.IFrame(tx_seq=1, payload=b'abc')).inOrder()
+
+        cert_channel.send_s_frame(req_seq=0, s=SupervisoryFunction.REJECT)
+
+        assertThat(cert_channel).emits(
+            L2capMatchers.IFrame(tx_seq=0, payload=b'abc'), L2capMatchers.IFrame(tx_seq=1, payload=b'abc')).inOrder()
+
+    @metadata(pts_test_id="L2CAP/ERM/BV-14-C", pts_test_name="Respond to S-Frame [SREJ] POLL Bit Set")
+    def test_respond_to_srej_p_set(self):
+        """
+        Verify the IUT responds with the correct I-frame when sent an SREJ
+        frame. Verify that the IUT processes the acknowledgment of previously
+        unacknowledged I-frames
+        """
+        self._setup_link_from_cert()
+
+        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, max_transmit=2, tx_window_size=3)
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM, req_config_options=config, rsp_config_options=config)
+
+        for _ in range(4):
+            dut_channel.send(b'abc')
+        assertThat(cert_channel).emits(
+            L2capMatchers.IFrame(tx_seq=0, payload=b'abc'), L2capMatchers.IFrame(tx_seq=1, payload=b'abc'),
+            L2capMatchers.IFrame(tx_seq=2, payload=b'abc')).inOrder()
+
+        cert_channel.send_s_frame(req_seq=1, p=Poll.POLL, s=SupervisoryFunction.SELECT_REJECT)
+
+        assertThat(cert_channel).emits(
+            L2capMatchers.IFrame(tx_seq=1, payload=b'abc', f=Final.POLL_RESPONSE),
+            L2capMatchers.IFrame(tx_seq=3, payload=b'abc')).inOrder()
+
+    @metadata(pts_test_id="L2CAP/ERM/BV-15-C", pts_test_name="Respond to S-Frame [SREJ] POLL Bit Clear")
+    def test_respond_to_srej_p_clear(self):
+        """
+        Verify the IUT responds with the correct I-frame when sent an SREJ frame
+        """
+        self._setup_link_from_cert()
+
+        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, max_transmit=2, tx_window_size=3)
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM, req_config_options=config, rsp_config_options=config)
+
+        for _ in range(4):
+            dut_channel.send(b'abc')
+        assertThat(cert_channel).emits(
+            L2capMatchers.IFrame(tx_seq=0, payload=b'abc'), L2capMatchers.IFrame(tx_seq=1, payload=b'abc'),
+            L2capMatchers.IFrame(tx_seq=2, payload=b'abc')).inOrder()
+
+        cert_channel.send_s_frame(req_seq=1, s=SupervisoryFunction.SELECT_REJECT)
+        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=1, payload=b'abc', f=Final.NOT_SET))
+        cert_channel.send_s_frame(req_seq=3, s=SupervisoryFunction.RECEIVER_READY)
+        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=3, payload=b'abc', f=Final.NOT_SET))
+
+    @metadata(pts_test_id="L2CAP/ERM/BV-16-C", pts_test_name="Send S-Frame [REJ]")
+    def test_send_s_frame_rej(self):
+        """
+        Verify the IUT can send an S-Frame [REJ] after receiving out of sequence
+        I-Frames
+        """
+        self._setup_link_from_cert()
+        tx_window_size = 4
+
+        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, tx_window_size=tx_window_size)
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM, req_config_options=config, rsp_config_options=config)
+
+        cert_channel.send_i_frame(tx_seq=0, req_seq=0, f=Final.NOT_SET, payload=SAMPLE_PACKET)
+        cert_channel.send_i_frame(tx_seq=2, req_seq=0, f=Final.NOT_SET, payload=SAMPLE_PACKET)
+
+        assertThat(cert_channel).emits(
+            L2capMatchers.SFrame(req_seq=1, f=Final.NOT_SET, s=SupervisoryFunction.REJECT, p=Poll.NOT_SET))
+
+        for i in range(1, tx_window_size):
+            cert_channel.send_i_frame(tx_seq=i, req_seq=0, f=Final.NOT_SET, payload=SAMPLE_PACKET)
+        assertThat(cert_channel).emits(
+            L2capMatchers.SFrame(req_seq=i + 1, f=Final.NOT_SET, s=SupervisoryFunction.RECEIVER_READY))
+
+    @metadata(pts_test_id="L2CAP/ERM/BV-18-C", pts_test_name="Receive S-Frame [RR] Final Bit = 1")
+    def test_receive_s_frame_rr_final_bit_set(self):
+        """
+        Verify the IUT will retransmit any previously sent I-frames
+        unacknowledged by receipt of an S-Frame [RR] with the Final Bit set
+        """
+        self._setup_link_from_cert()
+
+        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, retransmission_time_out=1500)
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM, req_config_options=config, rsp_config_options=config)
+
+        dut_channel.send(b'abc')
+
+        assertThat(cert_channel).emits(L2capMatchers.SFrame(p=l2cap_packets.Poll.POLL))
+
+        cert_channel.send_s_frame(req_seq=0, f=Final.POLL_RESPONSE)
+        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=0))
+
+    @metadata(pts_test_id="L2CAP/ERM/BV-19-C", pts_test_name="Receive I-Frame Final Bit = 1")
+    def test_receive_i_frame_final_bit_set(self):
+        """
+        Verify the IUT will retransmit any previously sent I-frames
+        unacknowledged by receipt of an I-frame with the final bit set
+        """
+        self._setup_link_from_cert()
+
+        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, retransmission_time_out=1500)
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM, req_config_options=config, rsp_config_options=config)
+
+        dut_channel.send(b'abc')
+        assertThat(cert_channel).emits(L2capMatchers.SFrame(p=Poll.POLL))
+
+        cert_channel.send_i_frame(tx_seq=0, req_seq=0, f=Final.POLL_RESPONSE, payload=SAMPLE_PACKET)
+        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=0))
+
+    @metadata(pts_test_id="L2CAP/ERM/BV-20-C", pts_test_name="Enter Remote Busy Condition")
+    def test_receive_rnr(self):
+        """
+        Verify the IUT will not retransmit any I-frames when it receives a
+        remote busy indication from the Lower Tester (S-frame [RNR])
+        """
+        self._setup_link_from_cert()
+
+        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, retransmission_time_out=1500)
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM, req_config_options=config, rsp_config_options=config)
+
+        dut_channel.send(b'abc')
+        assertThat(cert_channel).emits(L2capMatchers.SFrame(p=l2cap_packets.Poll.POLL))
+
+        cert_channel.send_s_frame(req_seq=0, s=SupervisoryFunction.RECEIVER_NOT_READY, f=Final.POLL_RESPONSE)
+        assertThat(cert_channel).emitsNone(L2capMatchers.IFrame(tx_seq=0))
+
+    @metadata(pts_test_id="L2CAP/ERM/BV-22-C", pts_test_name="Exit Local Busy Condition")
+    def test_exit_local_busy_condition(self):
+        """
+        Verify the IUT sends an S-frame [RR] Poll = 1 when the local busy condition is cleared
+        NOTE: In GD stack, we enter local busy condition if client doesn't dequeue
+        and packets are accumulating in buffer
+        """
+        self._setup_link_from_cert()
+
+        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, tx_window_size=10)
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM,
+            fcs=FcsType.NO_FCS,
+            req_config_options=config,
+            rsp_config_options=config)
+
+        dut_channel.send(b'abc')
+        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=0, payload=b'abc'))
+        cert_channel.send_i_frame(tx_seq=0, req_seq=1, payload=SAMPLE_PACKET)
+
+        dut_channel.set_traffic_paused(True)
+
+        # Allow 1 additional packet in channel queue buffer
+        buffer_size = self.dut_l2cap.get_channel_queue_buffer_size() + 1
+
+        for i in range(buffer_size):
+            cert_channel.send_i_frame(tx_seq=i + 1, req_seq=1, payload=SAMPLE_PACKET)
+            assertThat(cert_channel).emits(L2capMatchers.SFrame(s=l2cap_packets.SupervisoryFunction.RECEIVER_READY))
+
+        cert_channel.send_i_frame(tx_seq=buffer_size + 1, req_seq=1, payload=SAMPLE_PACKET)
+        assertThat(cert_channel).emits(L2capMatchers.SFrame(s=l2cap_packets.SupervisoryFunction.RECEIVER_NOT_READY))
+
+        dut_channel.set_traffic_paused(False)
+        assertThat(cert_channel).emits(
+            L2capMatchers.SFrame(s=l2cap_packets.SupervisoryFunction.RECEIVER_READY, p=l2cap_packets.Poll.POLL))
+        cert_channel.send_s_frame(1, f=l2cap_packets.Final.POLL_RESPONSE)
+
+    @metadata(pts_test_id="L2CAP/ERM/BV-23-C", pts_test_name="Transmit I-Frames using SAR")
+    def test_transmit_i_frames_using_sar(self):
+        """
+        Verify the IUT can send correctly formatted sequential I-frames with
+        valid values for the enhanced control fields (SAR, F-bit, ReqSeq,
+        TxSeq) when performing SAR.
+        """
+        self._setup_link_from_cert()
+
+        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, mps=11)
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM, req_config_options=config, rsp_config_options=config)
+
+        dut_channel.send(b'abcabcabc')
+        # First IFrame should contain SDU size after control field
+        assertThat(cert_channel).emits(
+            L2capMatchers.IFrameStart(tx_seq=0, payload=b'abc'), L2capMatchers.IFrame(tx_seq=1, payload=b'abc'),
+            L2capMatchers.IFrame(tx_seq=2, payload=b'abc')).inOrder()
+
+        cert_channel.send_s_frame(req_seq=3, s=SupervisoryFunction.RECEIVER_READY)
+
+        dut_channel.send(b'defdefdef')
+        # First IFrame should contain SDU size after control field
+        assertThat(cert_channel).emits(
+            L2capMatchers.IFrameStart(tx_seq=3, payload=b'def'), L2capMatchers.IFrame(tx_seq=4, payload=b'def'),
+            L2capMatchers.IFrame(tx_seq=5, payload=b'def')).inOrder()
+
+    @metadata(pts_test_id="L2CAP/ERM/BI-01-C", pts_test_name="S-Frame [REJ] Lost or Corrupted")
+    def test_sent_rej_lost(self):
+        """
+        Verify the IUT can handle receipt of an S-=frame [RR] Poll = 1 if the
+        S-frame [REJ] sent from the IUT is lost
+        """
+        self._setup_link_from_cert()
+        ertm_tx_window_size = 5
+
+        config = CertL2cap.config_option_ertm(fcs=FcsType.NO_FCS, tx_window_size=ertm_tx_window_size)
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM, req_config_options=config, rsp_config_options=config)
+
+        cert_channel.send_i_frame(tx_seq=0, req_seq=0, payload=SAMPLE_PACKET)
+        assertThat(cert_channel).emits(L2capMatchers.SFrame(req_seq=1))
+
+        cert_channel.send_i_frame(tx_seq=ertm_tx_window_size - 1, req_seq=0, payload=SAMPLE_PACKET)
+        assertThat(cert_channel).emits(L2capMatchers.SFrame(s=SupervisoryFunction.REJECT))
+
+        cert_channel.send_s_frame(req_seq=0, p=Poll.POLL)
+
+        assertThat(cert_channel).emits(L2capMatchers.SFrame(req_seq=1, f=l2cap_packets.Final.POLL_RESPONSE))
+        for i in range(1, ertm_tx_window_size):
+            cert_channel.send_i_frame(tx_seq=i, req_seq=0, payload=SAMPLE_PACKET)
+            assertThat(cert_channel).emits(L2capMatchers.SFrame(req_seq=i + 1))
+
+    @metadata(pts_test_id="L2CAP/ERM/BI-03-C", pts_test_name="Handle Duplicate S-Frame [SREJ]")
+    def test_handle_duplicate_srej(self):
+        """
+        Verify the IUT will only retransmit the requested I-frame once after
+        receiving a duplicate SREJ
+        """
+        self._setup_link_from_cert()
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM, fcs=FcsType.NO_FCS)
+
+        dut_channel.send(b'abc')
+        dut_channel.send(b'abc')
+        assertThat(cert_channel).emits(
+            L2capMatchers.IFrame(tx_seq=0), L2capMatchers.IFrame(tx_seq=1),
+            L2capMatchers.SFrame(p=Poll.POLL)).inOrder()
+
+        cert_channel.send_s_frame(req_seq=0, s=SupervisoryFunction.SELECT_REJECT)
+        assertThat(cert_channel).emitsNone(timeout=timedelta(seconds=0.5))
+
+        cert_channel.send_s_frame(req_seq=0, s=SupervisoryFunction.SELECT_REJECT, f=Final.POLL_RESPONSE)
+        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=0))
+
+    @metadata(
+        pts_test_id="L2CAP/ERM/BI-04-C",
+        pts_test_name="Handle Receipt of S-Frame [REJ] and S-Frame "
+        "[RR, F=1] that Both Require Retransmission of the "
+        "Same I-Frames")
+    def test_handle_receipt_rej_and_rr_with_f_set(self):
+        """
+        Verify the IUT will only retransmit the requested I-frames once after
+        receiving an S-frame [REJ] followed by an S-frame [RR] with the Final
+        bit set that indicates the same I-frames should be retransmitted
+        """
+        self._setup_link_from_cert()
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM, fcs=FcsType.NO_FCS)
+
+        dut_channel.send(b'abc')
+        dut_channel.send(b'abc')
+        assertThat(cert_channel).emits(
+            L2capMatchers.IFrame(tx_seq=0),
+            L2capMatchers.IFrame(tx_seq=1),
+            L2capMatchers.SFrame(p=l2cap_packets.Poll.POLL)).inOrder()
+
+        cert_channel.send_s_frame(req_seq=0, s=SupervisoryFunction.REJECT)
+        assertThat(cert_channel).emitsNone(timeout=timedelta(seconds=0.5))
+
+        # Send RR with F set
+        cert_channel.send_s_frame(req_seq=0, s=SupervisoryFunction.REJECT, f=Final.POLL_RESPONSE)
+        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=0))
+        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=1))
+
+    @metadata(
+        pts_test_id="L2CAP/ERM/BI-05-C",
+        pts_test_name="Handle receipt of S-Frame [REJ] and I-Frame [F=1] "
+        "that Both Require Retransmission of the Same "
+        "I-Frames")
+    def test_handle_rej_and_i_frame_with_f_set(self):
+        """
+        Verify the IUT will only retransmit the requested I-frames once after
+        receiving an S-frame [REJ] followed by an I-frame with the Final bit
+        set that indicates the same I-frames should be retransmitted
+        """
+        self._setup_link_from_cert()
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM, fcs=FcsType.NO_FCS)
+
+        dut_channel.send(b'abc')
+        dut_channel.send(b'abc')
+        assertThat(cert_channel).emits(
+            L2capMatchers.IFrame(tx_seq=0),
+            L2capMatchers.IFrame(tx_seq=1),
+            L2capMatchers.SFrame(p=l2cap_packets.Poll.POLL)).inOrder()
+
+        # Send SREJ with F not set
+        cert_channel.send_s_frame(req_seq=0, s=SupervisoryFunction.SELECT_REJECT)
+        assertThat(cert_channel).emitsNone(timeout=timedelta(seconds=0.5))
+
+        cert_channel.send_i_frame(tx_seq=0, req_seq=0, f=Final.POLL_RESPONSE, payload=SAMPLE_PACKET)
+
+        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=0))
+        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=1))
+
+    @metadata(
+        pts_test_id="L2CAP/CMC/BV-01-C", pts_test_name="IUT Initiated Configuration of Enhanced "
+        "Retransmission Mode")
+    def test_initiated_configuration_request_ertm(self):
+        """
+        Verify the IUT can send a Configuration Request command containing the
+        F&EC option that specifies Enhanced Retransmission Mode
+        """
+        self._setup_link_from_cert()
+
+        self._open_unconfigured_channel_from_cert(scid=0x41, psm=0x33, mode=RetransmissionFlowControlMode.ERTM)
+
+        assertThat(self.cert_l2cap.get_control_channel()).emits(L2capMatchers.ConfigurationRequestWithErtm())
+
+    @metadata(
+        pts_test_id="L2CAP/CMC/BV-02-C",
+        pts_test_name="Lower Tester Initiated Configuration of Enhanced "
+        "Retransmission Mode")
+    def test_respond_configuration_request_ertm(self):
+        """
+        Verify the IUT can accept a Configuration Request from the Lower Tester
+        containing an F&EC option that specifies Enhanced Retransmission Mode
+        """
+        self._setup_link_from_cert()
+
+        self._open_channel_from_dut(psm=0x33, mode=RetransmissionFlowControlMode.ERTM)
+
+    @metadata(
+        pts_test_id="L2CAP/CMC/BV-12-C",
+        pts_test_name="ERTM Not Supported by Lower Tester for Mandatory "
+        "ERTM channel")
+    def test_respond_not_support_ertm_when_using_mandatory_ertm(self):
+        """
+        The IUT is initiating connection of an L2CAP channel that mandates use
+        of ERTM. Verify the IUT will not attempt to configure the connection to
+        ERTM if the Lower Tester has not indicated support for ERTM in the
+        Information Response [Extended Features]
+        """
+        self._setup_link_from_cert()
+        self.cert_l2cap.claim_ertm_unsupported()
+        dut_channel_future = self.dut_l2cap.connect_dynamic_channel_to_cert(
+            psm=0x33, mode=RetransmissionFlowControlMode.ERTM)
+        assertThat(self.cert_l2cap.get_control_channel()).emitsNone(L2capMatchers.ConnectionRequest(0x33))
+
+    @metadata(
+        pts_test_id="L2CAP/CMC/BI-01-C",
+        pts_test_name="Failed Configuration of Enhanced Retransmission "
+        "Mode when use of the Mode is Mandatory]")
+    def test_config_respond_basic_mode_when_using_mandatory_ertm(self):
+        """
+        When creating a connection for a PSM that mandates the use of ERTM
+        verify the IUT can handle receipt (close the channel in accordance with
+        the specification) of a Configure Response indicating the peer L2CAP
+        entity doesn’t wish to use Enhanced Retransmission Mode (Configure
+        Response Result = Reject Unacceptable Parameters)
+        """
+
+        self._setup_link_from_cert()
+
+        (dut_channel, cert_channel) = self._open_channel_from_cert(
+            mode=RetransmissionFlowControlMode.ERTM,
+            req_config_options=CertL2cap.config_option_ertm(),
+            rsp_config_options=CertL2cap.config_option_basic_explicit())
+
+        cert_channel.verify_disconnect_request()
+
+    @metadata(
+        pts_test_id="L2CAP/CMC/BI-02-C",
+        pts_test_name="Configuration Mode mismatch when use of Enhanced "
+        "Retransmission Mode is Mandatory")
+    def test_config_request_basic_mode_when_using_mandatory_ertm(self):
+        """
+        When creating a connection for a PSM that mandates the use of ERTM,
+        verify the IUT will close the channel if the Lower Tester attempts to
+        configure Basic Mode.
+        """
+        self._setup_link_from_cert()
+
+        (dut_channel, cert_channel) = self._open_unconfigured_channel_from_cert(mode=RetransmissionFlowControlMode.ERTM)
+        cert_channel.send_configure_request(CertL2cap.config_option_basic_explicit())
+        cert_channel.verify_disconnect_request()
+
+    def test_initiate_connection_for_security(self):
+        """
+        This will test the PyL2cap API for initiating a connection for security
+        via the security api
+        """
+        self.dut.neighbor.EnablePageScan(neighbor_facade.EnableMsg(enabled=True))
+        self.cert.neighbor.EnablePageScan(neighbor_facade.EnableMsg(enabled=True))
+        self.dut_l2cap.initiate_connection_for_security()
+        self.cert_l2cap.accept_incoming_connection()
+        self.dut_l2cap.verify_security_connection()
diff --git a/gd/l2cap/le/cert/dual_l2cap_test.py b/gd/l2cap/le/cert/dual_l2cap_test.py
index 00e6392..7a36b42 100644
--- a/gd/l2cap/le/cert/dual_l2cap_test.py
+++ b/gd/l2cap/le/cert/dual_l2cap_test.py
@@ -14,174 +14,18 @@
 #   limitations under the License.
 
 from cert.gd_base_test import GdBaseTestClass
-from cert.truth import assertThat
-from cert.py_l2cap import PyLeL2cap, PyL2cap
-from cert.matchers import L2capMatchers
-from cert.metadata import metadata
-from facade import common_pb2 as common
-from google.protobuf import empty_pb2 as empty_proto
-from hci.facade import le_acl_manager_facade_pb2 as le_acl_manager_facade
-from hci.facade import le_advertising_manager_facade_pb2 as le_advertising_facade
-from hci.facade import le_initiator_address_facade_pb2 as le_initiator_address_facade
-import bluetooth_packets_python3 as bt_packets
-from bluetooth_packets_python3 import hci_packets, l2cap_packets
-from l2cap.classic.cert.cert_l2cap import CertL2cap
-from l2cap.le.cert.cert_le_l2cap import CertLeL2cap
-from neighbor.facade import facade_pb2 as neighbor_facade
-
-# Assemble a sample packet.
-SAMPLE_PACKET = bt_packets.RawBuilder([0x19, 0x26, 0x08, 0x17])
+from l2cap.le.cert.dual_l2cap_test_lib import DualL2capTestBase
 
 
-class DualL2capTest(GdBaseTestClass):
+class DualL2capTest(GdBaseTestClass, DualL2capTestBase):
 
     def setup_class(self):
-        super().setup_class(dut_module='L2CAP', cert_module='HCI_INTERFACES')
+        GdBaseTestClass.setup_class(self, dut_module='L2CAP', cert_module='HCI_INTERFACES')
 
     def setup_test(self):
-        super().setup_test()
-
-        self.dut_address = self.dut.hci_controller.GetMacAddressSimple()
-        cert_address = common.BluetoothAddress(
-            address=self.cert.controller_read_only_property.ReadLocalAddress(empty_proto.Empty()).address)
-
-        self.dut_l2cap = PyL2cap(self.dut, cert_address)
-        self.cert_l2cap = CertL2cap(self.cert)
-        self.dut_le_l2cap = PyLeL2cap(self.dut)
-        self.cert_le_l2cap = CertLeL2cap(self.cert)
-        self.dut_le_address = common.BluetoothAddressWithType(
-            address=common.BluetoothAddress(address=bytes(b'D0:05:04:03:02:01')), type=common.RANDOM_DEVICE_ADDRESS)
-        self.cert_address = common.BluetoothAddressWithType(
-            address=common.BluetoothAddress(address=bytes(b'C0:11:FF:AA:33:22')), type=common.RANDOM_DEVICE_ADDRESS)
-        dut_privacy_policy = le_initiator_address_facade.PrivacyPolicy(
-            address_policy=le_initiator_address_facade.AddressPolicy.USE_STATIC_ADDRESS,
-            address_with_type=self.dut_le_address,
-            rotation_irk=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
-            minimum_rotation_time=0,
-            maximum_rotation_time=0)
-        self.dut_l2cap._device.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(dut_privacy_policy)
-        privacy_policy = le_initiator_address_facade.PrivacyPolicy(
-            address_policy=le_initiator_address_facade.AddressPolicy.USE_STATIC_ADDRESS,
-            address_with_type=self.cert_address,
-            rotation_irk=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
-            minimum_rotation_time=0,
-            maximum_rotation_time=0)
-        self.cert_le_l2cap._device.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(privacy_policy)
+        GdBaseTestClass.setup_test(self)
+        DualL2capTestBase.setup_test(self, self.dut, self.cert)
 
     def teardown_test(self):
-        self.cert_le_l2cap.close()
-        self.dut_le_l2cap.close()
-        self.cert_l2cap.close()
-        self.dut_l2cap.close()
-        super().teardown_test()
-
-    def _setup_acl_link_from_cert(self):
-        self.dut.neighbor.EnablePageScan(neighbor_facade.EnableMsg(enabled=True))
-        self.cert_l2cap.connect_acl(self.dut_address)
-
-    def _setup_le_link_from_cert(self):
-        # DUT Advertises
-        gap_name = hci_packets.GapData()
-        gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
-        gap_name.data = list(bytes(b'Im_The_DUT'))
-        gap_data = le_advertising_facade.GapDataMsg(data=bytes(gap_name.Serialize()))
-        config = le_advertising_facade.AdvertisingConfig(
-            advertisement=[gap_data],
-            interval_min=512,
-            interval_max=768,
-            advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
-            own_address_type=common.USE_RANDOM_DEVICE_ADDRESS,
-            channel_map=7,
-            filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES)
-        request = le_advertising_facade.CreateAdvertiserRequest(config=config)
-        create_response = self.dut.hci_le_advertising_manager.CreateAdvertiser(request)
-        self.cert_le_l2cap.connect_le_acl(self.dut_le_address)
-
-    def _open_le_coc_from_dut(self, psm=0x33, our_scid=None):
-        response_future = self.dut_le_l2cap.connect_coc_to_cert(self.cert_address, psm)
-        cert_channel = self.cert_le_l2cap.verify_and_respond_open_channel_from_remote(psm=psm, our_scid=our_scid)
-        dut_channel = response_future.get_channel()
-        return (dut_channel, cert_channel)
-
-    def _open_channel_from_dut(self, psm=0x33, our_scid=None):
-        dut_channel_future = self.dut_l2cap.connect_dynamic_channel_to_cert(psm)
-        cert_channel = self.cert_l2cap.verify_and_respond_open_channel_from_remote(psm=psm, scid=our_scid)
-        dut_channel = dut_channel_future.get_channel()
-
-        cert_channel.verify_configuration_request_and_respond()
-        cert_channel.send_configure_request([])
-        cert_channel.verify_configuration_response()
-
-        return (dut_channel, cert_channel)
-
-    def _open_unconfigured_channel_from_cert(self, signal_id=1, scid=0x0101, psm=0x33):
-
-        dut_channel = self.dut_l2cap.register_dynamic_channel(psm)
-        cert_channel = self.cert_l2cap.open_channel(signal_id, psm, scid)
-
-        return (dut_channel, cert_channel)
-
-    def _open_channel_from_cert(self, signal_id=1, scid=0x0101, psm=0x33):
-        (dut_channel, cert_channel) = self._open_unconfigured_channel_from_cert(signal_id, scid, psm)
-        cert_channel.verify_configuration_request_and_respond()
-        cert_channel.send_configure_request([])
-        cert_channel.verify_configuration_response()
-
-        return (dut_channel, cert_channel)
-
-    def _open_le_coc_from_cert(self, signal_id=1, scid=0x0101, psm=0x35, mtu=1000, mps=100, initial_credit=6):
-
-        dut_channel = self.dut_le_l2cap.register_coc(self.cert_address, psm)
-        cert_channel = self.cert_le_l2cap.open_channel(signal_id, psm, scid, mtu, mps, initial_credit)
-
-        return (dut_channel, cert_channel)
-
-    @metadata(pts_test_id="L2CAP/LE/CID/BV-01-C", pts_test_name="Receiving DCID over BR/EDR and LE")
-    def test_receiving_dcid_over_bredr_and_le(self):
-        """
-        Test that the L2CAP entity can receive the same DCID in L2CAP connect responses on both the
-        BR/EDR and LE links.
-        """
-        self._setup_acl_link_from_cert()
-        # TODO: We should let LE use public address, same as classic link.
-        # TODO: Update AclManager::impl::create_le_connection
-        self._setup_le_link_from_cert()
-        (dut_channel, cert_channel) = self._open_channel_from_dut(0x33, 0x70)
-        (le_dut_channel, le_cert_channel) = self._open_le_coc_from_dut(0x35, 0x70)
-
-        dut_channel.send(b'abc')
-        assertThat(cert_channel).emits(L2capMatchers.Data(b'abc'))
-
-        le_dut_channel.send(b'hello')
-        assertThat(le_cert_channel).emits(L2capMatchers.FirstLeIFrame(b'hello', sdu_size=5))
-
-        le_cert_channel.send_first_le_i_frame(4, SAMPLE_PACKET)
-        assertThat(le_dut_channel).emits(L2capMatchers.PacketPayloadRawData(b'\x19\x26\x08\x17'))
-
-        cert_channel.disconnect_and_verify()
-        le_cert_channel.disconnect_and_verify()
-
-    @metadata(pts_test_id="L2CAP/LE/CID/BV-02-C", pts_test_name="Receiving SCID over BR/EDR and LE")
-    def test_receiving_scid_over_bredr_and_le(self):
-        """
-        Test that the L2CAP entity can receive the same SCID in L2CAP connect requests on both the
-        BR/EDR and LE links.
-        """
-        self._setup_acl_link_from_cert()
-        # TODO: We should let LE use public address, same as classic link.
-        # TODO: Update AclManager::impl::create_le_connection
-        self._setup_le_link_from_cert()
-        (dut_channel, cert_channel) = self._open_channel_from_cert(0x33, 0x70)
-        (le_dut_channel, le_cert_channel) = self._open_le_coc_from_cert(0x35, 0x70)
-
-        dut_channel.send(b'abc')
-        assertThat(cert_channel).emits(L2capMatchers.Data(b'abc'))
-
-        le_dut_channel.send(b'hello')
-        assertThat(le_cert_channel).emits(L2capMatchers.FirstLeIFrame(b'hello', sdu_size=5))
-
-        le_cert_channel.send_first_le_i_frame(4, SAMPLE_PACKET)
-        assertThat(le_dut_channel).emits(L2capMatchers.PacketPayloadRawData(b'\x19\x26\x08\x17'))
-
-        cert_channel.disconnect_and_verify()
-        le_cert_channel.disconnect_and_verify()
+        DualL2capTestBase.teardown_test(self)
+        GdBaseTestClass.teardown_test(self)
diff --git a/gd/l2cap/le/cert/dual_l2cap_test_lib.py b/gd/l2cap/le/cert/dual_l2cap_test_lib.py
new file mode 100644
index 0000000..139b47a
--- /dev/null
+++ b/gd/l2cap/le/cert/dual_l2cap_test_lib.py
@@ -0,0 +1,183 @@
+#
+#   Copyright 2020 - 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.
+
+from cert.truth import assertThat
+from cert.py_l2cap import PyLeL2cap, PyL2cap
+from cert.matchers import L2capMatchers
+from cert.metadata import metadata
+from facade import common_pb2 as common
+from google.protobuf import empty_pb2 as empty_proto
+from hci.facade import le_acl_manager_facade_pb2 as le_acl_manager_facade
+from hci.facade import le_advertising_manager_facade_pb2 as le_advertising_facade
+from hci.facade import le_initiator_address_facade_pb2 as le_initiator_address_facade
+import bluetooth_packets_python3 as bt_packets
+from bluetooth_packets_python3 import hci_packets, l2cap_packets
+from l2cap.classic.cert.cert_l2cap import CertL2cap
+from l2cap.le.cert.cert_le_l2cap import CertLeL2cap
+from neighbor.facade import facade_pb2 as neighbor_facade
+
+# Assemble a sample packet.
+SAMPLE_PACKET = bt_packets.RawBuilder([0x19, 0x26, 0x08, 0x17])
+
+
+class DualL2capTestBase():
+
+    def setup_test(self, dut, cert):
+        self.dut = dut
+        self.cert = cert
+
+        self.dut_address = self.dut.hci_controller.GetMacAddressSimple()
+        cert_address = common.BluetoothAddress(
+            address=self.cert.controller_read_only_property.ReadLocalAddress(empty_proto.Empty()).address)
+
+        self.dut_l2cap = PyL2cap(self.dut, cert_address)
+        self.cert_l2cap = CertL2cap(self.cert)
+        self.dut_le_l2cap = PyLeL2cap(self.dut)
+        self.cert_le_l2cap = CertLeL2cap(self.cert)
+        self.dut_le_address = common.BluetoothAddressWithType(
+            address=common.BluetoothAddress(address=bytes(b'D0:05:04:03:02:01')), type=common.RANDOM_DEVICE_ADDRESS)
+        self.cert_address = common.BluetoothAddressWithType(
+            address=common.BluetoothAddress(address=bytes(b'C0:11:FF:AA:33:22')), type=common.RANDOM_DEVICE_ADDRESS)
+        dut_privacy_policy = le_initiator_address_facade.PrivacyPolicy(
+            address_policy=le_initiator_address_facade.AddressPolicy.USE_STATIC_ADDRESS,
+            address_with_type=self.dut_le_address,
+            rotation_irk=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
+            minimum_rotation_time=0,
+            maximum_rotation_time=0)
+        self.dut_l2cap._device.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(dut_privacy_policy)
+        privacy_policy = le_initiator_address_facade.PrivacyPolicy(
+            address_policy=le_initiator_address_facade.AddressPolicy.USE_STATIC_ADDRESS,
+            address_with_type=self.cert_address,
+            rotation_irk=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
+            minimum_rotation_time=0,
+            maximum_rotation_time=0)
+        self.cert_le_l2cap._device.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(privacy_policy)
+
+    def teardown_test(self):
+        self.cert_le_l2cap.close()
+        self.dut_le_l2cap.close()
+        self.cert_l2cap.close()
+        self.dut_l2cap.close()
+
+    def _setup_acl_link_from_cert(self):
+        self.dut.neighbor.EnablePageScan(neighbor_facade.EnableMsg(enabled=True))
+        self.cert_l2cap.connect_acl(self.dut_address)
+
+    def _setup_le_link_from_cert(self):
+        # DUT Advertises
+        gap_name = hci_packets.GapData()
+        gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+        gap_name.data = list(bytes(b'Im_The_DUT'))
+        gap_data = le_advertising_facade.GapDataMsg(data=bytes(gap_name.Serialize()))
+        config = le_advertising_facade.AdvertisingConfig(
+            advertisement=[gap_data],
+            interval_min=512,
+            interval_max=768,
+            advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
+            own_address_type=common.USE_RANDOM_DEVICE_ADDRESS,
+            channel_map=7,
+            filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES)
+        request = le_advertising_facade.CreateAdvertiserRequest(config=config)
+        create_response = self.dut.hci_le_advertising_manager.CreateAdvertiser(request)
+        self.cert_le_l2cap.connect_le_acl(self.dut_le_address)
+
+    def _open_le_coc_from_dut(self, psm=0x33, our_scid=None):
+        response_future = self.dut_le_l2cap.connect_coc_to_cert(self.cert_address, psm)
+        cert_channel = self.cert_le_l2cap.verify_and_respond_open_channel_from_remote(psm=psm, our_scid=our_scid)
+        dut_channel = response_future.get_channel()
+        return (dut_channel, cert_channel)
+
+    def _open_channel_from_dut(self, psm=0x33, our_scid=None):
+        dut_channel_future = self.dut_l2cap.connect_dynamic_channel_to_cert(psm)
+        cert_channel = self.cert_l2cap.verify_and_respond_open_channel_from_remote(psm=psm, scid=our_scid)
+        dut_channel = dut_channel_future.get_channel()
+
+        cert_channel.verify_configuration_request_and_respond()
+        cert_channel.send_configure_request([])
+        cert_channel.verify_configuration_response()
+
+        return (dut_channel, cert_channel)
+
+    def _open_unconfigured_channel_from_cert(self, signal_id=1, scid=0x0101, psm=0x33):
+
+        dut_channel = self.dut_l2cap.register_dynamic_channel(psm)
+        cert_channel = self.cert_l2cap.open_channel(signal_id, psm, scid)
+
+        return (dut_channel, cert_channel)
+
+    def _open_channel_from_cert(self, signal_id=1, scid=0x0101, psm=0x33):
+        (dut_channel, cert_channel) = self._open_unconfigured_channel_from_cert(signal_id, scid, psm)
+        cert_channel.verify_configuration_request_and_respond()
+        cert_channel.send_configure_request([])
+        cert_channel.verify_configuration_response()
+
+        return (dut_channel, cert_channel)
+
+    def _open_le_coc_from_cert(self, signal_id=1, scid=0x0101, psm=0x35, mtu=1000, mps=100, initial_credit=6):
+
+        dut_channel = self.dut_le_l2cap.register_coc(self.cert_address, psm)
+        cert_channel = self.cert_le_l2cap.open_channel(signal_id, psm, scid, mtu, mps, initial_credit)
+
+        return (dut_channel, cert_channel)
+
+    @metadata(pts_test_id="L2CAP/LE/CID/BV-01-C", pts_test_name="Receiving DCID over BR/EDR and LE")
+    def test_receiving_dcid_over_bredr_and_le(self):
+        """
+        Test that the L2CAP entity can receive the same DCID in L2CAP connect responses on both the
+        BR/EDR and LE links.
+        """
+        self._setup_acl_link_from_cert()
+        # TODO: We should let LE use public address, same as classic link.
+        # TODO: Update AclManager::impl::create_le_connection
+        self._setup_le_link_from_cert()
+        (dut_channel, cert_channel) = self._open_channel_from_dut(0x33, 0x70)
+        (le_dut_channel, le_cert_channel) = self._open_le_coc_from_dut(0x35, 0x70)
+
+        dut_channel.send(b'abc')
+        assertThat(cert_channel).emits(L2capMatchers.Data(b'abc'))
+
+        le_dut_channel.send(b'hello')
+        assertThat(le_cert_channel).emits(L2capMatchers.FirstLeIFrame(b'hello', sdu_size=5))
+
+        le_cert_channel.send_first_le_i_frame(4, SAMPLE_PACKET)
+        assertThat(le_dut_channel).emits(L2capMatchers.PacketPayloadRawData(b'\x19\x26\x08\x17'))
+
+        cert_channel.disconnect_and_verify()
+        le_cert_channel.disconnect_and_verify()
+
+    @metadata(pts_test_id="L2CAP/LE/CID/BV-02-C", pts_test_name="Receiving SCID over BR/EDR and LE")
+    def test_receiving_scid_over_bredr_and_le(self):
+        """
+        Test that the L2CAP entity can receive the same SCID in L2CAP connect requests on both the
+        BR/EDR and LE links.
+        """
+        self._setup_acl_link_from_cert()
+        # TODO: We should let LE use public address, same as classic link.
+        # TODO: Update AclManager::impl::create_le_connection
+        self._setup_le_link_from_cert()
+        (dut_channel, cert_channel) = self._open_channel_from_cert(0x33, 0x70)
+        (le_dut_channel, le_cert_channel) = self._open_le_coc_from_cert(0x35, 0x70)
+
+        dut_channel.send(b'abc')
+        assertThat(cert_channel).emits(L2capMatchers.Data(b'abc'))
+
+        le_dut_channel.send(b'hello')
+        assertThat(le_cert_channel).emits(L2capMatchers.FirstLeIFrame(b'hello', sdu_size=5))
+
+        le_cert_channel.send_first_le_i_frame(4, SAMPLE_PACKET)
+        assertThat(le_dut_channel).emits(L2capMatchers.PacketPayloadRawData(b'\x19\x26\x08\x17'))
+
+        cert_channel.disconnect_and_verify()
+        le_cert_channel.disconnect_and_verify()
diff --git a/gd/l2cap/le/cert/le_l2cap_test.py b/gd/l2cap/le/cert/le_l2cap_test.py
index 80f3aed..a5c8607 100644
--- a/gd/l2cap/le/cert/le_l2cap_test.py
+++ b/gd/l2cap/le/cert/le_l2cap_test.py
@@ -14,558 +14,18 @@
 #   limitations under the License.
 
 from cert.gd_base_test import GdBaseTestClass
-from cert.truth import assertThat
-from cert.py_l2cap import PyLeL2cap
-from cert.matchers import L2capMatchers
-from cert.metadata import metadata
-from facade import common_pb2 as common
-from google.protobuf import empty_pb2 as empty_proto
-from hci.facade import le_acl_manager_facade_pb2 as le_acl_manager_facade
-from hci.facade import le_advertising_manager_facade_pb2 as le_advertising_facade
-from hci.facade import le_initiator_address_facade_pb2 as le_initiator_address_facade
-import bluetooth_packets_python3 as bt_packets
-from bluetooth_packets_python3 import hci_packets, l2cap_packets
-from bluetooth_packets_python3.l2cap_packets import LeCreditBasedConnectionResponseResult
-from l2cap.le.cert.cert_le_l2cap import CertLeL2cap
-from l2cap.le.facade_pb2 import SecurityLevel
-
-# Assemble a sample packet.
-SAMPLE_PACKET = bt_packets.RawBuilder([0x19, 0x26, 0x08, 0x17])
+from l2cap.le.cert.le_l2cap_test_lib import LeL2capTestBase
 
 
-class LeL2capTest(GdBaseTestClass):
+class LeL2capTest(GdBaseTestClass, LeL2capTestBase):
 
     def setup_class(self):
-        super().setup_class(dut_module='L2CAP', cert_module='HCI_INTERFACES')
+        GdBaseTestClass.setup_class(self, dut_module='L2CAP', cert_module='HCI_INTERFACES')
 
     def setup_test(self):
-        super().setup_test()
-
-        self.dut_l2cap = PyLeL2cap(self.dut)
-        self.cert_l2cap = CertLeL2cap(self.cert)
-        self.dut_address = common.BluetoothAddressWithType(
-            address=common.BluetoothAddress(address=bytes(b'D0:05:04:03:02:01')), type=common.RANDOM_DEVICE_ADDRESS)
-        self.cert_address = common.BluetoothAddressWithType(
-            address=common.BluetoothAddress(address=bytes(b'C0:11:FF:AA:33:22')), type=common.RANDOM_DEVICE_ADDRESS)
-        dut_privacy_policy = le_initiator_address_facade.PrivacyPolicy(
-            address_policy=le_initiator_address_facade.AddressPolicy.USE_STATIC_ADDRESS,
-            address_with_type=self.dut_address,
-            rotation_irk=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
-            minimum_rotation_time=0,
-            maximum_rotation_time=0)
-        self.dut_l2cap._device.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(dut_privacy_policy)
-        privacy_policy = le_initiator_address_facade.PrivacyPolicy(
-            address_policy=le_initiator_address_facade.AddressPolicy.USE_STATIC_ADDRESS,
-            address_with_type=self.cert_address,
-            rotation_irk=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
-            minimum_rotation_time=0,
-            maximum_rotation_time=0)
-        self.cert_l2cap._device.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(privacy_policy)
+        GdBaseTestClass.setup_test(self)
+        LeL2capTestBase.setup_test(self, self.dut, self.cert)
 
     def teardown_test(self):
-        self.cert_l2cap.close()
-        self.dut_l2cap.close()
-        super().teardown_test()
-
-    def _setup_link_from_cert(self):
-        # DUT Advertises
-        gap_name = hci_packets.GapData()
-        gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
-        gap_name.data = list(bytes(b'Im_The_DUT'))
-        gap_data = le_advertising_facade.GapDataMsg(data=bytes(gap_name.Serialize()))
-        config = le_advertising_facade.AdvertisingConfig(
-            advertisement=[gap_data],
-            interval_min=512,
-            interval_max=768,
-            advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
-            own_address_type=common.USE_RANDOM_DEVICE_ADDRESS,
-            channel_map=7,
-            filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES)
-        request = le_advertising_facade.CreateAdvertiserRequest(config=config)
-        create_response = self.dut.hci_le_advertising_manager.CreateAdvertiser(request)
-        self.cert_l2cap.connect_le_acl(self.dut_address)
-
-    def _set_link_from_dut_and_open_channel(self,
-                                            signal_id=1,
-                                            scid=0x0101,
-                                            psm=0x33,
-                                            mtu=1000,
-                                            mps=100,
-                                            initial_credit=6):
-        # Cert Advertises
-        gap_name = hci_packets.GapData()
-        gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
-        gap_name.data = list(bytes(b'Im_The_DUT'))
-        gap_data = le_advertising_facade.GapDataMsg(data=bytes(gap_name.Serialize()))
-        config = le_advertising_facade.AdvertisingConfig(
-            advertisement=[gap_data],
-            interval_min=512,
-            interval_max=768,
-            advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
-            own_address_type=common.USE_RANDOM_DEVICE_ADDRESS,
-            channel_map=7,
-            filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES)
-        request = le_advertising_facade.CreateAdvertiserRequest(config=config)
-        create_response = self.cert.hci_le_advertising_manager.CreateAdvertiser(request)
-        response_future = self.dut_l2cap.connect_coc_to_cert(self.cert_address, psm)
-        self.cert_l2cap.wait_for_connection()
-        # TODO: Currently we can only connect by using Dynamic channel API. Use fixed channel instead.
-        cert_channel = self.cert_l2cap.verify_and_respond_open_channel_from_remote(psm)
-        dut_channel = response_future.get_channel()
-        return (dut_channel, cert_channel)
-
-    def _open_channel_from_cert(self, signal_id=1, scid=0x0101, psm=0x33, mtu=1000, mps=100, initial_credit=6):
-
-        dut_channel = self.dut_l2cap.register_coc(self.cert_address, psm)
-        cert_channel = self.cert_l2cap.open_channel(signal_id, psm, scid, mtu, mps, initial_credit)
-
-        return (dut_channel, cert_channel)
-
-    def _open_channel_from_dut(self, psm=0x33):
-        response_future = self.dut_l2cap.connect_coc_to_cert(self.cert_address, psm)
-        cert_channel = self.cert_l2cap.verify_and_respond_open_channel_from_remote(psm)
-        dut_channel = response_future.get_channel()
-        return (dut_channel, cert_channel)
-
-    def _open_fixed_channel(self, cid=4):
-        dut_channel = self.dut_l2cap.get_fixed_channel(cid)
-        cert_channel = self.cert_l2cap.open_fixed_channel(cid)
-        return (dut_channel, cert_channel)
-
-    def test_fixed_channel_send(self):
-        self.dut_l2cap.enable_fixed_channel(4)
-        self._setup_link_from_cert()
-        (dut_channel, cert_channel) = self._open_fixed_channel(4)
-        dut_channel.send(b'hello' * 40)
-        assertThat(cert_channel).emits(L2capMatchers.Data(b'hello' * 40))
-
-    def test_fixed_channel_receive(self):
-        self.dut_l2cap.enable_fixed_channel(4)
-        self._setup_link_from_cert()
-        (dut_channel, cert_channel) = self._open_fixed_channel(4)
-        cert_channel.send(SAMPLE_PACKET)
-        assertThat(dut_channel).emits(L2capMatchers.PacketPayloadRawData(b'\x19\x26\x08\x17'))
-
-    def test_connect_from_dut_and_open_dynamic_channel(self):
-        """
-        Internal test for GD stack only
-        """
-        self._set_link_from_dut_and_open_channel()
-
-    @metadata(pts_test_id="L2CAP/LE/CPU/BV-01-C", pts_test_name="Send Connection Parameter Update Request")
-    def test_send_connection_parameter_update_request(self):
-        """
-        Verify that the IUT is able to send the connection parameter update Request to Lower Tester when acting as a peripheral device.
-        NOTE: This is an optional feature. Also if both LL central and peripheral supports 4.1+ connection parameter update, this should happen in LL only, not L2CAP
-        NOTE: Currently we need to establish at least one dynamic channel to allow update.
-        """
-        self._setup_link_from_cert()
-        self._open_channel_from_dut()
-        self.dut_l2cap.update_connection_parameter()
-        assertThat(self.cert_l2cap.get_control_channel()).emits(L2capMatchers.LeConnectionParameterUpdateRequest())
-
-    @metadata(pts_test_id="L2CAP/LE/CPU/BV-02-C", pts_test_name="Accept Connection Parameter Update Request")
-    def test_accept_connection_parameter_update_request(self):
-        """
-        Verify that the IUT is able to receive and handle a request for connection parameter update when acting as a central device.
-        NOTE: Currently we need to establish at least one dynamic channel to allow update.
-        """
-        self._set_link_from_dut_and_open_channel()
-        self.cert_l2cap.get_control_channel().send(
-            l2cap_packets.ConnectionParameterUpdateRequestBuilder(2, 0x10, 0x10, 0x0a, 0x64))
-        assertThat(self.cert_l2cap.get_control_channel()).emits(
-            L2capMatchers.LeConnectionParameterUpdateResponse(
-                l2cap_packets.ConnectionParameterUpdateResponseResult.ACCEPTED))
-
-    @metadata(pts_test_id="L2CAP/LE/CPU/BI-01-C", pts_test_name="Reject Connection Parameter Update Parameters")
-    def test_reject_connection_parameter_update_parameters(self):
-        """
-        Verify that the IUT is able to reject a request for connection parameter update with illegal parameters.
-        NOTE: Currently we need to establish at least one dynamic channel to allow update.
-        """
-        self._set_link_from_dut_and_open_channel()
-        self.cert_l2cap.get_control_channel().send(
-            l2cap_packets.ConnectionParameterUpdateRequestBuilder(2, 0x10, 0x10, 512, 0x64))
-        assertThat(self.cert_l2cap.get_control_channel()).emits(
-            L2capMatchers.LeConnectionParameterUpdateResponse(
-                l2cap_packets.ConnectionParameterUpdateResponseResult.REJECTED))
-
-    @metadata(pts_test_id="L2CAP/LE/CPU/BI-02-C", pts_test_name="Reject Connection Parameter Update Request")
-    def test_reject_connection_parameter_update_request(self):
-        """
-        Verify that the IUT is able to reject a request for connection parameter update in peripheral mode.
-        """
-        self._setup_link_from_cert()
-        self.cert_l2cap.get_control_channel().send(
-            l2cap_packets.ConnectionParameterUpdateRequestBuilder(2, 0x10, 0x10, 0x0a, 0x64))
-        assertThat(self.cert_l2cap.get_control_channel()).emits(L2capMatchers.LeCommandReject())
-
-    @metadata(pts_test_id="L2CAP/COS/CFC/BV-01-C", pts_test_name="Segmentation")
-    def test_segmentation(self):
-        """
-        Verify that the IUT can send data segments which are larger than the LE frame size.
-        """
-        self._setup_link_from_cert()
-        (dut_channel, cert_channel) = self._open_channel_from_cert(mtu=1000, mps=102)
-        dut_channel.send(b'hello' * 20 + b'world')
-        # The first LeInformation packet contains 2 bytes of SDU size.
-        # The packet is divided into first 100 bytes from 'hellohello....'
-        # and remaining 5 bytes 'world'
-        assertThat(cert_channel).emits(
-            L2capMatchers.FirstLeIFrame(b'hello' * 20, sdu_size=105), L2capMatchers.Data(b'world')).inOrder()
-
-    @metadata(pts_test_id="L2CAP/COS/CFC/BV-02-C", pts_test_name="No Segmentation")
-    def test_no_segmentation(self):
-        """
-        Verify that the IUT can send data segments which do not require segmentation.
-        """
-        self._setup_link_from_cert()
-        (dut_channel, cert_channel) = self._open_channel_from_cert(mtu=1000, mps=202)
-        dut_channel.send(b'hello' * 40)
-        assertThat(cert_channel).emits(L2capMatchers.FirstLeIFrame(b'hello' * 40, sdu_size=200))
-
-    def test_no_segmentation_dut_is_central(self):
-        """
-        L2CAP/COS/CFC/BV-02-C
-        """
-        (dut_channel, cert_channel) = self._set_link_from_dut_and_open_channel()
-        dut_channel.send(b'hello' * 40)
-        assertThat(cert_channel).emits(L2capMatchers.FirstLeIFrame(b'hello' * 40, sdu_size=200))
-
-    @metadata(pts_test_id="L2CAP/COS/CFC/BV-03-C", pts_test_name="Reassembling")
-    def test_reassembling(self):
-        """
-        Verify that the IUT can correctly reassemble data received from the Lower Tester which is greater than the IUT LE-frame size.
-        """
-        self._setup_link_from_cert()
-        (dut_channel, cert_channel) = self._open_channel_from_cert()
-        sdu_size_for_two_sample_packet = 8
-        cert_channel.send_first_le_i_frame(sdu_size_for_two_sample_packet, SAMPLE_PACKET)
-        cert_channel.send(SAMPLE_PACKET)
-        assertThat(dut_channel).emits(L2capMatchers.PacketPayloadRawData(b'\x19\x26\x08\x17' * 2))
-
-    @metadata(pts_test_id="L2CAP/COS/CFC/BV-04-C", pts_test_name="Data Receiving")
-    def test_data_receiving(self):
-        """
-        Verify that the IUT can receive unsegmented data correctly.
-        """
-        self._setup_link_from_cert()
-        (dut_channel, cert_channel) = self._open_channel_from_cert()
-        cert_channel.send_first_le_i_frame(4, SAMPLE_PACKET)
-        assertThat(dut_channel).emits(L2capMatchers.PacketPayloadRawData(b'\x19\x26\x08\x17'))
-
-    def test_data_receiving_dut_is_central(self):
-        """
-        L2CAP/COS/CFC/BV-04-C
-        """
-        (dut_channel, cert_channel) = self._set_link_from_dut_and_open_channel()
-        cert_channel.send_first_le_i_frame(4, SAMPLE_PACKET)
-        assertThat(dut_channel).emits(L2capMatchers.PacketPayloadRawData(b'\x19\x26\x08\x17'))
-
-    @metadata(pts_test_id="L2CAP/COS/CFC/BV-05-C", pts_test_name="Multiple Channels with Interleaved Data Streams")
-    def test_multiple_channels_with_interleaved_data_streams(self):
-        """
-        Verify that an IUT can create multiple channels and receives data streams on the channels when the streams are interleaved.
-        """
-        self._setup_link_from_cert()
-        (dut_channel_x, cert_channel_x) = self._open_channel_from_cert(signal_id=1, scid=0x0103, psm=0x33)
-        (dut_channel_y, cert_channel_y) = self._open_channel_from_cert(signal_id=2, scid=0x0105, psm=0x35)
-        (dut_channel_z, cert_channel_z) = self._open_channel_from_cert(signal_id=3, scid=0x0107, psm=0x37)
-        cert_channel_y.send_first_le_i_frame(4, SAMPLE_PACKET)
-        cert_channel_z.send_first_le_i_frame(4, SAMPLE_PACKET)
-        cert_channel_y.send_first_le_i_frame(4, SAMPLE_PACKET)
-        cert_channel_z.send_first_le_i_frame(4, SAMPLE_PACKET)
-        cert_channel_y.send_first_le_i_frame(4, SAMPLE_PACKET)
-        # TODO: We should assert two events in order, but it got stuck
-        assertThat(dut_channel_y).emits(L2capMatchers.PacketPayloadRawData(b'\x19\x26\x08\x17'), at_least_times=3)
-        assertThat(dut_channel_z).emits(
-            L2capMatchers.PacketPayloadRawData(b'\x19\x26\x08\x17'),
-            L2capMatchers.PacketPayloadRawData(b'\x19\x26\x08\x17')).inOrder()
-        cert_channel_z.send_first_le_i_frame(4, SAMPLE_PACKET)
-        assertThat(dut_channel_z).emits(L2capMatchers.PacketPayloadRawData(b'\x19\x26\x08\x17'))
-
-    @metadata(pts_test_id="L2CAP/LE/REJ/BI-01-C", pts_test_name="Reject Unknown Command in LE Signaling Channel")
-    def test_reject_unknown_command_in_le_sigling_channel(self):
-        """
-        Verify that the IUT is able to reject unknown command.
-        """
-        self._setup_link_from_cert()
-        self.cert_l2cap.get_control_channel().send(
-            l2cap_packets.InformationRequestBuilder(
-                2, l2cap_packets.InformationRequestInfoType.EXTENDED_FEATURES_SUPPORTED))
-        assertThat(self.cert_l2cap.get_control_channel()).emits(L2capMatchers.LeCommandReject())
-
-    @metadata(pts_test_id="L2CAP/LE/REJ/BI-02-C", pts_test_name="Command Reject – Reserved PDU Codes")
-    def test_command_reject_reserved_pdu_codes(self):
-        """
-        Verify that an IUT receiving a PDU with a reserved command code sends a command reject.
-        """
-        self._setup_link_from_cert()
-        self.cert_l2cap.get_control_channel().send(l2cap_packets.MoveChannelRequestBuilder(2, 0, 0))
-        assertThat(self.cert_l2cap.get_control_channel()).emits(L2capMatchers.LeCommandReject())
-
-    @metadata(pts_test_id="L2CAP/LE/CFC/BV-01-C", pts_test_name="LE Credit Based Connection Request - Legacy Peer")
-    def test_le_credit_based_connection_request_legacy_peer(self):
-        """
-        Verify that an IUT sending an LE Credit Based Connection Request to a legacy peer and receiving a Command Reject does not establish the channel.
-        """
-        self._setup_link_from_cert()
-        response_future = self.dut_l2cap.connect_coc_to_cert(self.cert_address, psm=0x33)
-        self.cert_l2cap.verify_and_reject_open_channel_from_remote(psm=0x33)
-        assertThat(response_future.get_status()).isNotEqualTo(LeCreditBasedConnectionResponseResult.SUCCESS)
-
-    @metadata(
-        pts_test_id="L2CAP/LE/CFC/BV-02-C", pts_test_name="LE Credit Based Connection Request on Supported LE_PSM")
-    def test_le_credit_based_connection_request_on_supported_le_psm(self):
-        """
-        Verify that an IUT sending an LE Credit Based Connection Request to a peer will establish the channel upon receiving the LE Credit Based Connection Response.
-        """
-        self._setup_link_from_cert()
-        (dut_channel, cert_channel) = self._open_channel_from_dut()
-        cert_channel.send_first_le_i_frame(4, SAMPLE_PACKET)
-        assertThat(dut_channel).emits(L2capMatchers.PacketPayloadRawData(b'\x19\x26\x08\x17'))
-
-    @metadata(
-        pts_test_id="L2CAP/LE/CFC/BV-03-C", pts_test_name="LE Credit Based Connection Response on Supported LE_PSM")
-    def test_credit_based_connection_response_on_supported_le_psm(self):
-        """
-        Verify that an IUT receiving a valid LE Credit Based Connection Request from a peer will send an LE Credit Based Connection Response and establish the channel.
-        """
-        self._setup_link_from_cert()
-        (dut_channel, cert_channel) = self._open_channel_from_cert()
-        dut_channel.send(b'hello')
-        assertThat(cert_channel).emits(L2capMatchers.FirstLeIFrame(b'hello', sdu_size=5))
-
-    @metadata(
-        pts_test_id="L2CAP/LE/CFC/BV-04-C", pts_test_name="LE Credit Based Connection Request on an Unsupported LE_PSM")
-    def test_credit_based_connection_request_on_an_unsupported_le_psm(self):
-        """
-        Verify that an IUT sending an LE Credit Based Connection Request on an unsupported LE_PSM will not establish a channel upon receiving an LE Credit Based Connection Response refusing the connection.
-        """
-        self._setup_link_from_cert()
-        response_future = self.dut_l2cap.connect_coc_to_cert(self.cert_address, psm=0x33)
-        self.cert_l2cap.verify_and_respond_open_channel_from_remote(
-            psm=0x33, result=LeCreditBasedConnectionResponseResult.LE_PSM_NOT_SUPPORTED)
-        assertThat(response_future.get_status()).isEqualTo(LeCreditBasedConnectionResponseResult.LE_PSM_NOT_SUPPORTED)
-
-    @metadata(
-        pts_test_id="L2CAP/LE/CFC/BV-05-C", pts_test_name="LE Credit Based Connection Request - unsupported LE_PSM")
-    def test_credit_based_connection_request_unsupported_le_psm(self):
-        """
-        Verify that an IUT receiving an LE Credit Based Connection Request on an unsupported LE_PSM will respond with an LE Credit Based Connection Response refusing the connection.
-        """
-        self._setup_link_from_cert()
-        self.cert_l2cap.get_control_channel().send(
-            l2cap_packets.LeCreditBasedConnectionRequestBuilder(1, 0x34, 0x0101, 2000, 1000, 1000))
-        assertThat(self.cert_l2cap.get_control_channel()).emits(
-            L2capMatchers.CreditBasedConnectionResponse(
-                result=LeCreditBasedConnectionResponseResult.LE_PSM_NOT_SUPPORTED))
-
-    @metadata(pts_test_id="L2CAP/LE/CFC/BV-06-C", pts_test_name="Credit Exchange – Receiving Incremental Credits")
-    def test_credit_exchange_receiving_incremental_credits(self):
-        """
-        Verify the IUT handles flow control correctly, by handling the LE Flow Control Credit sent by the peer.
-        """
-        self._setup_link_from_cert()
-        (dut_channel, cert_channel) = self._open_channel_from_cert(initial_credit=0)
-        for _ in range(4):
-            dut_channel.send(b'hello')
-        cert_channel.send_credits(1)
-        assertThat(cert_channel).emits(L2capMatchers.FirstLeIFrame(b'hello', sdu_size=5))
-        cert_channel.send_credits(1)
-        assertThat(cert_channel).emits(L2capMatchers.FirstLeIFrame(b'hello', sdu_size=5))
-        cert_channel.send_credits(2)
-        assertThat(cert_channel).emits(
-            L2capMatchers.FirstLeIFrame(b'hello', sdu_size=5), L2capMatchers.FirstLeIFrame(b'hello', sdu_size=5))
-
-    @metadata(pts_test_id="L2CAP/LE/CFC/BV-07-C", pts_test_name="Credit Exchange – Sending Credits")
-    def test_credit_exchange_sending_credits(self):
-        """
-        Verify that the IUT sends LE Flow Control Credit to the peer.
-        """
-        self._setup_link_from_cert()
-        (dut_channel, cert_channel) = self._open_channel_from_cert()
-        credits = cert_channel.credits_left()
-        # Note: DUT only needs to send credit when ALL credits are consumed.
-        # Here we enforce that DUT sends credit after receiving 3 packets, to
-        # test without sending too many packets (may take too long).
-        # This behavior is not expected for all Bluetooth stacks.
-        for _ in range(min(credits + 1, 3)):
-            cert_channel.send_first_le_i_frame(4, SAMPLE_PACKET)
-        self.cert_l2cap.verify_le_flow_control_credit(cert_channel)
-
-    @metadata(pts_test_id="L2CAP/LE/CFC/BV-08-C", pts_test_name="Disconnection Request")
-    def test_disconnection_request(self):
-        """
-        Verify that the IUT can disconnect the channel.
-        """
-        self._setup_link_from_cert()
-        (dut_channel, cert_channel) = self._open_channel_from_cert()
-        dut_channel.close_channel()
-        cert_channel.verify_disconnect_request()
-
-    @metadata(pts_test_id="L2CAP/LE/CFC/BV-09-C", pts_test_name="Disconnection Response")
-    def test_disconnection_response(self):
-        """
-        Verify that the IUT responds correctly to reception of a Disconnection Request.
-        """
-        self._setup_link_from_cert()
-        (dut_channel, cert_channel) = self._open_channel_from_cert()
-        cert_channel.disconnect_and_verify()
-
-    @metadata(pts_test_id="L2CAP/LE/CFC/BV-10-C", pts_test_name="Security - Insufficient Authentication – Initiator")
-    def test_security_insufficient_authentication_initiator(self):
-        """
-        Verify that the IUT does not establish the channel upon receipt of an LE Credit Based Connection Response indicating the connection was refused with Result “0x0005 – Connection Refused – Insufficient Authentication".
-        """
-        self._setup_link_from_cert()
-        response_future = self.dut_l2cap.connect_coc_to_cert(self.cert_address, psm=0x33)
-        self.cert_l2cap.verify_and_respond_open_channel_from_remote(
-            psm=0x33, result=LeCreditBasedConnectionResponseResult.INSUFFICIENT_AUTHENTICATION)
-        assertThat(response_future.get_status()).isEqualTo(
-            LeCreditBasedConnectionResponseResult.INSUFFICIENT_AUTHENTICATION)
-
-    @metadata(pts_test_id="L2CAP/LE/CFC/BV-11-C", pts_test_name="Security - Insufficient Authentication – Responder")
-    def test_security_insufficient_authentication_responder(self):
-        """
-        Verify that an IUT refuses to create a connection upon reception of an LE Credit Based Connection
-Request which fails to satisfy authentication requirements.
-        """
-        self._setup_link_from_cert()
-        psm = 0x33
-        self.dut_l2cap.register_coc(self.cert_address, psm, SecurityLevel.AUTHENTICATED_PAIRING_WITH_ENCRYPTION)
-        self.cert_l2cap.open_channel_with_expected_result(
-            psm, LeCreditBasedConnectionResponseResult.INSUFFICIENT_AUTHENTICATION)
-
-    @metadata(pts_test_id="L2CAP/LE/CFC/BV-12-C", pts_test_name="Security - Insufficient Authorization – Initiator")
-    def test_security_insufficient_authorization_initiator(self):
-        """
-        Verify that the IUT does not establish the channel upon receipt of an LE Credit Based Connection Response indicating the connection was refused with Result “0x0006 – Connection Refused – Insufficient Authorization”.
-        """
-        self._setup_link_from_cert()
-        response_future = self.dut_l2cap.connect_coc_to_cert(self.cert_address, psm=0x33)
-        self.cert_l2cap.verify_and_respond_open_channel_from_remote(
-            psm=0x33, result=LeCreditBasedConnectionResponseResult.INSUFFICIENT_AUTHORIZATION)
-        assertThat(response_future.get_status()).isEqualTo(
-            LeCreditBasedConnectionResponseResult.INSUFFICIENT_AUTHORIZATION)
-
-    @metadata(pts_test_id="L2CAP/LE/CFC/BV-13-C", pts_test_name="Security - Insufficient Authorization – Responder")
-    def test_security_insufficient_authorization_responder(self):
-        """
-        Verify that an IUT refuses to create a connection upon reception of an LE Credit Based Connection
-        Request which fails to satisfy authentication requirements.
-        """
-        self._setup_link_from_cert()
-        psm = 0x33
-        self.dut_l2cap.register_coc(self.cert_address, psm, SecurityLevel.AUTHORIZATION)
-        self.cert_l2cap.open_channel_with_expected_result(
-            psm, LeCreditBasedConnectionResponseResult.INSUFFICIENT_AUTHORIZATION)
-
-    @metadata(pts_test_id="L2CAP/LE/CFC/BV-14-C", pts_test_name="Security - Insufficient Key Size – Initiator")
-    def test_security_insufficient_key_size_initiator(self):
-        """
-        Verify that the IUT does not establish the channel upon receipt of an
-        LE Credit Based Connection Response indicating the connection was
-        refused with Result "0x0007 – Connection Refused – Insufficient
-        Encryption Key Size".
-        """
-        self._setup_link_from_cert()
-        response_future = self.dut_l2cap.connect_coc_to_cert(self.cert_address, psm=0x33)
-        self.cert_l2cap.verify_and_respond_open_channel_from_remote(
-            psm=0x33, result=LeCreditBasedConnectionResponseResult.INSUFFICIENT_ENCRYPTION_KEY_SIZE)
-        assertThat(response_future.get_status()).isEqualTo(
-            LeCreditBasedConnectionResponseResult.INSUFFICIENT_ENCRYPTION_KEY_SIZE)
-
-    @metadata(
-        pts_test_id="L2CAP/LE/CFC/BV-15-C", pts_test_name="Security - Insufficient Encryption Key Size – Responder")
-    def test_security_insufficient_encryption_key_size_responder(self):
-        """
-        Verify that an IUT refuses to create a connection upon receipt of an LE Credit Based Connection
-        Request which fails to satisfy Encryption Key Size requirements.
-        """
-        self._setup_link_from_cert()
-        psm = 0x33
-        self.dut_l2cap.register_coc(self.cert_address, psm, SecurityLevel.AUTHENTICATED_PAIRING_WITH_128_BIT_KEY)
-        self.cert_l2cap.open_channel_with_expected_result(
-            psm, LeCreditBasedConnectionResponseResult.INSUFFICIENT_ENCRYPTION_KEY_SIZE)
-
-    @metadata(
-        pts_test_id="L2CAP/LE/CFC/BV-16-C",
-        pts_test_name="LE Credit Based Connection Request - refuse due to insufficient resources - Initiator")
-    def test_le_connection_request_insufficient_resources_initiator(self):
-        """
-        Verify that an IUT sending an LE Credit Based Connection Request does
-        not establish the channel upon receiving an LE Credit Based Connection
-        Response refusing the connection with result "0x0004 – Connection
-        refused – no resources available".
-        """
-        self._setup_link_from_cert()
-        response_future = self.dut_l2cap.connect_coc_to_cert(self.cert_address, psm=0x33)
-        self.cert_l2cap.verify_and_respond_open_channel_from_remote(
-            psm=0x33, result=LeCreditBasedConnectionResponseResult.NO_RESOURCES_AVAILABLE)
-        assertThat(response_future.get_status()).isEqualTo(LeCreditBasedConnectionResponseResult.NO_RESOURCES_AVAILABLE)
-
-    @metadata(
-        pts_test_id="L2CAP/LE/CFC/BV-18-C",
-        pts_test_name="LE Credit Based Connection Request - refused due to Invalid Source CID - Initiator")
-    def test_request_refused_due_to_invalid_source_cid_initiator(self):
-        """
-        Verify that an IUT sending an LE Credit Based Connection Request does not establish the channel upon receiving an LE Credit Based Connection Response refusing the connection with result "0x0009 – Connection refused – Invalid Source CID".
-        """
-        self._setup_link_from_cert()
-        response_future = self.dut_l2cap.connect_coc_to_cert(self.cert_address, psm=0x33)
-        self.cert_l2cap.verify_and_respond_open_channel_from_remote(
-            psm=0x33, result=LeCreditBasedConnectionResponseResult.INVALID_SOURCE_CID)
-        assertThat(response_future.get_status()).isEqualTo(LeCreditBasedConnectionResponseResult.INVALID_SOURCE_CID)
-
-    @metadata(
-        pts_test_id="L2CAP/LE/CFC/BV-19-C",
-        pts_test_name="LE Credit Based Connection Request - refused due to source CID already allocated - Initiator")
-    def test_request_refused_due_to_source_cid_already_allocated_initiator(self):
-        """
-        Verify that an IUT sending an LE Credit Based Connection Request does not establish the channel upon receiving an LE Credit Based Connection Response refusing the connection with result "0x000A – Connection refused – Source CID already allocated".
-        """
-        self._setup_link_from_cert()
-        response_future = self.dut_l2cap.connect_coc_to_cert(self.cert_address, psm=0x33)
-        self.cert_l2cap.verify_and_respond_open_channel_from_remote(
-            psm=0x33, result=LeCreditBasedConnectionResponseResult.SOURCE_CID_ALREADY_ALLOCATED)
-        assertThat(response_future.get_status()).isEqualTo(
-            LeCreditBasedConnectionResponseResult.SOURCE_CID_ALREADY_ALLOCATED)
-
-    @metadata(
-        pts_test_id="L2CAP/LE/CFC/BV-20-C",
-        pts_test_name="LE Credit Based Connection Response - refused due to Source CID already allocated - Responder")
-    def test_request_refused_due_to_source_cid_already_allocated_responder(self):
-        """
-        Verify that an IUT receiving an LE Credit Based Connection Request for a second channel will refuse the connection with result "0x000A - Connection refused – Source CID already allocated" if it receives a Source CID which is already in use.
-        """
-        self._setup_link_from_cert()
-        (dut_channel, cert_channel) = self._open_channel_from_cert(psm=0x33, scid=0x0101)
-        self.dut_l2cap.register_coc(self.cert_address, psm=0x35)
-        self.cert_l2cap.get_control_channel().send(
-            l2cap_packets.LeCreditBasedConnectionRequestBuilder(2, 0x35, 0x0101, 1000, 1000, 1000))
-        assertThat(self.cert_l2cap.get_control_channel()).emits(L2capMatchers.CreditBasedConnectionResponseUsedCid())
-
-    @metadata(
-        pts_test_id="L2CAP/LE/CFC/BV-21-C",
-        pts_test_name="LE Credit Based Connection Request - refused due to Unacceptable Parameters - Initiator")
-    def test_request_refused_due_to_unacceptable_parameters_initiator(self):
-        """
-        Verify that an IUT sending an LE Credit Based Connection Request does not establish the channel upon receiving an LE Credit Based Connection Response refusing the connection with result "0x000B – Connection refused – Unacceptable Parameters".
-        """
-        self._setup_link_from_cert()
-        response_future = self.dut_l2cap.connect_coc_to_cert(self.cert_address, psm=0x33)
-        self.cert_l2cap.verify_and_respond_open_channel_from_remote(
-            psm=0x33, result=LeCreditBasedConnectionResponseResult.UNACCEPTABLE_PARAMETERS)
-        assertThat(response_future.get_status()).isEqualTo(
-            LeCreditBasedConnectionResponseResult.UNACCEPTABLE_PARAMETERS)
-
-    @metadata(pts_test_id="L2CAP/LE/CFC/BI-01-C", pts_test_name="Credit Exchange – Exceed Initial Credits")
-    def test_credit_exchange_exceed_initial_credits(self):
-        """
-        Verify that the IUT disconnects the LE Data Channel when the credit count exceeds 65535.
-        """
-        self._setup_link_from_cert()
-        (dut_channel, cert_channel) = self._open_channel_from_cert()
-        cert_channel.send_credits(65535)
-        cert_channel.verify_disconnect_request()
+        LeL2capTestBase.teardown_test(self)
+        GdBaseTestClass.teardown_test(self)
diff --git a/gd/l2cap/le/cert/le_l2cap_test_lib.py b/gd/l2cap/le/cert/le_l2cap_test_lib.py
new file mode 100644
index 0000000..8d34e16
--- /dev/null
+++ b/gd/l2cap/le/cert/le_l2cap_test_lib.py
@@ -0,0 +1,570 @@
+#
+#   Copyright 2020 - 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.
+
+from cert.truth import assertThat
+from cert.py_l2cap import PyLeL2cap
+from cert.matchers import L2capMatchers
+from cert.metadata import metadata
+from facade import common_pb2 as common
+from google.protobuf import empty_pb2 as empty_proto
+from hci.facade import le_acl_manager_facade_pb2 as le_acl_manager_facade
+from hci.facade import le_advertising_manager_facade_pb2 as le_advertising_facade
+from hci.facade import le_initiator_address_facade_pb2 as le_initiator_address_facade
+import bluetooth_packets_python3 as bt_packets
+from bluetooth_packets_python3 import hci_packets, l2cap_packets
+from bluetooth_packets_python3.l2cap_packets import LeCreditBasedConnectionResponseResult
+from l2cap.le.cert.cert_le_l2cap import CertLeL2cap
+from l2cap.le.facade_pb2 import SecurityLevel
+
+# Assemble a sample packet.
+SAMPLE_PACKET = bt_packets.RawBuilder([0x19, 0x26, 0x08, 0x17])
+
+
+class LeL2capTestBase():
+
+    def setup_class(self):
+        GdBaseTestClass.setup_class(self, dut_module='L2CAP', cert_module='HCI_INTERFACES')
+
+    def setup_test(self, dut, cert):
+        self.dut = dut
+        self.cert = cert
+
+        self.dut_l2cap = PyLeL2cap(self.dut)
+        self.cert_l2cap = CertLeL2cap(self.cert)
+        self.dut_address = common.BluetoothAddressWithType(
+            address=common.BluetoothAddress(address=bytes(b'D0:05:04:03:02:01')), type=common.RANDOM_DEVICE_ADDRESS)
+        self.cert_address = common.BluetoothAddressWithType(
+            address=common.BluetoothAddress(address=bytes(b'C0:11:FF:AA:33:22')), type=common.RANDOM_DEVICE_ADDRESS)
+        dut_privacy_policy = le_initiator_address_facade.PrivacyPolicy(
+            address_policy=le_initiator_address_facade.AddressPolicy.USE_STATIC_ADDRESS,
+            address_with_type=self.dut_address,
+            rotation_irk=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
+            minimum_rotation_time=0,
+            maximum_rotation_time=0)
+        self.dut_l2cap._device.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(dut_privacy_policy)
+        privacy_policy = le_initiator_address_facade.PrivacyPolicy(
+            address_policy=le_initiator_address_facade.AddressPolicy.USE_STATIC_ADDRESS,
+            address_with_type=self.cert_address,
+            rotation_irk=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
+            minimum_rotation_time=0,
+            maximum_rotation_time=0)
+        self.cert_l2cap._device.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(privacy_policy)
+
+    def teardown_test(self):
+        self.cert_l2cap.close()
+        self.dut_l2cap.close()
+
+    def _setup_link_from_cert(self):
+        # DUT Advertises
+        gap_name = hci_packets.GapData()
+        gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+        gap_name.data = list(bytes(b'Im_The_DUT'))
+        gap_data = le_advertising_facade.GapDataMsg(data=bytes(gap_name.Serialize()))
+        config = le_advertising_facade.AdvertisingConfig(
+            advertisement=[gap_data],
+            interval_min=512,
+            interval_max=768,
+            advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
+            own_address_type=common.USE_RANDOM_DEVICE_ADDRESS,
+            channel_map=7,
+            filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES)
+        request = le_advertising_facade.CreateAdvertiserRequest(config=config)
+        create_response = self.dut.hci_le_advertising_manager.CreateAdvertiser(request)
+        self.cert_l2cap.connect_le_acl(self.dut_address)
+
+    def _set_link_from_dut_and_open_channel(self,
+                                            signal_id=1,
+                                            scid=0x0101,
+                                            psm=0x33,
+                                            mtu=1000,
+                                            mps=100,
+                                            initial_credit=6):
+        # Cert Advertises
+        gap_name = hci_packets.GapData()
+        gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+        gap_name.data = list(bytes(b'Im_The_DUT'))
+        gap_data = le_advertising_facade.GapDataMsg(data=bytes(gap_name.Serialize()))
+        config = le_advertising_facade.AdvertisingConfig(
+            advertisement=[gap_data],
+            interval_min=512,
+            interval_max=768,
+            advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
+            own_address_type=common.USE_RANDOM_DEVICE_ADDRESS,
+            channel_map=7,
+            filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES)
+        request = le_advertising_facade.CreateAdvertiserRequest(config=config)
+        create_response = self.cert.hci_le_advertising_manager.CreateAdvertiser(request)
+        response_future = self.dut_l2cap.connect_coc_to_cert(self.cert_address, psm)
+        self.cert_l2cap.wait_for_connection()
+        # TODO: Currently we can only connect by using Dynamic channel API. Use fixed channel instead.
+        cert_channel = self.cert_l2cap.verify_and_respond_open_channel_from_remote(psm)
+        dut_channel = response_future.get_channel()
+        return (dut_channel, cert_channel)
+
+    def _open_channel_from_cert(self, signal_id=1, scid=0x0101, psm=0x33, mtu=1000, mps=100, initial_credit=6):
+
+        dut_channel = self.dut_l2cap.register_coc(self.cert_address, psm)
+        cert_channel = self.cert_l2cap.open_channel(signal_id, psm, scid, mtu, mps, initial_credit)
+
+        return (dut_channel, cert_channel)
+
+    def _open_channel_from_dut(self, psm=0x33):
+        response_future = self.dut_l2cap.connect_coc_to_cert(self.cert_address, psm)
+        cert_channel = self.cert_l2cap.verify_and_respond_open_channel_from_remote(psm)
+        dut_channel = response_future.get_channel()
+        return (dut_channel, cert_channel)
+
+    def _open_fixed_channel(self, cid=4):
+        dut_channel = self.dut_l2cap.get_fixed_channel(cid)
+        cert_channel = self.cert_l2cap.open_fixed_channel(cid)
+        return (dut_channel, cert_channel)
+
+    def test_fixed_channel_send(self):
+        self.dut_l2cap.enable_fixed_channel(4)
+        self._setup_link_from_cert()
+        (dut_channel, cert_channel) = self._open_fixed_channel(4)
+        dut_channel.send(b'hello' * 40)
+        assertThat(cert_channel).emits(L2capMatchers.Data(b'hello' * 40))
+
+    def test_fixed_channel_receive(self):
+        self.dut_l2cap.enable_fixed_channel(4)
+        self._setup_link_from_cert()
+        (dut_channel, cert_channel) = self._open_fixed_channel(4)
+        cert_channel.send(SAMPLE_PACKET)
+        assertThat(dut_channel).emits(L2capMatchers.PacketPayloadRawData(b'\x19\x26\x08\x17'))
+
+    def test_connect_from_dut_and_open_dynamic_channel(self):
+        """
+        Internal test for GD stack only
+        """
+        self._set_link_from_dut_and_open_channel()
+
+    @metadata(pts_test_id="L2CAP/LE/CPU/BV-01-C", pts_test_name="Send Connection Parameter Update Request")
+    def test_send_connection_parameter_update_request(self):
+        """
+        Verify that the IUT is able to send the connection parameter update Request to Lower Tester when acting as a peripheral device.
+        NOTE: This is an optional feature. Also if both LL central and peripheral supports 4.1+ connection parameter update, this should happen in LL only, not L2CAP
+        NOTE: Currently we need to establish at least one dynamic channel to allow update.
+        """
+        self._setup_link_from_cert()
+        self._open_channel_from_dut()
+        self.dut_l2cap.update_connection_parameter()
+        assertThat(self.cert_l2cap.get_control_channel()).emits(L2capMatchers.LeConnectionParameterUpdateRequest())
+
+    @metadata(pts_test_id="L2CAP/LE/CPU/BV-02-C", pts_test_name="Accept Connection Parameter Update Request")
+    def test_accept_connection_parameter_update_request(self):
+        """
+        Verify that the IUT is able to receive and handle a request for connection parameter update when acting as a central device.
+        NOTE: Currently we need to establish at least one dynamic channel to allow update.
+        """
+        self._set_link_from_dut_and_open_channel()
+        self.cert_l2cap.get_control_channel().send(
+            l2cap_packets.ConnectionParameterUpdateRequestBuilder(2, 0x10, 0x10, 0x0a, 0x64))
+        assertThat(self.cert_l2cap.get_control_channel()).emits(
+            L2capMatchers.LeConnectionParameterUpdateResponse(
+                l2cap_packets.ConnectionParameterUpdateResponseResult.ACCEPTED))
+
+    @metadata(pts_test_id="L2CAP/LE/CPU/BI-01-C", pts_test_name="Reject Connection Parameter Update Parameters")
+    def test_reject_connection_parameter_update_parameters(self):
+        """
+        Verify that the IUT is able to reject a request for connection parameter update with illegal parameters.
+        NOTE: Currently we need to establish at least one dynamic channel to allow update.
+        """
+        self._set_link_from_dut_and_open_channel()
+        self.cert_l2cap.get_control_channel().send(
+            l2cap_packets.ConnectionParameterUpdateRequestBuilder(2, 0x10, 0x10, 512, 0x64))
+        assertThat(self.cert_l2cap.get_control_channel()).emits(
+            L2capMatchers.LeConnectionParameterUpdateResponse(
+                l2cap_packets.ConnectionParameterUpdateResponseResult.REJECTED))
+
+    @metadata(pts_test_id="L2CAP/LE/CPU/BI-02-C", pts_test_name="Reject Connection Parameter Update Request")
+    def test_reject_connection_parameter_update_request(self):
+        """
+        Verify that the IUT is able to reject a request for connection parameter update in peripheral mode.
+        """
+        self._setup_link_from_cert()
+        self.cert_l2cap.get_control_channel().send(
+            l2cap_packets.ConnectionParameterUpdateRequestBuilder(2, 0x10, 0x10, 0x0a, 0x64))
+        assertThat(self.cert_l2cap.get_control_channel()).emits(L2capMatchers.LeCommandReject())
+
+    @metadata(pts_test_id="L2CAP/COS/CFC/BV-01-C", pts_test_name="Segmentation")
+    def test_segmentation(self):
+        """
+        Verify that the IUT can send data segments which are larger than the LE frame size.
+        """
+        self._setup_link_from_cert()
+        (dut_channel, cert_channel) = self._open_channel_from_cert(mtu=1000, mps=102)
+        dut_channel.send(b'hello' * 20 + b'world')
+        # The first LeInformation packet contains 2 bytes of SDU size.
+        # The packet is divided into first 100 bytes from 'hellohello....'
+        # and remaining 5 bytes 'world'
+        assertThat(cert_channel).emits(
+            L2capMatchers.FirstLeIFrame(b'hello' * 20, sdu_size=105), L2capMatchers.Data(b'world')).inOrder()
+
+    @metadata(pts_test_id="L2CAP/COS/CFC/BV-02-C", pts_test_name="No Segmentation")
+    def test_no_segmentation(self):
+        """
+        Verify that the IUT can send data segments which do not require segmentation.
+        """
+        self._setup_link_from_cert()
+        (dut_channel, cert_channel) = self._open_channel_from_cert(mtu=1000, mps=202)
+        dut_channel.send(b'hello' * 40)
+        assertThat(cert_channel).emits(L2capMatchers.FirstLeIFrame(b'hello' * 40, sdu_size=200))
+
+    def test_no_segmentation_dut_is_central(self):
+        """
+        L2CAP/COS/CFC/BV-02-C
+        """
+        (dut_channel, cert_channel) = self._set_link_from_dut_and_open_channel()
+        dut_channel.send(b'hello' * 40)
+        assertThat(cert_channel).emits(L2capMatchers.FirstLeIFrame(b'hello' * 40, sdu_size=200))
+
+    @metadata(pts_test_id="L2CAP/COS/CFC/BV-03-C", pts_test_name="Reassembling")
+    def test_reassembling(self):
+        """
+        Verify that the IUT can correctly reassemble data received from the Lower Tester which is greater than the IUT LE-frame size.
+        """
+        self._setup_link_from_cert()
+        (dut_channel, cert_channel) = self._open_channel_from_cert()
+        sdu_size_for_two_sample_packet = 8
+        cert_channel.send_first_le_i_frame(sdu_size_for_two_sample_packet, SAMPLE_PACKET)
+        cert_channel.send(SAMPLE_PACKET)
+        assertThat(dut_channel).emits(L2capMatchers.PacketPayloadRawData(b'\x19\x26\x08\x17' * 2))
+
+    @metadata(pts_test_id="L2CAP/COS/CFC/BV-04-C", pts_test_name="Data Receiving")
+    def test_data_receiving(self):
+        """
+        Verify that the IUT can receive unsegmented data correctly.
+        """
+        self._setup_link_from_cert()
+        (dut_channel, cert_channel) = self._open_channel_from_cert()
+        cert_channel.send_first_le_i_frame(4, SAMPLE_PACKET)
+        assertThat(dut_channel).emits(L2capMatchers.PacketPayloadRawData(b'\x19\x26\x08\x17'))
+
+    def test_data_receiving_dut_is_central(self):
+        """
+        L2CAP/COS/CFC/BV-04-C
+        """
+        (dut_channel, cert_channel) = self._set_link_from_dut_and_open_channel()
+        cert_channel.send_first_le_i_frame(4, SAMPLE_PACKET)
+        assertThat(dut_channel).emits(L2capMatchers.PacketPayloadRawData(b'\x19\x26\x08\x17'))
+
+    @metadata(pts_test_id="L2CAP/COS/CFC/BV-05-C", pts_test_name="Multiple Channels with Interleaved Data Streams")
+    def test_multiple_channels_with_interleaved_data_streams(self):
+        """
+        Verify that an IUT can create multiple channels and receives data streams on the channels when the streams are interleaved.
+        """
+        self._setup_link_from_cert()
+        (dut_channel_x, cert_channel_x) = self._open_channel_from_cert(signal_id=1, scid=0x0103, psm=0x33)
+        (dut_channel_y, cert_channel_y) = self._open_channel_from_cert(signal_id=2, scid=0x0105, psm=0x35)
+        (dut_channel_z, cert_channel_z) = self._open_channel_from_cert(signal_id=3, scid=0x0107, psm=0x37)
+        cert_channel_y.send_first_le_i_frame(4, SAMPLE_PACKET)
+        cert_channel_z.send_first_le_i_frame(4, SAMPLE_PACKET)
+        cert_channel_y.send_first_le_i_frame(4, SAMPLE_PACKET)
+        cert_channel_z.send_first_le_i_frame(4, SAMPLE_PACKET)
+        cert_channel_y.send_first_le_i_frame(4, SAMPLE_PACKET)
+        # TODO: We should assert two events in order, but it got stuck
+        assertThat(dut_channel_y).emits(L2capMatchers.PacketPayloadRawData(b'\x19\x26\x08\x17'), at_least_times=3)
+        assertThat(dut_channel_z).emits(
+            L2capMatchers.PacketPayloadRawData(b'\x19\x26\x08\x17'),
+            L2capMatchers.PacketPayloadRawData(b'\x19\x26\x08\x17')).inOrder()
+        cert_channel_z.send_first_le_i_frame(4, SAMPLE_PACKET)
+        assertThat(dut_channel_z).emits(L2capMatchers.PacketPayloadRawData(b'\x19\x26\x08\x17'))
+
+    @metadata(pts_test_id="L2CAP/LE/REJ/BI-01-C", pts_test_name="Reject Unknown Command in LE Signaling Channel")
+    def test_reject_unknown_command_in_le_sigling_channel(self):
+        """
+        Verify that the IUT is able to reject unknown command.
+        """
+        self._setup_link_from_cert()
+        self.cert_l2cap.get_control_channel().send(
+            l2cap_packets.InformationRequestBuilder(
+                2, l2cap_packets.InformationRequestInfoType.EXTENDED_FEATURES_SUPPORTED))
+        assertThat(self.cert_l2cap.get_control_channel()).emits(L2capMatchers.LeCommandReject())
+
+    @metadata(pts_test_id="L2CAP/LE/REJ/BI-02-C", pts_test_name="Command Reject – Reserved PDU Codes")
+    def test_command_reject_reserved_pdu_codes(self):
+        """
+        Verify that an IUT receiving a PDU with a reserved command code sends a command reject.
+        """
+        self._setup_link_from_cert()
+        self.cert_l2cap.get_control_channel().send(l2cap_packets.MoveChannelRequestBuilder(2, 0, 0))
+        assertThat(self.cert_l2cap.get_control_channel()).emits(L2capMatchers.LeCommandReject())
+
+    @metadata(pts_test_id="L2CAP/LE/CFC/BV-01-C", pts_test_name="LE Credit Based Connection Request - Legacy Peer")
+    def test_le_credit_based_connection_request_legacy_peer(self):
+        """
+        Verify that an IUT sending an LE Credit Based Connection Request to a legacy peer and receiving a Command Reject does not establish the channel.
+        """
+        self._setup_link_from_cert()
+        response_future = self.dut_l2cap.connect_coc_to_cert(self.cert_address, psm=0x33)
+        self.cert_l2cap.verify_and_reject_open_channel_from_remote(psm=0x33)
+        assertThat(response_future.get_status()).isNotEqualTo(LeCreditBasedConnectionResponseResult.SUCCESS)
+
+    @metadata(
+        pts_test_id="L2CAP/LE/CFC/BV-02-C", pts_test_name="LE Credit Based Connection Request on Supported LE_PSM")
+    def test_le_credit_based_connection_request_on_supported_le_psm(self):
+        """
+        Verify that an IUT sending an LE Credit Based Connection Request to a peer will establish the channel upon receiving the LE Credit Based Connection Response.
+        """
+        self._setup_link_from_cert()
+        (dut_channel, cert_channel) = self._open_channel_from_dut()
+        cert_channel.send_first_le_i_frame(4, SAMPLE_PACKET)
+        assertThat(dut_channel).emits(L2capMatchers.PacketPayloadRawData(b'\x19\x26\x08\x17'))
+
+    @metadata(
+        pts_test_id="L2CAP/LE/CFC/BV-03-C", pts_test_name="LE Credit Based Connection Response on Supported LE_PSM")
+    def test_credit_based_connection_response_on_supported_le_psm(self):
+        """
+        Verify that an IUT receiving a valid LE Credit Based Connection Request from a peer will send an LE Credit Based Connection Response and establish the channel.
+        """
+        self._setup_link_from_cert()
+        (dut_channel, cert_channel) = self._open_channel_from_cert()
+        dut_channel.send(b'hello')
+        assertThat(cert_channel).emits(L2capMatchers.FirstLeIFrame(b'hello', sdu_size=5))
+
+    @metadata(
+        pts_test_id="L2CAP/LE/CFC/BV-04-C", pts_test_name="LE Credit Based Connection Request on an Unsupported LE_PSM")
+    def test_credit_based_connection_request_on_an_unsupported_le_psm(self):
+        """
+        Verify that an IUT sending an LE Credit Based Connection Request on an unsupported LE_PSM will not establish a channel upon receiving an LE Credit Based Connection Response refusing the connection.
+        """
+        self._setup_link_from_cert()
+        response_future = self.dut_l2cap.connect_coc_to_cert(self.cert_address, psm=0x33)
+        self.cert_l2cap.verify_and_respond_open_channel_from_remote(
+            psm=0x33, result=LeCreditBasedConnectionResponseResult.LE_PSM_NOT_SUPPORTED)
+        assertThat(response_future.get_status()).isEqualTo(LeCreditBasedConnectionResponseResult.LE_PSM_NOT_SUPPORTED)
+
+    @metadata(
+        pts_test_id="L2CAP/LE/CFC/BV-05-C", pts_test_name="LE Credit Based Connection Request - unsupported LE_PSM")
+    def test_credit_based_connection_request_unsupported_le_psm(self):
+        """
+        Verify that an IUT receiving an LE Credit Based Connection Request on an unsupported LE_PSM will respond with an LE Credit Based Connection Response refusing the connection.
+        """
+        self._setup_link_from_cert()
+        self.cert_l2cap.get_control_channel().send(
+            l2cap_packets.LeCreditBasedConnectionRequestBuilder(1, 0x34, 0x0101, 2000, 1000, 1000))
+        assertThat(self.cert_l2cap.get_control_channel()).emits(
+            L2capMatchers.CreditBasedConnectionResponse(
+                result=LeCreditBasedConnectionResponseResult.LE_PSM_NOT_SUPPORTED))
+
+    @metadata(pts_test_id="L2CAP/LE/CFC/BV-06-C", pts_test_name="Credit Exchange – Receiving Incremental Credits")
+    def test_credit_exchange_receiving_incremental_credits(self):
+        """
+        Verify the IUT handles flow control correctly, by handling the LE Flow Control Credit sent by the peer.
+        """
+        self._setup_link_from_cert()
+        (dut_channel, cert_channel) = self._open_channel_from_cert(initial_credit=0)
+        for _ in range(4):
+            dut_channel.send(b'hello')
+        cert_channel.send_credits(1)
+        assertThat(cert_channel).emits(L2capMatchers.FirstLeIFrame(b'hello', sdu_size=5))
+        cert_channel.send_credits(1)
+        assertThat(cert_channel).emits(L2capMatchers.FirstLeIFrame(b'hello', sdu_size=5))
+        cert_channel.send_credits(2)
+        assertThat(cert_channel).emits(
+            L2capMatchers.FirstLeIFrame(b'hello', sdu_size=5), L2capMatchers.FirstLeIFrame(b'hello', sdu_size=5))
+
+    @metadata(pts_test_id="L2CAP/LE/CFC/BV-07-C", pts_test_name="Credit Exchange – Sending Credits")
+    def test_credit_exchange_sending_credits(self):
+        """
+        Verify that the IUT sends LE Flow Control Credit to the peer.
+        """
+        self._setup_link_from_cert()
+        (dut_channel, cert_channel) = self._open_channel_from_cert()
+        credits = cert_channel.credits_left()
+        # Note: DUT only needs to send credit when ALL credits are consumed.
+        # Here we enforce that DUT sends credit after receiving 3 packets, to
+        # test without sending too many packets (may take too long).
+        # This behavior is not expected for all Bluetooth stacks.
+        for _ in range(min(credits + 1, 3)):
+            cert_channel.send_first_le_i_frame(4, SAMPLE_PACKET)
+        self.cert_l2cap.verify_le_flow_control_credit(cert_channel)
+
+    @metadata(pts_test_id="L2CAP/LE/CFC/BV-08-C", pts_test_name="Disconnection Request")
+    def test_disconnection_request(self):
+        """
+        Verify that the IUT can disconnect the channel.
+        """
+        self._setup_link_from_cert()
+        (dut_channel, cert_channel) = self._open_channel_from_cert()
+        dut_channel.close_channel()
+        cert_channel.verify_disconnect_request()
+
+    @metadata(pts_test_id="L2CAP/LE/CFC/BV-09-C", pts_test_name="Disconnection Response")
+    def test_disconnection_response(self):
+        """
+        Verify that the IUT responds correctly to reception of a Disconnection Request.
+        """
+        self._setup_link_from_cert()
+        (dut_channel, cert_channel) = self._open_channel_from_cert()
+        cert_channel.disconnect_and_verify()
+
+    @metadata(pts_test_id="L2CAP/LE/CFC/BV-10-C", pts_test_name="Security - Insufficient Authentication – Initiator")
+    def test_security_insufficient_authentication_initiator(self):
+        """
+        Verify that the IUT does not establish the channel upon receipt of an LE Credit Based Connection Response indicating the connection was refused with Result “0x0005 – Connection Refused – Insufficient Authentication".
+        """
+        self._setup_link_from_cert()
+        response_future = self.dut_l2cap.connect_coc_to_cert(self.cert_address, psm=0x33)
+        self.cert_l2cap.verify_and_respond_open_channel_from_remote(
+            psm=0x33, result=LeCreditBasedConnectionResponseResult.INSUFFICIENT_AUTHENTICATION)
+        assertThat(response_future.get_status()).isEqualTo(
+            LeCreditBasedConnectionResponseResult.INSUFFICIENT_AUTHENTICATION)
+
+    @metadata(pts_test_id="L2CAP/LE/CFC/BV-11-C", pts_test_name="Security - Insufficient Authentication – Responder")
+    def test_security_insufficient_authentication_responder(self):
+        """
+        Verify that an IUT refuses to create a connection upon reception of an LE Credit Based Connection
+Request which fails to satisfy authentication requirements.
+        """
+        self._setup_link_from_cert()
+        psm = 0x33
+        self.dut_l2cap.register_coc(self.cert_address, psm, SecurityLevel.AUTHENTICATED_PAIRING_WITH_ENCRYPTION)
+        self.cert_l2cap.open_channel_with_expected_result(
+            psm, LeCreditBasedConnectionResponseResult.INSUFFICIENT_AUTHENTICATION)
+
+    @metadata(pts_test_id="L2CAP/LE/CFC/BV-12-C", pts_test_name="Security - Insufficient Authorization – Initiator")
+    def test_security_insufficient_authorization_initiator(self):
+        """
+        Verify that the IUT does not establish the channel upon receipt of an LE Credit Based Connection Response indicating the connection was refused with Result “0x0006 – Connection Refused – Insufficient Authorization”.
+        """
+        self._setup_link_from_cert()
+        response_future = self.dut_l2cap.connect_coc_to_cert(self.cert_address, psm=0x33)
+        self.cert_l2cap.verify_and_respond_open_channel_from_remote(
+            psm=0x33, result=LeCreditBasedConnectionResponseResult.INSUFFICIENT_AUTHORIZATION)
+        assertThat(response_future.get_status()).isEqualTo(
+            LeCreditBasedConnectionResponseResult.INSUFFICIENT_AUTHORIZATION)
+
+    @metadata(pts_test_id="L2CAP/LE/CFC/BV-13-C", pts_test_name="Security - Insufficient Authorization – Responder")
+    def test_security_insufficient_authorization_responder(self):
+        """
+        Verify that an IUT refuses to create a connection upon reception of an LE Credit Based Connection
+        Request which fails to satisfy authentication requirements.
+        """
+        self._setup_link_from_cert()
+        psm = 0x33
+        self.dut_l2cap.register_coc(self.cert_address, psm, SecurityLevel.AUTHORIZATION)
+        self.cert_l2cap.open_channel_with_expected_result(
+            psm, LeCreditBasedConnectionResponseResult.INSUFFICIENT_AUTHORIZATION)
+
+    @metadata(pts_test_id="L2CAP/LE/CFC/BV-14-C", pts_test_name="Security - Insufficient Key Size – Initiator")
+    def test_security_insufficient_key_size_initiator(self):
+        """
+        Verify that the IUT does not establish the channel upon receipt of an
+        LE Credit Based Connection Response indicating the connection was
+        refused with Result "0x0007 – Connection Refused – Insufficient
+        Encryption Key Size".
+        """
+        self._setup_link_from_cert()
+        response_future = self.dut_l2cap.connect_coc_to_cert(self.cert_address, psm=0x33)
+        self.cert_l2cap.verify_and_respond_open_channel_from_remote(
+            psm=0x33, result=LeCreditBasedConnectionResponseResult.INSUFFICIENT_ENCRYPTION_KEY_SIZE)
+        assertThat(response_future.get_status()).isEqualTo(
+            LeCreditBasedConnectionResponseResult.INSUFFICIENT_ENCRYPTION_KEY_SIZE)
+
+    @metadata(
+        pts_test_id="L2CAP/LE/CFC/BV-15-C", pts_test_name="Security - Insufficient Encryption Key Size – Responder")
+    def test_security_insufficient_encryption_key_size_responder(self):
+        """
+        Verify that an IUT refuses to create a connection upon receipt of an LE Credit Based Connection
+        Request which fails to satisfy Encryption Key Size requirements.
+        """
+        self._setup_link_from_cert()
+        psm = 0x33
+        self.dut_l2cap.register_coc(self.cert_address, psm, SecurityLevel.AUTHENTICATED_PAIRING_WITH_128_BIT_KEY)
+        self.cert_l2cap.open_channel_with_expected_result(
+            psm, LeCreditBasedConnectionResponseResult.INSUFFICIENT_ENCRYPTION_KEY_SIZE)
+
+    @metadata(
+        pts_test_id="L2CAP/LE/CFC/BV-16-C",
+        pts_test_name="LE Credit Based Connection Request - refuse due to insufficient resources - Initiator")
+    def test_le_connection_request_insufficient_resources_initiator(self):
+        """
+        Verify that an IUT sending an LE Credit Based Connection Request does
+        not establish the channel upon receiving an LE Credit Based Connection
+        Response refusing the connection with result "0x0004 – Connection
+        refused – no resources available".
+        """
+        self._setup_link_from_cert()
+        response_future = self.dut_l2cap.connect_coc_to_cert(self.cert_address, psm=0x33)
+        self.cert_l2cap.verify_and_respond_open_channel_from_remote(
+            psm=0x33, result=LeCreditBasedConnectionResponseResult.NO_RESOURCES_AVAILABLE)
+        assertThat(response_future.get_status()).isEqualTo(LeCreditBasedConnectionResponseResult.NO_RESOURCES_AVAILABLE)
+
+    @metadata(
+        pts_test_id="L2CAP/LE/CFC/BV-18-C",
+        pts_test_name="LE Credit Based Connection Request - refused due to Invalid Source CID - Initiator")
+    def test_request_refused_due_to_invalid_source_cid_initiator(self):
+        """
+        Verify that an IUT sending an LE Credit Based Connection Request does not establish the channel upon receiving an LE Credit Based Connection Response refusing the connection with result "0x0009 – Connection refused – Invalid Source CID".
+        """
+        self._setup_link_from_cert()
+        response_future = self.dut_l2cap.connect_coc_to_cert(self.cert_address, psm=0x33)
+        self.cert_l2cap.verify_and_respond_open_channel_from_remote(
+            psm=0x33, result=LeCreditBasedConnectionResponseResult.INVALID_SOURCE_CID)
+        assertThat(response_future.get_status()).isEqualTo(LeCreditBasedConnectionResponseResult.INVALID_SOURCE_CID)
+
+    @metadata(
+        pts_test_id="L2CAP/LE/CFC/BV-19-C",
+        pts_test_name="LE Credit Based Connection Request - refused due to source CID already allocated - Initiator")
+    def test_request_refused_due_to_source_cid_already_allocated_initiator(self):
+        """
+        Verify that an IUT sending an LE Credit Based Connection Request does not establish the channel upon receiving an LE Credit Based Connection Response refusing the connection with result "0x000A – Connection refused – Source CID already allocated".
+        """
+        self._setup_link_from_cert()
+        response_future = self.dut_l2cap.connect_coc_to_cert(self.cert_address, psm=0x33)
+        self.cert_l2cap.verify_and_respond_open_channel_from_remote(
+            psm=0x33, result=LeCreditBasedConnectionResponseResult.SOURCE_CID_ALREADY_ALLOCATED)
+        assertThat(response_future.get_status()).isEqualTo(
+            LeCreditBasedConnectionResponseResult.SOURCE_CID_ALREADY_ALLOCATED)
+
+    @metadata(
+        pts_test_id="L2CAP/LE/CFC/BV-20-C",
+        pts_test_name="LE Credit Based Connection Response - refused due to Source CID already allocated - Responder")
+    def test_request_refused_due_to_source_cid_already_allocated_responder(self):
+        """
+        Verify that an IUT receiving an LE Credit Based Connection Request for a second channel will refuse the connection with result "0x000A - Connection refused – Source CID already allocated" if it receives a Source CID which is already in use.
+        """
+        self._setup_link_from_cert()
+        (dut_channel, cert_channel) = self._open_channel_from_cert(psm=0x33, scid=0x0101)
+        self.dut_l2cap.register_coc(self.cert_address, psm=0x35)
+        self.cert_l2cap.get_control_channel().send(
+            l2cap_packets.LeCreditBasedConnectionRequestBuilder(2, 0x35, 0x0101, 1000, 1000, 1000))
+        assertThat(self.cert_l2cap.get_control_channel()).emits(L2capMatchers.CreditBasedConnectionResponseUsedCid())
+
+    @metadata(
+        pts_test_id="L2CAP/LE/CFC/BV-21-C",
+        pts_test_name="LE Credit Based Connection Request - refused due to Unacceptable Parameters - Initiator")
+    def test_request_refused_due_to_unacceptable_parameters_initiator(self):
+        """
+        Verify that an IUT sending an LE Credit Based Connection Request does not establish the channel upon receiving an LE Credit Based Connection Response refusing the connection with result "0x000B – Connection refused – Unacceptable Parameters".
+        """
+        self._setup_link_from_cert()
+        response_future = self.dut_l2cap.connect_coc_to_cert(self.cert_address, psm=0x33)
+        self.cert_l2cap.verify_and_respond_open_channel_from_remote(
+            psm=0x33, result=LeCreditBasedConnectionResponseResult.UNACCEPTABLE_PARAMETERS)
+        assertThat(response_future.get_status()).isEqualTo(
+            LeCreditBasedConnectionResponseResult.UNACCEPTABLE_PARAMETERS)
+
+    @metadata(pts_test_id="L2CAP/LE/CFC/BI-01-C", pts_test_name="Credit Exchange – Exceed Initial Credits")
+    def test_credit_exchange_exceed_initial_credits(self):
+        """
+        Verify that the IUT disconnects the LE Data Channel when the credit count exceeds 65535.
+        """
+        self._setup_link_from_cert()
+        (dut_channel, cert_channel) = self._open_channel_from_cert()
+        cert_channel.send_credits(65535)
+        cert_channel.verify_disconnect_request()
diff --git a/gd/neighbor/cert/neighbor_test.py b/gd/neighbor/cert/neighbor_test.py
index 6b744ba..9328b46 100644
--- a/gd/neighbor/cert/neighbor_test.py
+++ b/gd/neighbor/cert/neighbor_test.py
@@ -13,86 +13,19 @@
 #   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.
-
-from datetime import timedelta
-
 from cert.gd_base_test import GdBaseTestClass
-from cert.matchers import HciMatchers, NeighborMatchers
-from cert.py_hci import PyHci
-from cert.truth import assertThat
-from neighbor.cert.py_neighbor import PyNeighbor
-from neighbor.facade import facade_pb2 as neighbor_facade
-from bluetooth_packets_python3 import hci_packets
-from bluetooth_packets_python3.hci_packets import OpCode
+from neighbor.cert.neighbor_test_lib import NeighborTestBase
 
 
-class NeighborTest(GdBaseTestClass):
+class NeighborTest(GdBaseTestClass, NeighborTestBase):
 
     def setup_class(self):
-        super().setup_class(dut_module='HCI_INTERFACES', cert_module='HCI')
+        GdBaseTestClass.setup_class(self, dut_module='HCI_INTERFACES', cert_module='HCI')
 
     def setup_test(self):
-        super().setup_test()
-        self.cert_hci = PyHci(self.cert, acl_streaming=True)
-        self.cert_hci.send_command(hci_packets.WriteScanEnableBuilder(hci_packets.ScanEnable.INQUIRY_AND_PAGE_SCAN))
-        self.cert_name = b'Im_A_Cert'
-        self.cert_address = self.cert_hci.read_own_address()
-        self.cert_name += b'@' + self.cert_address.encode('utf8')
-        self.dut_neighbor = PyNeighbor(self.dut)
+        GdBaseTestClass.setup_test(self)
+        NeighborTestBase.setup_test(self, self.dut, self.cert)
 
     def teardown_test(self):
-        self.cert_hci.close()
-        super().teardown_test()
-
-    def _set_name(self):
-        padded_name = self.cert_name
-        while len(padded_name) < 248:
-            padded_name = padded_name + b'\0'
-        self.cert_hci.send_command(hci_packets.WriteLocalNameBuilder(padded_name))
-
-        assertThat(self.cert_hci.get_event_stream()).emits(HciMatchers.CommandComplete(OpCode.WRITE_LOCAL_NAME))
-
-    def test_inquiry_from_dut(self):
-        inquiry_msg = neighbor_facade.InquiryMsg(
-            inquiry_mode=neighbor_facade.DiscoverabilityMode.GENERAL,
-            result_mode=neighbor_facade.ResultMode.STANDARD,
-            length_1_28s=3,
-            max_results=0)
-        session = self.dut_neighbor.set_inquiry_mode(inquiry_msg)
-        self.cert_hci.send_command(hci_packets.WriteScanEnableBuilder(hci_packets.ScanEnable.INQUIRY_AND_PAGE_SCAN))
-        assertThat(session).emits(NeighborMatchers.InquiryResult(self.cert_address), timeout=timedelta(seconds=10))
-
-    def test_inquiry_rssi_from_dut(self):
-        inquiry_msg = neighbor_facade.InquiryMsg(
-            inquiry_mode=neighbor_facade.DiscoverabilityMode.GENERAL,
-            result_mode=neighbor_facade.ResultMode.RSSI,
-            length_1_28s=6,
-            max_results=0)
-        session = self.dut_neighbor.set_inquiry_mode(inquiry_msg)
-        self.cert_hci.send_command(hci_packets.WriteScanEnableBuilder(hci_packets.ScanEnable.INQUIRY_AND_PAGE_SCAN))
-        assertThat(session).emits(
-            NeighborMatchers.InquiryResultwithRssi(self.cert_address), timeout=timedelta(seconds=10))
-
-    def test_inquiry_extended_from_dut(self):
-        self._set_name()
-        gap_name = hci_packets.GapData()
-        gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
-        gap_name.data = list(bytes(self.cert_name))
-        gap_data = list([gap_name])
-
-        self.cert_hci.send_command(
-            hci_packets.WriteExtendedInquiryResponseBuilder(hci_packets.FecRequired.NOT_REQUIRED, gap_data))
-        inquiry_msg = neighbor_facade.InquiryMsg(
-            inquiry_mode=neighbor_facade.DiscoverabilityMode.GENERAL,
-            result_mode=neighbor_facade.ResultMode.EXTENDED,
-            length_1_28s=8,
-            max_results=0)
-        session = self.dut_neighbor.set_inquiry_mode(inquiry_msg)
-        self.cert_hci.send_command(hci_packets.WriteScanEnableBuilder(hci_packets.ScanEnable.INQUIRY_AND_PAGE_SCAN))
-        assertThat(session).emits(
-            NeighborMatchers.ExtendedInquiryResult(self.cert_address), timeout=timedelta(seconds=10))
-
-    def test_remote_name(self):
-        self._set_name()
-        session = self.dut_neighbor.get_remote_name(self.cert_address)
-        session.verify_name(self.cert_name)
+        NeighborTestBase.teardown_test(self)
+        GdBaseTestClass.teardown_test(self)
diff --git a/gd/neighbor/cert/neighbor_test_lib.py b/gd/neighbor/cert/neighbor_test_lib.py
new file mode 100644
index 0000000..e4b50f8
--- /dev/null
+++ b/gd/neighbor/cert/neighbor_test_lib.py
@@ -0,0 +1,92 @@
+#!/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.
+
+from datetime import timedelta
+
+from cert.matchers import HciMatchers, NeighborMatchers
+from cert.py_hci import PyHci
+from cert.truth import assertThat
+from neighbor.cert.py_neighbor import PyNeighbor
+from neighbor.facade import facade_pb2 as neighbor_facade
+from bluetooth_packets_python3 import hci_packets
+from bluetooth_packets_python3.hci_packets import OpCode
+
+
+class NeighborTestBase():
+
+    def setup_test(self, dut, cert):
+        self.cert_hci = PyHci(cert, acl_streaming=True)
+        self.cert_hci.send_command(hci_packets.WriteScanEnableBuilder(hci_packets.ScanEnable.INQUIRY_AND_PAGE_SCAN))
+        self.cert_name = b'Im_A_Cert'
+        self.cert_address = self.cert_hci.read_own_address()
+        self.cert_name += b'@' + self.cert_address.encode('utf8')
+        self.dut_neighbor = PyNeighbor(dut)
+
+    def teardown_test(self):
+        self.cert_hci.close()
+
+    def _set_name(self):
+        padded_name = self.cert_name
+        while len(padded_name) < 248:
+            padded_name = padded_name + b'\0'
+        self.cert_hci.send_command(hci_packets.WriteLocalNameBuilder(padded_name))
+
+        assertThat(self.cert_hci.get_event_stream()).emits(HciMatchers.CommandComplete(OpCode.WRITE_LOCAL_NAME))
+
+    def test_inquiry_from_dut(self):
+        inquiry_msg = neighbor_facade.InquiryMsg(
+            inquiry_mode=neighbor_facade.DiscoverabilityMode.GENERAL,
+            result_mode=neighbor_facade.ResultMode.STANDARD,
+            length_1_28s=3,
+            max_results=0)
+        session = self.dut_neighbor.set_inquiry_mode(inquiry_msg)
+        self.cert_hci.send_command(hci_packets.WriteScanEnableBuilder(hci_packets.ScanEnable.INQUIRY_AND_PAGE_SCAN))
+        assertThat(session).emits(NeighborMatchers.InquiryResult(self.cert_address), timeout=timedelta(seconds=10))
+
+    def test_inquiry_rssi_from_dut(self):
+        inquiry_msg = neighbor_facade.InquiryMsg(
+            inquiry_mode=neighbor_facade.DiscoverabilityMode.GENERAL,
+            result_mode=neighbor_facade.ResultMode.RSSI,
+            length_1_28s=6,
+            max_results=0)
+        session = self.dut_neighbor.set_inquiry_mode(inquiry_msg)
+        self.cert_hci.send_command(hci_packets.WriteScanEnableBuilder(hci_packets.ScanEnable.INQUIRY_AND_PAGE_SCAN))
+        assertThat(session).emits(
+            NeighborMatchers.InquiryResultwithRssi(self.cert_address), timeout=timedelta(seconds=10))
+
+    def test_inquiry_extended_from_dut(self):
+        self._set_name()
+        gap_name = hci_packets.GapData()
+        gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+        gap_name.data = list(bytes(self.cert_name))
+        gap_data = list([gap_name])
+
+        self.cert_hci.send_command(
+            hci_packets.WriteExtendedInquiryResponseBuilder(hci_packets.FecRequired.NOT_REQUIRED, gap_data))
+        inquiry_msg = neighbor_facade.InquiryMsg(
+            inquiry_mode=neighbor_facade.DiscoverabilityMode.GENERAL,
+            result_mode=neighbor_facade.ResultMode.EXTENDED,
+            length_1_28s=8,
+            max_results=0)
+        session = self.dut_neighbor.set_inquiry_mode(inquiry_msg)
+        self.cert_hci.send_command(hci_packets.WriteScanEnableBuilder(hci_packets.ScanEnable.INQUIRY_AND_PAGE_SCAN))
+        assertThat(session).emits(
+            NeighborMatchers.ExtendedInquiryResult(self.cert_address), timeout=timedelta(seconds=10))
+
+    def test_remote_name(self):
+        self._set_name()
+        session = self.dut_neighbor.get_remote_name(self.cert_address)
+        session.verify_name(self.cert_name)
diff --git a/gd/rust/chromeos/examples/upstart/btmanagerd.conf b/gd/rust/chromeos/examples/upstart/btmanagerd.conf
new file mode 100644
index 0000000..0004a90
--- /dev/null
+++ b/gd/rust/chromeos/examples/upstart/btmanagerd.conf
@@ -0,0 +1,7 @@
+description     "Start the bluetooth manager daemon"
+author          "chromium-os-dev@chromium.org"
+
+start on started system-services
+stop on stopping system-services
+
+exec btmanagerd
\ No newline at end of file
diff --git a/gd/rust/linux/client/Cargo.toml b/gd/rust/linux/client/Cargo.toml
index 04c3b34..92a9bc4 100644
--- a/gd/rust/linux/client/Cargo.toml
+++ b/gd/rust/linux/client/Cargo.toml
@@ -6,7 +6,6 @@
 [dependencies]
 rustyline = "8.0"
 bt_topshim = { path = "../../topshim" }
-bt_shim = { path = "../../shim" }
 btstack = { path = "../stack" }
 
 dbus = "0.9.2"
@@ -26,4 +25,3 @@
 [[bin]]
 name = "btclient"
 path = "src/main.rs"
-build = "build.rs"
diff --git a/gd/rust/linux/client/build.rs b/gd/rust/linux/client/build.rs
deleted file mode 100644
index 41f4083..0000000
--- a/gd/rust/linux/client/build.rs
+++ /dev/null
@@ -1,27 +0,0 @@
-use pkg_config::Config;
-
-fn main() {
-    let target_dir = std::env::var_os("CARGO_TARGET_DIR").unwrap();
-
-    // The main linking point with c++ code is the libbluetooth-static.a
-    // These includes all the symbols built via C++ but doesn't include other
-    // links (i.e. pkg-config)
-    println!("cargo:rustc-link-lib=static=bluetooth-static");
-    println!("cargo:rustc-link-search=native={}", target_dir.into_string().unwrap());
-
-    // A few dynamic links
-    println!("cargo:rustc-link-lib=dylib=flatbuffers");
-    println!("cargo:rustc-link-lib=dylib=protobuf");
-    println!("cargo:rustc-link-lib=dylib=resolv");
-
-    // Clang requires -lc++ instead of -lstdc++
-    println!("cargo:rustc-link-lib=c++");
-
-    // A few more dependencies from pkg-config. These aren't included as part of
-    // the libbluetooth-static.a
-    Config::new().probe("libchrome").unwrap();
-    Config::new().probe("libmodp_b64").unwrap();
-    Config::new().probe("tinyxml2").unwrap();
-
-    println!("cargo:rerun-if-changed=build.rs");
-}
diff --git a/gd/rust/linux/client/src/main.rs b/gd/rust/linux/client/src/main.rs
index 45f41fe..c3fc15b 100644
--- a/gd/rust/linux/client/src/main.rs
+++ b/gd/rust/linux/client/src/main.rs
@@ -1,12 +1,7 @@
-extern crate bt_shim;
-
-use bt_topshim::btif::get_btinterface;
 use bt_topshim::topstack;
-use btstack::bluetooth::{
-    get_bt_dispatcher, Bluetooth, BluetoothDevice, IBluetooth, IBluetoothCallback,
-};
 
-use btstack::{RPCProxy, Stack};
+use btstack::bluetooth::{BluetoothDevice, IBluetooth, IBluetoothCallback};
+use btstack::RPCProxy;
 
 use dbus::channel::MatchingReceiver;
 
@@ -29,7 +24,6 @@
 mod editor;
 
 struct BtCallback {
-    disconnect_callbacks: Arc<Mutex<Vec<Box<dyn Fn() + Send>>>>,
     objpath: String,
 }
 
@@ -52,9 +46,7 @@
 }
 
 impl RPCProxy for BtCallback {
-    fn register_disconnect(&mut self, f: Box<dyn Fn() + Send>) {
-        self.disconnect_callbacks.lock().unwrap().push(f);
-    }
+    fn register_disconnect(&mut self, _f: Box<dyn Fn() + Send>) {}
 
     fn get_object_id(&self) -> String {
         self.objpath.clone()
@@ -65,25 +57,6 @@
     bluetooth: Arc<Mutex<Box<T>>>,
 }
 
-// This creates the API implementations directly embedded to this client.
-// TODO: Remove when D-Bus client is completed since this is only useful while D-Bus client is
-// under development.
-#[allow(dead_code)]
-fn create_api_embedded() -> API<Bluetooth> {
-    let (tx, rx) = Stack::create_channel();
-
-    let intf = Arc::new(Mutex::new(get_btinterface().unwrap()));
-    let bluetooth = Arc::new(Mutex::new(Box::new(Bluetooth::new(tx.clone(), intf.clone()))));
-
-    intf.lock().unwrap().initialize(get_bt_dispatcher(tx), vec![]);
-
-    bluetooth.lock().unwrap().init_profiles();
-
-    topstack::get_runtime().spawn(Stack::dispatch(rx, bluetooth.clone()));
-
-    API { bluetooth }
-}
-
 // This creates the API implementations over D-Bus.
 fn create_api_dbus(conn: Arc<SyncConnection>, cr: Arc<Mutex<Crossroads>>) -> API<BluetoothDBus> {
     let bluetooth = Arc::new(Mutex::new(Box::new(BluetoothDBus::new(conn.clone(), cr))));
@@ -125,20 +98,12 @@
 
         let api = create_api_dbus(conn, cr);
 
-        let dc_callbacks = Arc::new(Mutex::new(vec![]));
         api.bluetooth.lock().unwrap().register_callback(Box::new(BtCallback {
-            disconnect_callbacks: dc_callbacks.clone(),
             objpath: String::from("/org/chromium/bluetooth/client/bluetooth_callback"),
         }));
 
         let handler = CommandHandler::<BluetoothDBus>::new(api.bluetooth.clone());
 
-        let simulate_disconnect = move |_cmd| {
-            for callback in &*dc_callbacks.lock().unwrap() {
-                callback();
-            }
-        };
-
         let handle_cmd = move |cmd: String| match cmd.split(' ').collect::<Vec<&str>>()[0] {
             "enable" => handler.cmd_enable(cmd),
             "disable" => handler.cmd_disable(cmd),
@@ -147,10 +112,6 @@
             "cancel_discovery" => handler.cmd_cancel_discovery(cmd),
             "create_bond" => handler.cmd_create_bond(cmd),
 
-            // Simulate client disconnection. Only useful in embedded mode. In D-Bus mode there is
-            // real D-Bus disconnection.
-            "simulate_disconnect" => simulate_disconnect(cmd),
-
             // Ignore empty commands.
             "" => {}
 
diff --git a/gd/rust/linux/mgmt/Cargo.toml b/gd/rust/linux/mgmt/Cargo.toml
index e182273..4b3128b 100644
--- a/gd/rust/linux/mgmt/Cargo.toml
+++ b/gd/rust/linux/mgmt/Cargo.toml
@@ -15,6 +15,7 @@
 dbus-tokio = "0.7.3"
 dbus-crossroads = "0.3.0"
 inotify = "*"
+log = "0.4.14"
 nix = "*"
 regex = "1.5"
 serde_json = "1.0"
diff --git a/gd/rust/linux/mgmt/src/bin/btmanagerd/main.rs b/gd/rust/linux/mgmt/src/bin/btmanagerd/main.rs
index 43ab8ca..9c9a68a 100644
--- a/gd/rust/linux/mgmt/src/bin/btmanagerd/main.rs
+++ b/gd/rust/linux/mgmt/src/bin/btmanagerd/main.rs
@@ -7,11 +7,31 @@
 use dbus::nonblock::SyncConnection;
 use dbus_crossroads::Crossroads;
 use dbus_tokio::connection;
+use log::{error, info, warn};
+use log::{Level, LevelFilter, Metadata, Record, SetLoggerError};
 use std::process::Command;
 use std::sync::atomic::{AtomicBool, Ordering};
 use std::sync::Arc;
 use tokio::sync::Mutex;
 
+struct SimpleLogger;
+
+impl log::Log for SimpleLogger {
+    fn enabled(&self, metadata: &Metadata) -> bool {
+        true || metadata.level() <= Level::Info
+    }
+
+    fn log(&self, record: &Record) {
+        if self.enabled(record.metadata()) {
+            println!("{} - {}", record.level(), record.args());
+        }
+    }
+
+    fn flush(&self) {}
+}
+
+static LOGGER: SimpleLogger = SimpleLogger;
+
 const BLUEZ_INIT_TARGET: &str = "bluetoothd";
 
 #[derive(Clone)]
@@ -25,6 +45,8 @@
 
 #[tokio::main]
 pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
+    log::set_logger(&LOGGER).map(|()| log::set_max_level(LevelFilter::Debug)).unwrap();
+
     // Initialize config util
     config_util::fix_config_file_format();
 
@@ -79,10 +101,9 @@
             (),
             |mut ctx, cr, (hci_interface,): (i32,)| {
                 if !config_util::modify_hci_n_enabled(hci_interface, true) {
-                    println!("Config is not successfully modified");
+                    error!("Config is not successfully modified");
                 }
                 let proxy = cr.data_mut::<ManagerContext>(ctx.path()).unwrap().proxy.clone();
-                println!("Incoming Start call for hci {}!", hci_interface);
                 async move {
                     let result = proxy.start_bluetooth(hci_interface).await;
                     match result {
@@ -101,7 +122,7 @@
             |mut ctx, cr, (hci_interface,): (i32,)| {
                 let proxy = cr.data_mut::<ManagerContext>(ctx.path()).unwrap().proxy.clone();
                 if !config_util::modify_hci_n_enabled(hci_interface, false) {
-                    println!("Config is not successfully modified");
+                    error!("Config is not successfully modified");
                 }
                 async move {
                     let result = proxy.stop_bluetooth(hci_interface).await;
@@ -188,7 +209,9 @@
                             .expect("failed to stop bluetoothd");
                         // TODO: Implement multi-hci case
                         let default_device = config_util::list_hci_devices()[0];
-                        let _ = proxy.start_bluetooth(default_device).await;
+                        if config_util::is_hci_n_enabled(default_device) {
+                            let _ = proxy.start_bluetooth(default_device).await;
+                        }
                     } else if prev != enabled {
                         // TODO: Implement multi-hci case
                         let default_device = config_util::list_hci_devices()[0];
diff --git a/gd/rust/linux/mgmt/src/bin/btmanagerd/state_machine.rs b/gd/rust/linux/mgmt/src/bin/btmanagerd/state_machine.rs
index bec83b8..2599c27 100644
--- a/gd/rust/linux/mgmt/src/bin/btmanagerd/state_machine.rs
+++ b/gd/rust/linux/mgmt/src/bin/btmanagerd/state_machine.rs
@@ -1,4 +1,5 @@
 use bt_common::time::Alarm;
+use log::{debug, error, info, warn};
 use nix::sys::signal::{self, Signal};
 use nix::unistd::Pid;
 use regex::Regex;
@@ -6,7 +7,7 @@
 use std::sync::Arc;
 use std::time::Duration;
 use tokio::io::unix::AsyncFd;
-use tokio::sync::mpsc::error::SendError;
+use tokio::sync::mpsc::error::SendTimeoutError;
 use tokio::sync::{mpsc, Mutex};
 
 // Directory for Bluetooth pid file
@@ -50,7 +51,7 @@
     where
         PM: ProcessManager + Send,
     {
-        let (tx, rx) = mpsc::channel::<StateMachineActions>(1);
+        let (tx, rx) = mpsc::channel::<StateMachineActions>(10);
         StateMachineContext { tx: tx, rx: rx, state_machine: state_machine }
     }
 
@@ -69,19 +70,32 @@
     state: Arc<Mutex<State>>,
 }
 
+const TX_SEND_TIMEOUT_DURATION: Duration = Duration::from_secs(3);
+const COMMAND_TIMEOUT_DURATION: Duration = Duration::from_secs(3);
+
 impl StateMachineProxy {
     pub async fn start_bluetooth(
         &self,
         hci_interface: i32,
-    ) -> Result<(), SendError<StateMachineActions>> {
-        self.tx.send(StateMachineActions::StartBluetooth(hci_interface)).await
+    ) -> Result<(), SendTimeoutError<StateMachineActions>> {
+        self.tx
+            .send_timeout(
+                StateMachineActions::StartBluetooth(hci_interface),
+                TX_SEND_TIMEOUT_DURATION,
+            )
+            .await
     }
 
     pub async fn stop_bluetooth(
         &self,
         hci_interface: i32,
-    ) -> Result<(), SendError<StateMachineActions>> {
-        self.tx.send(StateMachineActions::StopBluetooth(hci_interface)).await
+    ) -> Result<(), SendTimeoutError<StateMachineActions>> {
+        self.tx
+            .send_timeout(
+                StateMachineActions::StopBluetooth(hci_interface),
+                TX_SEND_TIMEOUT_DURATION,
+            )
+            .await
     }
 
     pub async fn get_state(&self) -> State {
@@ -125,7 +139,6 @@
     PM: ProcessManager + Send,
 {
     let mut command_timeout = Alarm::new();
-    let command_timeout_duration = Duration::from_secs(2);
     let mut pid_async_fd = pid_inotify_async_fd();
     let mut hci_devices_async_fd = hci_devices_inotify_async_fd();
     loop {
@@ -133,42 +146,47 @@
             Some(action) = context.rx.recv() => {
               match action {
                 StateMachineActions::StartBluetooth(i) => {
-                    match context.state_machine.action_start_bluetooth(i) {
+                    match context.state_machine.action_start_bluetooth(i).await {
                         true => {
-                            command_timeout.reset(command_timeout_duration);
+                            command_timeout.reset(COMMAND_TIMEOUT_DURATION);
                         },
-                        false => (),
+                        false => command_timeout.cancel(),
                     }
                 },
                 StateMachineActions::StopBluetooth(i) => {
-                  match context.state_machine.action_stop_bluetooth(i) {
-                      true => command_timeout.reset(command_timeout_duration),
-                      false => (),
+                  match context.state_machine.action_stop_bluetooth(i).await {
+                      true => {
+                          command_timeout.reset(COMMAND_TIMEOUT_DURATION);
+                      },
+                      false => command_timeout.cancel(),
                   }
                 },
                 StateMachineActions::BluetoothStarted(pid, hci) => {
-                  match context.state_machine.action_on_bluetooth_started(pid, hci) {
-                      true => command_timeout.cancel(),
-                      false => println!("unexpected BluetoothStarted pid{} hci{}", pid, hci),
+                  match context.state_machine.action_on_bluetooth_started(pid, hci).await {
+                      true => {
+                          command_timeout.cancel();
+                      }
+                      false => error!("unexpected BluetoothStarted pid{} hci{}", pid, hci),
                   }
                 },
                 StateMachineActions::BluetoothStopped() => {
-                  match context.state_machine.action_on_bluetooth_stopped() {
-                      true => command_timeout.cancel(),
+                  match context.state_machine.action_on_bluetooth_stopped().await {
+                      true => {
+                          command_timeout.cancel();
+                      }
                       false => {
-                        println!("BluetoothStopped");
-                          command_timeout.reset(command_timeout_duration);
+                          command_timeout.reset(COMMAND_TIMEOUT_DURATION);
                       }
                   }
                 },
               }
             },
-            _ = command_timeout.expired() => {
-                println!("expired {:?}", *context.state_machine.state.lock().await);
-                let timeout_action = context.state_machine.action_on_command_timeout();
+            expired = command_timeout.expired() => {
+                info!("expired {:?}", *context.state_machine.state.lock().await);
+                let timeout_action = context.state_machine.action_on_command_timeout().await;
                 match timeout_action {
                     StateMachineTimeoutActions::Noop => (),
-                    _ => command_timeout.reset(command_timeout_duration),
+                    _ => command_timeout.reset(COMMAND_TIMEOUT_DURATION),
                 }
             },
             r = pid_async_fd.readable_mut() => {
@@ -176,8 +194,8 @@
                 let mut buffer: [u8; 1024] = [0; 1024];
                 match fd_ready.try_io(|inner| inner.get_mut().read_events(&mut buffer)) {
                     Ok(Ok(events)) => {
-                        println!("got some events");
                         for event in events {
+                            debug!("got some events from pid {:?}", event.mask);
                             match (event.mask, event.name) {
                                 (inotify::EventMask::CREATE, Some(oss)) => {
                                     let path = std::path::Path::new(PID_DIR).join(oss);
@@ -185,21 +203,18 @@
                                     match (get_hci_interface_from_pid_file_name(file_name), tokio::fs::read(path).await.ok()) {
                                         (Some(hci), Some(s)) => {
                                             let pid = String::from_utf8(s).expect("invalid pid file").parse::<i32>().unwrap_or(0);
-                                            let _ = context.tx.send(StateMachineActions::BluetoothStarted(pid, hci)).await;
+                                            let _ = context.tx.send_timeout(StateMachineActions::BluetoothStarted(pid, hci), TX_SEND_TIMEOUT_DURATION).await.unwrap();
                                         },
-                                        (hci, s) => println!("invalid file hci={:?} pid_file={:?}", hci, s),
+                                        (hci, s) => warn!("invalid file hci={:?} pid_file={:?}", hci, s),
                                     }
                                 },
                                 (inotify::EventMask::DELETE, Some(oss)) => {
                                     let file_name = oss.to_str().unwrap_or("invalid file");
-                                    match get_hci_interface_from_pid_file_name(file_name) {
-                                        Some(hci) => {
-                                            let _ = context.tx.send(StateMachineActions::BluetoothStopped()).await;
-                                        },
-                                        _ => (),
+                                    if let Some(hci) = get_hci_interface_from_pid_file_name(file_name) {
+                                        context.tx.send_timeout(StateMachineActions::BluetoothStopped(), TX_SEND_TIMEOUT_DURATION).await.unwrap();
                                     }
                                 },
-                                _ => println!("Ignored event {:?}", event.mask)
+                                _ => debug!("Ignored event {:?}", event.mask)
                             }
                         }
                     },
@@ -231,7 +246,7 @@
                                         _ => (),
                                     }
                                 },
-                                _ => println!("Ignored event {:?}", event.mask)
+                                _ => debug!("Ignored event {:?}", event.mask)
                             }
                         }
                     },
@@ -277,7 +292,7 @@
                 self.process_container = None;
             }
             None => {
-                println!("Process doesn't exist");
+                warn!("Process doesn't exist");
             }
         }
     }
@@ -347,8 +362,8 @@
     }
 
     /// Returns true if we are starting bluetooth process.
-    pub fn action_start_bluetooth(&mut self, hci_interface: i32) -> bool {
-        let mut state = self.state.try_lock().unwrap();
+    pub async fn action_start_bluetooth(&mut self, hci_interface: i32) -> bool {
+        let mut state = self.state.lock().await;
         match *state {
             State::Off => {
                 *state = State::TurningOn;
@@ -362,39 +377,44 @@
     }
 
     /// Returns true if we are stopping bluetooth process.
-    pub fn action_stop_bluetooth(&mut self, hci_interface: i32) -> bool {
+    pub async fn action_stop_bluetooth(&mut self, hci_interface: i32) -> bool {
         if self.hci_interface != hci_interface {
-            println!(
+            warn!(
                 "We are running hci{} but attempting to stop hci{}",
                 self.hci_interface, hci_interface
             );
             return false;
         }
 
-        let mut state = self.state.try_lock().unwrap();
+        let mut state = self.state.lock().await;
         match *state {
-            State::On | State::TurningOn => {
+            State::On => {
                 *state = State::TurningOff;
                 self.process_manager.stop(self.hci_interface.to_string());
                 true
             }
+            State::TurningOn => {
+                *state = State::Off;
+                self.process_manager.stop(self.hci_interface.to_string());
+                false
+            }
             // Otherwise no op
             _ => false,
         }
     }
 
     /// Returns true if the event is expected.
-    pub fn action_on_bluetooth_started(&mut self, pid: i32, hci_interface: i32) -> bool {
-        let mut state = self.state.try_lock().unwrap();
+    pub async fn action_on_bluetooth_started(&mut self, pid: i32, hci_interface: i32) -> bool {
+        let mut state = self.state.lock().await;
         if self.hci_interface != hci_interface {
-            println!(
+            warn!(
                 "We should start hci{} but hci{} is started; capturing that process",
                 self.hci_interface, hci_interface
             );
             self.hci_interface = hci_interface;
         }
         if *state != State::TurningOn {
-            println!("Unexpected Bluetooth started");
+            warn!("Unexpected Bluetooth started");
         }
         *state = State::On;
         self.bluetooth_pid = pid;
@@ -404,9 +424,8 @@
     /// Returns true if the event is expected.
     /// If unexpected, Bluetooth probably crashed;
     /// start the timer for restart timeout
-    pub fn action_on_bluetooth_stopped(&mut self) -> bool {
-        // Need to check if file exists
-        let mut state = self.state.try_lock().unwrap();
+    pub async fn action_on_bluetooth_stopped(&mut self) -> bool {
+        let mut state = self.state.lock().await;
 
         match *state {
             State::TurningOff => {
@@ -414,7 +433,7 @@
                 true
             }
             State::On => {
-                println!("Bluetooth stopped unexpectedly, try restarting");
+                warn!("Bluetooth stopped unexpectedly, try restarting");
                 *state = State::TurningOn;
                 self.process_manager.start(format!("{}", self.hci_interface));
                 false
@@ -428,22 +447,22 @@
 
     /// Triggered on Bluetooth start/stop timeout.  Return the actions that the
     /// state machine has taken, for the external context to reset the timer.
-    pub fn action_on_command_timeout(&mut self) -> StateMachineTimeoutActions {
-        let mut state = self.state.try_lock().unwrap();
+    pub async fn action_on_command_timeout(&mut self) -> StateMachineTimeoutActions {
+        let mut state = self.state.lock().await;
         match *state {
             State::TurningOn => {
-                println!("Restarting bluetooth");
+                info!("Restarting bluetooth");
                 *state = State::TurningOn;
+                self.process_manager.stop(format! {"{}", self.hci_interface});
                 self.process_manager.start(format! {"{}", self.hci_interface});
                 StateMachineTimeoutActions::RetryStart
             }
             State::TurningOff => {
-                println!("Killing bluetooth");
-
-                *state = State::Off;
+                info!("Killing bluetooth");
+                self.process_manager.stop(format! {"{}", self.hci_interface});
                 StateMachineTimeoutActions::RetryStop
             }
-            _ => panic!("Unexpected timeout on {:?}", *state),
+            _ => StateMachineTimeoutActions::Noop,
         }
     }
 }
@@ -497,128 +516,151 @@
 
     #[test]
     fn initial_state_is_off() {
-        let process_manager = MockProcessManager::new();
-        let state_machine = ManagerStateMachine::new(process_manager);
-        assert_eq!(*state_machine.state.try_lock().unwrap(), State::Off);
+        tokio::runtime::Runtime::new().unwrap().block_on(async {
+            let process_manager = MockProcessManager::new();
+            let state_machine = ManagerStateMachine::new(process_manager);
+            assert_eq!(*state_machine.state.lock().await, State::Off);
+        })
     }
 
     #[test]
     fn off_turnoff_should_noop() {
-        let process_manager = MockProcessManager::new();
-        let mut state_machine = ManagerStateMachine::new(process_manager);
-        state_machine.action_stop_bluetooth(0);
-        assert_eq!(*state_machine.state.try_lock().unwrap(), State::Off);
+        tokio::runtime::Runtime::new().unwrap().block_on(async {
+            let process_manager = MockProcessManager::new();
+            let mut state_machine = ManagerStateMachine::new(process_manager);
+            state_machine.action_stop_bluetooth(0).await;
+            assert_eq!(*state_machine.state.lock().await, State::Off);
+        })
     }
 
     #[test]
     fn off_turnon_should_turningon() {
-        let mut process_manager = MockProcessManager::new();
-        // Expect to send start command
-        process_manager.expect_start();
-        let mut state_machine = ManagerStateMachine::new(process_manager);
-        state_machine.action_start_bluetooth(0);
-        assert_eq!(*state_machine.state.try_lock().unwrap(), State::TurningOn);
+        tokio::runtime::Runtime::new().unwrap().block_on(async {
+            let mut process_manager = MockProcessManager::new();
+            // Expect to send start command
+            process_manager.expect_start();
+            let mut state_machine = ManagerStateMachine::new(process_manager);
+            state_machine.action_start_bluetooth(0).await;
+            assert_eq!(*state_machine.state.lock().await, State::TurningOn);
+        })
     }
 
     #[test]
     fn turningon_turnon_again_noop() {
-        let mut process_manager = MockProcessManager::new();
-        // Expect to send start command just once
-        process_manager.expect_start();
-        let mut state_machine = ManagerStateMachine::new(process_manager);
-        state_machine.action_start_bluetooth(0);
-        assert_eq!(state_machine.action_start_bluetooth(0), false);
+        tokio::runtime::Runtime::new().unwrap().block_on(async {
+            let mut process_manager = MockProcessManager::new();
+            // Expect to send start command just once
+            process_manager.expect_start();
+            let mut state_machine = ManagerStateMachine::new(process_manager);
+            state_machine.action_start_bluetooth(0).await;
+            assert_eq!(state_machine.action_start_bluetooth(0).await, false);
+        })
     }
 
     #[test]
     fn turningon_bluetooth_started() {
-        let mut process_manager = MockProcessManager::new();
-        process_manager.expect_start();
-        let mut state_machine = ManagerStateMachine::new(process_manager);
-        state_machine.action_start_bluetooth(0);
-        state_machine.action_on_bluetooth_started(0, 0);
-        assert_eq!(*state_machine.state.try_lock().unwrap(), State::On);
+        tokio::runtime::Runtime::new().unwrap().block_on(async {
+            let mut process_manager = MockProcessManager::new();
+            process_manager.expect_start();
+            let mut state_machine = ManagerStateMachine::new(process_manager);
+            state_machine.action_start_bluetooth(0).await;
+            state_machine.action_on_bluetooth_started(0, 0).await;
+            assert_eq!(*state_machine.state.lock().await, State::On);
+        })
     }
 
     #[test]
     fn turningon_timeout() {
-        let mut process_manager = MockProcessManager::new();
-        process_manager.expect_start();
-        process_manager.expect_start(); // start bluetooth again
-        let mut state_machine = ManagerStateMachine::new(process_manager);
-        state_machine.action_start_bluetooth(0);
-        assert_eq!(
-            state_machine.action_on_command_timeout(),
-            StateMachineTimeoutActions::RetryStart
-        );
-        assert_eq!(*state_machine.state.try_lock().unwrap(), State::TurningOn);
+        tokio::runtime::Runtime::new().unwrap().block_on(async {
+            let mut process_manager = MockProcessManager::new();
+            process_manager.expect_start();
+            process_manager.expect_stop();
+            process_manager.expect_start(); // start bluetooth again
+            let mut state_machine = ManagerStateMachine::new(process_manager);
+            state_machine.action_start_bluetooth(0).await;
+            assert_eq!(
+                state_machine.action_on_command_timeout().await,
+                StateMachineTimeoutActions::RetryStart
+            );
+            assert_eq!(*state_machine.state.lock().await, State::TurningOn);
+        })
     }
 
     #[test]
     fn turningon_turnoff_should_turningoff_and_send_command() {
-        let mut process_manager = MockProcessManager::new();
-        process_manager.expect_start();
-        // Expect to send stop command
-        process_manager.expect_stop();
-        let mut state_machine = ManagerStateMachine::new(process_manager);
-        state_machine.action_start_bluetooth(0);
-        state_machine.action_stop_bluetooth(0);
-        assert_eq!(*state_machine.state.try_lock().unwrap(), State::TurningOff);
+        tokio::runtime::Runtime::new().unwrap().block_on(async {
+            let mut process_manager = MockProcessManager::new();
+            process_manager.expect_start();
+            // Expect to send stop command
+            process_manager.expect_stop();
+            let mut state_machine = ManagerStateMachine::new(process_manager);
+            state_machine.action_start_bluetooth(0).await;
+            state_machine.action_stop_bluetooth(0).await;
+            assert_eq!(*state_machine.state.lock().await, State::Off);
+        })
     }
 
     #[test]
     fn on_turnoff_should_turningoff_and_send_command() {
-        let mut process_manager = MockProcessManager::new();
-        process_manager.expect_start();
-        // Expect to send stop command
-        process_manager.expect_stop();
-        let mut state_machine = ManagerStateMachine::new(process_manager);
-        state_machine.action_start_bluetooth(0);
-        state_machine.action_on_bluetooth_started(0, 0);
-        state_machine.action_stop_bluetooth(0);
-        assert_eq!(*state_machine.state.try_lock().unwrap(), State::TurningOff);
+        tokio::runtime::Runtime::new().unwrap().block_on(async {
+            let mut process_manager = MockProcessManager::new();
+            process_manager.expect_start();
+            // Expect to send stop command
+            process_manager.expect_stop();
+            let mut state_machine = ManagerStateMachine::new(process_manager);
+            state_machine.action_start_bluetooth(0).await;
+            state_machine.action_on_bluetooth_started(0, 0).await;
+            state_machine.action_stop_bluetooth(0).await;
+            assert_eq!(*state_machine.state.lock().await, State::TurningOff);
+        })
     }
 
     #[test]
     fn on_bluetooth_stopped() {
-        let mut process_manager = MockProcessManager::new();
-        process_manager.expect_start();
-        // Expect to start again
-        process_manager.expect_start();
-        let mut state_machine = ManagerStateMachine::new(process_manager);
-        state_machine.action_start_bluetooth(0);
-        state_machine.action_on_bluetooth_started(0, 0);
-        assert_eq!(state_machine.action_on_bluetooth_stopped(), false);
-        assert_eq!(*state_machine.state.try_lock().unwrap(), State::TurningOn);
+        tokio::runtime::Runtime::new().unwrap().block_on(async {
+            let mut process_manager = MockProcessManager::new();
+            process_manager.expect_start();
+            // Expect to start again
+            process_manager.expect_start();
+            let mut state_machine = ManagerStateMachine::new(process_manager);
+            state_machine.action_start_bluetooth(0).await;
+            state_machine.action_on_bluetooth_started(0, 0).await;
+            assert_eq!(state_machine.action_on_bluetooth_stopped().await, false);
+            assert_eq!(*state_machine.state.lock().await, State::TurningOn);
+        })
     }
 
     #[test]
     fn turningoff_bluetooth_down_should_off() {
-        let mut process_manager = MockProcessManager::new();
-        process_manager.expect_start();
-        process_manager.expect_stop();
-        let mut state_machine = ManagerStateMachine::new(process_manager);
-        state_machine.action_start_bluetooth(0);
-        state_machine.action_on_bluetooth_started(0, 0);
-        state_machine.action_stop_bluetooth(0);
-        state_machine.action_on_bluetooth_stopped();
-        assert_eq!(*state_machine.state.try_lock().unwrap(), State::Off);
+        tokio::runtime::Runtime::new().unwrap().block_on(async {
+            let mut process_manager = MockProcessManager::new();
+            process_manager.expect_start();
+            process_manager.expect_stop();
+            let mut state_machine = ManagerStateMachine::new(process_manager);
+            state_machine.action_start_bluetooth(0).await;
+            state_machine.action_on_bluetooth_started(0, 0).await;
+            state_machine.action_stop_bluetooth(0).await;
+            state_machine.action_on_bluetooth_stopped().await;
+            assert_eq!(*state_machine.state.lock().await, State::Off);
+        })
     }
 
     #[test]
     fn restart_bluetooth() {
-        let mut process_manager = MockProcessManager::new();
-        process_manager.expect_start();
-        process_manager.expect_stop();
-        process_manager.expect_start();
-        let mut state_machine = ManagerStateMachine::new(process_manager);
-        state_machine.action_start_bluetooth(0);
-        state_machine.action_on_bluetooth_started(0, 0);
-        state_machine.action_stop_bluetooth(0);
-        state_machine.action_on_bluetooth_stopped();
-        state_machine.action_start_bluetooth(0);
-        state_machine.action_on_bluetooth_started(0, 0);
-        assert_eq!(*state_machine.state.try_lock().unwrap(), State::On);
+        tokio::runtime::Runtime::new().unwrap().block_on(async {
+            let mut process_manager = MockProcessManager::new();
+            process_manager.expect_start();
+            process_manager.expect_stop();
+            process_manager.expect_start();
+            let mut state_machine = ManagerStateMachine::new(process_manager);
+            state_machine.action_start_bluetooth(0).await;
+            state_machine.action_on_bluetooth_started(0, 0).await;
+            state_machine.action_stop_bluetooth(0).await;
+            state_machine.action_on_bluetooth_stopped().await;
+            state_machine.action_start_bluetooth(0).await;
+            state_machine.action_on_bluetooth_started(0, 0).await;
+            assert_eq!(*state_machine.state.lock().await, State::On);
+        })
     }
 
     #[test]
diff --git a/gd/rust/linux/mgmt/test/btmanagerd_test.py b/gd/rust/linux/mgmt/test/btmanagerd_test.py
new file mode 100644
index 0000000..d909ae6
--- /dev/null
+++ b/gd/rust/linux/mgmt/test/btmanagerd_test.py
@@ -0,0 +1,122 @@
+#!/usr/bin/python3
+
+#%%
+
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+import time
+import unittest
+
+HCI_DEVICES_DIR = "/sys/class/bluetooth"
+
+# File to store the Bluetooth daemon to use (bluez or floss)
+BLUETOOTH_DAEMON_CURRENT = "/var/lib/misc/bluetooth-daemon.current"
+
+# File to store the config for BluetoothManager
+BTMANAGERD_CONF = "/var/lib/bluetooth/btmanagerd.json"
+
+# D-BUS bus name
+BUS_NAME = "org.chromium.bluetooth.Manager"
+
+# D-Bus Bluetooth Manager object path
+MANAGER_OBJECT_PATH = "/org/chromium/bluetooth/Manager"
+
+# D-Bus Bluetooth Manager interface name
+MANAGER_INTERFACE_NAME = "org.chromium.bluetooth.Manager"
+
+# D-Bus Bluetooth Manager client (this test) object path
+CLIENT_OBJECT_PATH = "/test_client"
+
+
+def use_floss():
+  with open(BLUETOOTH_DAEMON_CURRENT, "w") as f:
+    f.write("floss")
+
+def use_bluez():
+  with open(BLUETOOTH_DAEMON_CURRENT, "w") as f:
+    f.write("floss")
+
+class BtmanagerdTest(unittest.TestCase):
+
+    def setUp(self):
+      self.bus = dbus.SystemBus()
+      self.manager_object = self.bus.get_object(BUS_NAME, MANAGER_OBJECT_PATH)
+      self.client_object = self.bus.get_object(BUS_NAME, CLIENT_OBJECT_PATH)
+      self.manager_object.Stop(0, dbus_interface=MANAGER_INTERFACE_NAME)
+      time.sleep(2.5)
+      self.assertEqual(self._get_state(), 0)
+
+    def _start_hci(self, hci=0):
+      self.manager_object.Start(hci, dbus_interface=MANAGER_INTERFACE_NAME)
+      time.sleep(2.5)
+
+    def _stop_hci(self, hci=0):
+      self.manager_object.Stop(hci, dbus_interface=MANAGER_INTERFACE_NAME)
+      time.sleep(2.5)
+
+    def _get_state(self):
+      return self.manager_object.GetState(dbus_interface=MANAGER_INTERFACE_NAME)
+
+    def _register_hci_device_change_observer(self):
+      return self.manager_object.RegisterStateChangeObserver(self.client_object_path, dbus_interface=MANAGER_INTERFACE_NAME)
+
+    def _unregister_hci_device_change_observer(self):
+      return self.manager_object.UnregisterHciDeviceChangeObserver(self.client_object_path, dbus_interface=MANAGER_INTERFACE_NAME)
+
+    def _get_floss_enabled(self):
+      return self.manager_object.GetFlossEnabled(dbus_interface=MANAGER_INTERFACE_NAME)
+
+    def _set_floss_enabled(self, enabled=True):
+      return self.manager_object.SetFlossEnabled(enabled, dbus_interface=MANAGER_INTERFACE_NAME)
+
+    def _list_hci_devices(self):
+      return self.manager_object.ListHciDevices(dbus_interface=MANAGER_INTERFACE_NAME)
+
+    def _register_hci_device_change_observer(self):
+      return self.manager_object.RegisterHciDeviceChangeObserver(self.client_object_path, dbus_interface=MANAGER_INTERFACE_NAME)
+
+    def _unregister_hci_device_change_observer(self):
+      return self.manager_object.UnregisterHciDeviceChangeObserver(self.client_object_path, dbus_interface=MANAGER_INTERFACE_NAME)
+
+    def test_list_hci_devices(self):
+      self.assertTrue(len(self._list_hci_devices()) > 0)
+
+    def test_floss_enabled(self):
+      self.assertTrue(self._get_floss_enabled())
+
+    def test_disable_floss(self):
+      self._set_floss_enabled(False)
+      self.assertFalse(self._get_floss_enabled())
+      with open(BLUETOOTH_DAEMON_CURRENT, "r") as f:
+        self.assertEqual(f.read(), "bluez")
+
+      self._set_floss_enabled(True)
+      self.assertTrue(self._get_floss_enabled())
+
+    def test_start(self):
+      self._start_hci()
+      self.assertEqual(self._get_state(), 2)
+
+    def test_astart_and_start(self):
+      self._start_hci()
+      self._start_hci()
+      self.assertEqual(self._get_state(), 2)
+
+    def test_stop(self):
+      self._start_hci()
+      self._stop_hci()
+      self.assertEqual(self._get_state(), 0)
+
+    def test_stop_and_stop(self):
+      self._start_hci()
+      self._stop_hci()
+      self._stop_hci()
+      self.assertEqual(self._get_state(), 0)
+
+
+# %%
+if __name__ == '__main__':
+  dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+  use_floss()
+  unittest.main()
diff --git a/gd/security/cert/le_security_test.py b/gd/security/cert/le_security_test.py
index 7a0719b..2c2cee2 100644
--- a/gd/security/cert/le_security_test.py
+++ b/gd/security/cert/le_security_test.py
@@ -13,1085 +13,19 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import time
-
-from bluetooth_packets_python3 import hci_packets
-from bluetooth_packets_python3 import security_packets
-from cert.event_stream import EventStream
 from cert.gd_base_test import GdBaseTestClass
-from cert.matchers import HciMatchers
-from cert.matchers import SecurityMatchers
-from cert.metadata import metadata
-from cert.py_hci import PyHci
-from cert.py_le_security import PyLeSecurity
-from cert.truth import assertThat
-from datetime import timedelta
-from facade import common_pb2 as common
-from hci.facade import controller_facade_pb2 as controller_facade
-from hci.facade import le_advertising_manager_facade_pb2 as le_advertising_facade
-from hci.facade import le_initiator_address_facade_pb2 as le_initiator_address_facade
-from google.protobuf import empty_pb2 as empty_proto
-from neighbor.facade import facade_pb2 as neighbor_facade
-from security.cert.cert_security import CertSecurity
-from security.facade_pb2 import AuthenticationRequirements
-from security.facade_pb2 import BondMsgType
-from security.facade_pb2 import OobDataMessage
-from security.facade_pb2 import UiCallbackMsg
-from security.facade_pb2 import UiCallbackType
-from security.facade_pb2 import UiMsgType
-from security.facade_pb2 import LeAuthRequirementsMessage
-from security.facade_pb2 import LeIoCapabilityMessage
-from security.facade_pb2 import LeOobDataPresentMessage
-from security.facade_pb2 import LeMaximumEncryptionKeySizeMessage
-
-import time
-from bluetooth_packets_python3.hci_packets import OpCode
-from bluetooth_packets_python3.security_packets import PairingFailedReason
-
-from mobly import asserts
-
-LeIoCapabilities = LeIoCapabilityMessage.LeIoCapabilities
-LeOobDataFlag = LeOobDataPresentMessage.LeOobDataFlag
-
-DISPLAY_ONLY = LeIoCapabilityMessage(capabilities=LeIoCapabilities.DISPLAY_ONLY)
-KEYBOARD_ONLY = LeIoCapabilityMessage(capabilities=LeIoCapabilities.KEYBOARD_ONLY)
-NO_INPUT_NO_OUTPUT = LeIoCapabilityMessage(capabilities=LeIoCapabilities.NO_INPUT_NO_OUTPUT)
-KEYBOARD_DISPLAY = LeIoCapabilityMessage(capabilities=LeIoCapabilities.KEYBOARD_DISPLAY)
-
-OOB_NOT_PRESENT = LeOobDataPresentMessage(data_present=LeOobDataFlag.NOT_PRESENT)
-OOB_PRESENT = LeOobDataPresentMessage(data_present=LeOobDataFlag.PRESENT)
+from security.cert.le_security_test_lib import LeSecurityTestBase
 
 
-class LeSecurityTest(GdBaseTestClass):
-    """
-        Collection of tests that each sample results from
-        different (unique) combinations of io capabilities, authentication requirements, and oob data.
-    """
+class LeSecurityTest(GdBaseTestClass, LeSecurityTestBase):
 
     def setup_class(self):
-        super().setup_class(dut_module='SECURITY', cert_module='SECURITY')
+        GdBaseTestClass.setup_class(self, dut_module='SECURITY', cert_module='SECURITY')
 
     def setup_test(self):
-        super().setup_test()
-
-        self.dut_security = PyLeSecurity(self.dut)
-        self.cert_security = PyLeSecurity(self.cert)
-        self.dut_hci = PyHci(self.dut)
-
-        raw_addr = self.dut.hci_controller.GetMacAddress(empty_proto.Empty()).address
-
-        self.dut_address = common.BluetoothAddressWithType(
-            address=common.BluetoothAddress(address=raw_addr), type=common.PUBLIC_DEVICE_ADDRESS)
-        privacy_policy = le_initiator_address_facade.PrivacyPolicy(
-            address_policy=le_initiator_address_facade.AddressPolicy.USE_PUBLIC_ADDRESS,
-            address_with_type=self.dut_address)
-        self.dut.security.SetLeInitiatorAddressPolicy(privacy_policy)
-        self.cert_address = common.BluetoothAddressWithType(
-            address=common.BluetoothAddress(
-                address=self.cert.hci_controller.GetMacAddress(empty_proto.Empty()).address),
-            type=common.PUBLIC_DEVICE_ADDRESS)
-        cert_privacy_policy = le_initiator_address_facade.PrivacyPolicy(
-            address_policy=le_initiator_address_facade.AddressPolicy.USE_PUBLIC_ADDRESS,
-            address_with_type=self.cert_address)
-        self.cert.security.SetLeInitiatorAddressPolicy(cert_privacy_policy)
-
-        asserts.skip("Unhandled race condition - Flaky test")
+        GdBaseTestClass.setup_test(self)
+        LeSecurityTestBase.setup_test(self, self.dut, self.cert)
 
     def teardown_test(self):
-        self.dut_hci.close()
-        self.dut_security.close()
-        self.cert_security.close()
-        super().teardown_test()
-
-    def _prepare_cert_for_connection(self):
-        # DUT Advertises
-        gap_name = hci_packets.GapData()
-        gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
-        gap_name.data = list(bytes(b'Im_The_CERT'))
-        gap_data = le_advertising_facade.GapDataMsg(data=bytes(gap_name.Serialize()))
-        config = le_advertising_facade.AdvertisingConfig(
-            advertisement=[gap_data],
-            interval_min=512,
-            interval_max=768,
-            advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
-            own_address_type=common.USE_PUBLIC_DEVICE_ADDRESS,
-            channel_map=7,
-            filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES)
-        request = le_advertising_facade.CreateAdvertiserRequest(config=config)
-        create_response = self.cert.hci_le_advertising_manager.CreateAdvertiser(request)
-
-    def _prepare_dut_for_connection(self):
-        # DUT Advertises
-        gap_name = hci_packets.GapData()
-        gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
-        gap_name.data = list(bytes(b'Im_The_DUT'))
-        gap_data = le_advertising_facade.GapDataMsg(data=bytes(gap_name.Serialize()))
-        config = le_advertising_facade.AdvertisingConfig(
-            advertisement=[gap_data],
-            interval_min=512,
-            interval_max=768,
-            advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
-            own_address_type=common.USE_PUBLIC_DEVICE_ADDRESS,
-            channel_map=7,
-            filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES)
-        request = le_advertising_facade.CreateAdvertiserRequest(config=config)
-        create_response = self.dut.hci_le_advertising_manager.CreateAdvertiser(request)
-
-    @metadata(pts_test_id="SM/MAS/PROT/BV-01-C", pts_test_name="SMP Time Out – IUT Initiator")
-    def test_le_smp_timeout_iut_initiator(self):
-        """
-            Verify that the IUT handles the lack of pairing response after 30 seconds when acting as initiator.
-        """
-        self._prepare_cert_for_connection()
-        self.dut.security.CreateBondLe(self.cert_address)
-        assertThat(self.dut_security.get_bond_stream()).emits(
-            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BOND_FAILED, self.cert_address), timeout=timedelta(seconds=35))
-
-    @metadata(pts_test_id="SM/SLA/PROT/BV-02-C", pts_test_name="SMP Time Out – IUT Responder")
-    def test_le_smp_timeout_iut_responder(self):
-        """
-            Verify that the IUT handles the lack of pairing response after 30 seconds when acting as initiator.
-        """
-        self.cert.security.SetLeIoCapability(KEYBOARD_ONLY)
-        self.dut.security.SetLeIoCapability(DISPLAY_ONLY)
-
-        self._prepare_dut_for_connection()
-
-        # 1. Lower Tester transmits Pairing Request.
-        self.cert.security.CreateBondLe(self.dut_address)
-
-        assertThat(self.dut_security.get_ui_stream()).emits(
-            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.cert_address), timeout=timedelta(seconds=35))
-
-        # 2. IUT responds with Pairing Response.
-        self.dut.security.SendUiCallback(
-            UiCallbackMsg(
-                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.cert_address))
-
-        # 3. In phase 2, Lower Tester does not issue the expected Pairing Confirm.
-
-        # Here the cert receives DISPLAY_PASSKEY_ENTRY. By not replying to it we make sure Pairing Confirm is never sent
-        assertThat(self.cert_security.get_ui_stream()).emits(
-            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PASSKEY_ENTRY, self.dut_address), timeout=timedelta(seconds=5))
-
-        # 4. IUT times out 30 seconds after issued Pairing Response and reports the failure to the Upper Tester.
-        assertThat(self.dut_security.get_bond_stream()).emits(
-            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BOND_FAILED, self.cert_address), timeout=timedelta(seconds=35))
-
-        # 5. After additionally (at least) 10 seconds the Lower Tester issues the expected Pairing Confirm.
-        # 6. The IUT closes the connection before receiving the delayed response or does not respond to it when it is received.
-        #TODO:
-        #assertThat(self.dut_hci.get_event_stream()).emits(HciMatchers.Disconnect())
-
-    @metadata(pts_test_id="SM/MAS/JW/BV-01-C", pts_test_name="Just Works IUT Initiator – Success")
-    def test_just_works_iut_initiator(self):
-        """
-            Verify that the IUT performs the Just Works pairing procedure correctly as central, initiator when both sides do not require MITM protection.
-        """
-        self._prepare_cert_for_connection()
-
-        self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
-        self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
-        self.dut_security.SetLeAuthRequirements()
-
-        self.cert.security.SetLeIoCapability(DISPLAY_ONLY)
-        self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
-        self.cert_security.SetLeAuthRequirements()
-
-        # 1. IUT transmits Pairing Request command with:
-        # a. IO capability set to any IO capability
-        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
-        # c. AuthReq Bonding Flags set to ‘00’ and the MITM flag set to ‘0’ and all the reserved bits are set to ‘0’
-        self.dut.security.CreateBondLe(self.cert_address)
-
-        assertThat(self.cert_security.get_ui_stream()).emits(
-            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.dut_address))
-
-        # 2. Lower Tester responds with a Pairing Response command, with:
-        # a. IO capability set to “KeyboardDisplay”
-        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
-        # c. AuthReq Bonding Flags set to ‘00’, and the MITM flag set to ‘0’ and all the reserved bits are set to ‘0’
-        self.cert.security.SendUiCallback(
-            UiCallbackMsg(
-                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.dut_address))
-
-        # 3. IUT and Lower Tester perform phase 2 of the just works pairing procedure and establish an encrypted link with the key generated in phase 2.
-        assertThat(self.dut_security.get_bond_stream()).emits(
-            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address))
-
-    @metadata(pts_test_id="SM/SLA/JW/BV-02-C", pts_test_name="Just Works IUT Responder – Success")
-    def test_just_works_iut_responder(self):
-        """
-            Verify that the IUT is able to perform the Just Works pairing procedure correctly when acting as peripheral, responder.
-        """
-        self._prepare_dut_for_connection()
-
-        self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
-        self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
-        self.dut_security.SetLeAuthRequirements()
-
-        self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
-        self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
-        self.cert_security.SetLeAuthRequirements()
-
-        # 1. Lower Tester transmits Pairing Request command with:
-        # a. IO capability set to “NoInputNoOutput”
-        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
-        # c. MITM flag set to ‘0’ and all reserved bits are set to ‘0’
-        self.cert.security.CreateBondLe(self.dut_address)
-
-        assertThat(self.dut_security.get_ui_stream()).emits(
-            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.cert_address))
-
-        # 2. IUT responds with a Pairing Response command, with:
-        # a. IO capability set to any IO capability
-        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
-        self.dut.security.SendUiCallback(
-            UiCallbackMsg(
-                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.cert_address))
-
-        # IUT and Lower Tester perform phase 2 of the just works pairing and establish an encrypted link with the generated STK.
-        assertThat(self.dut_security.get_bond_stream()).emits(
-            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address))
-
-    @metadata(
-        pts_test_id="SM/SLA/JW/BI-03-C", pts_test_name="Just Works IUT Responder – Handle AuthReq flag RFU correctly")
-    def test_just_works_iut_responder_auth_req_rfu(self):
-        """
-            Verify that the IUT is able to perform the Just Works pairing procedure when receiving additional bits set in the AuthReq flag. Reserved For Future Use bits are correctly handled when acting as peripheral, responder.
-        """
-        self._prepare_dut_for_connection()
-
-        self.dut.security.SetLeIoCapability(KEYBOARD_DISPLAY)
-        self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
-        self.dut_security.SetLeAuthRequirements()
-
-        self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
-        self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
-        self.cert_security.SetLeAuthRequirements(mitm=1, secure_connections=1, reserved_bits=2)
-
-        # 1. Lower Tester transmits Pairing Request command with:
-        # a. IO Capability set to ”NoInputNoOutput”
-        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
-        # c. MITM set to ‘0’ and all reserved bits are set to ‘1’
-        self.cert.security.CreateBondLe(self.dut_address)
-
-        assertThat(self.dut_security.get_ui_stream()).emits(
-            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.cert_address))
-
-        # 2. IUT responds with a Pairing Response command, with:
-        # a. IO capability set to any IO capability
-        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
-        # c. All reserved bits are set to ‘0’
-        self.dut.security.SendUiCallback(
-            UiCallbackMsg(
-                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.cert_address))
-
-        # 3. IUT and Lower Tester perform phase 2 of the just works pairing and establish an encrypted link with the generated STK.
-        assertThat(self.dut_security.get_bond_stream()).emits(
-            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address))
-
-    @metadata(
-        pts_test_id="SM/MAS/JW/BI-04-C", pts_test_name="Just Works IUT Initiator – Handle AuthReq flag RFU correctly")
-    def test_just_works_iut_initiator_auth_req_rfu(self):
-        """
-            Verify that the IUT is able to perform the Just Works pairing procedure when receiving additional bits set in the AuthReq flag. Reserved For Future Use bits are correctly handled when acting as central, initiator.
-        """
-        self._prepare_cert_for_connection()
-
-        self.dut.security.SetLeIoCapability(KEYBOARD_DISPLAY)
-        self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
-        self.dut_security.SetLeAuthRequirements()
-
-        self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
-        self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
-        self.cert_security.SetLeAuthRequirements(mitm=1, secure_connections=1, reserved_bits=3)
-
-        # 1. IUT transmits a Pairing Request command with:
-        # a. IO Capability set to any IO Capability
-        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
-        # c. All reserved bits are set to ‘0’. For the purposes of this test the Secure Connections bit and the Keypress bits in the AuthReq bonding flag set by the IUT are ignored by the Lower Tester.
-        self.dut.security.CreateBondLe(self.cert_address)
-
-        assertThat(self.cert_security.get_ui_stream()).emits(
-            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.dut_address))
-
-        # 2. Lower Tester responds with a Pairing Response command, with:
-        # a. IO Capability set to “NoInputNoOutput”
-        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
-        # c. AuthReq bonding flag set to the value indicated in the IXIT [7] for ‘Bonding Flags’ and the MITM flag set to ‘0’ and all reserved bits are set to ‘1’. The SC and Keypress bits in the AuthReq bonding flag are set to 0 by the Lower Tester for this test.
-        self.cert.security.SendUiCallback(
-            UiCallbackMsg(
-                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.dut_address))
-
-        # 3. IUT and Lower Tester perform phase 2 of the just works pairing and establish an encrypted link with the generated STK.
-        assertThat(self.dut_security.get_bond_stream()).emits(
-            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address))
-
-    @metadata(
-        pts_test_id="SM/MAS/SCJW/BV-01-C", pts_test_name="Just Works, IUT Initiator, Secure Connections – Success")
-    def test_just_works_iut_initiator_secure_connections(self):
-        """
-            Verify that the IUT supporting LE Secure Connections performs the Just Works or Numeric Comparison pairing procedure correctly as initiator.
-        """
-        self._prepare_cert_for_connection()
-
-        self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
-        self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
-        self.dut_security.SetLeAuthRequirements(secure_connections=1)
-
-        self.cert.security.SetLeIoCapability(DISPLAY_ONLY)
-        self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
-        self.cert_security.SetLeAuthRequirements(secure_connections=1)
-
-        # 1. IUT transmits Pairing Request command with:
-        # a. IO capability set to any IO capability
-        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
-        # c. AuthReq Bonding Flags set to ‘00’, the MITM flag set to either ‘0’ for Just Works or '1' for Numeric Comparison, Secure Connections flag set to '1' and all the reserved bits are set to ‘0’
-        self.dut.security.CreateBondLe(self.cert_address)
-
-        assertThat(self.cert_security.get_ui_stream()).emits(
-            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.dut_address))
-
-        # 2. Lower Tester responds with a Pairing Response command, with:
-        # a. IO capability set to “KeyboardDisplay”
-        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
-        # c. AuthReq Bonding Flags set to ‘00’, the MITM flag set to ‘0’, Secure Connections flag set to '1' and all the reserved bits are set to ‘0’
-        self.cert.security.SendUiCallback(
-            UiCallbackMsg(
-                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.dut_address))
-
-        # 3. IUT and Lower Tester perform phase 2 of the Just Works or Numeric Comparison pairing procedure according to the MITM flag and IO capabilities, and establish an encrypted link with the LTK generated in phase 2.
-        assertThat(self.dut_security.get_bond_stream()).emits(
-            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address))
-
-    @metadata(
-        pts_test_id="SM/SLA/SCJW/BV-02-C", pts_test_name="Just Works, IUT Responder, Secure Connections – Success")
-    def test_just_works_iut_responder_secure_connections(self):
-        """
-            Verify that the IUT supporting LE Secure Connections is able to perform the Just Works or Numeric Comparison pairing procedure correctly when acting as responder.
-        """
-        self._prepare_dut_for_connection()
-
-        self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
-        self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
-        self.dut_security.SetLeAuthRequirements(secure_connections=1)
-
-        self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
-        self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
-        self.cert_security.SetLeAuthRequirements(secure_connections=1)
-
-        # 1. Lower Tester transmits Pairing Request command with:
-        # a. IO capability set to “NoInputNoOutput”
-        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
-        # c. AuthReq Bonding Flags set to ‘00’, MITM flag set to ‘0’, Secure Connections flag set to '1' and all reserved bits are set to ‘0’
-        self.cert.security.CreateBondLe(self.dut_address)
-
-        assertThat(self.dut_security.get_ui_stream()).emits(
-            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.cert_address))
-
-        # 2. IUT responds with a Pairing Response command, with:
-        # a. IO capability set to any IO capability
-        # b. AuthReq Bonding Flags set to ‘00’, MITM flag set to either ‘0’ for Just Works or '1' for Numeric Comparison, Secure Connections flag set to '1' and all reserved bits are set to ‘0’
-        self.dut.security.SendUiCallback(
-            UiCallbackMsg(
-                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.cert_address))
-
-        # 3. UT and Lower Tester perform phase 2 of the Just Works or Numeric Comparison pairing procedure according to the MITM flag and IO capabilities, and establish an encrypted link with the LTK generated in phase 2.
-        assertThat(self.dut_security.get_bond_stream()).emits(
-            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address))
-
-    @metadata(
-        pts_test_id="SM/SLA/SCJW/BV-03-C",
-        pts_test_name="Just Works, IUT Responder, Secure Connections – Handle AuthReq Flag RFU Correctly")
-    def test_just_works_iut_responder_secure_connections_auth_req_rfu(self):
-        """
-            Verify that the IUT is able to perform the Just Works pairing procedure when receiving additional bits set in the AuthReq flag. Reserved For Future Use bits are correctly handled when acting as peripheral, responder.
-        """
-        self._prepare_dut_for_connection()
-
-        self.dut.security.SetLeIoCapability(KEYBOARD_DISPLAY)
-        self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
-        self.dut_security.SetLeAuthRequirements(secure_connections=1)
-
-        self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
-        self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
-        self.cert_security.SetLeAuthRequirements(mitm=1, secure_connections=1, reserved_bits=3)
-
-        # 1. Lower Tester transmits Pairing Request command with:
-        # a. IO Capability set to ”NoInputNoOutput”
-        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
-        # c. MITM set to ‘0’ and all reserved bits are set to a random value.
-        self.cert.security.CreateBondLe(self.dut_address)
-
-        assertThat(self.dut_security.get_ui_stream()).emits(
-            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.cert_address))
-
-        # 2. IUT responds with a Pairing Response command, with:
-        # a. IO capability set to any IO capability
-        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
-        # c. All reserved bits are set to ‘0’
-        self.dut.security.SendUiCallback(
-            UiCallbackMsg(
-                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.cert_address))
-
-        # 3. IUT and Lower Tester perform phase 2 of the Just Works pairing and establish an encrypted link with the generated LTK.
-        assertThat(self.dut_security.get_bond_stream()).emits(
-            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address))
-
-    @metadata(
-        pts_test_id="SM/MAS/SCJW/BV-04-C",
-        pts_test_name="Just Works, IUT Initiator, Secure Connections – Handle AuthReq Flag RFU Correctly")
-    def test_just_works_iut_initiator_secure_connections_auth_req_rfu(self):
-        """
-            Verify that the IUT is able to perform the Just Works pairing procedure when receiving additional bits set in the AuthReq flag. Reserved For Future Use bits are correctly handled when acting as central, initiator.
-        """
-        self._prepare_cert_for_connection()
-
-        self.dut.security.SetLeIoCapability(KEYBOARD_DISPLAY)
-        self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
-        self.dut_security.SetLeAuthRequirements(secure_connections=1)
-
-        self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
-        self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
-        self.cert_security.SetLeAuthRequirements(mitm=1, secure_connections=1, reserved_bits=3)
-
-        # 1. IUT transmits a Pairing Request command with:
-        # a. IO Capability set to any IO Capability
-        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
-        # c. All reserved bits are set to ‘0’.
-        self.dut.security.CreateBondLe(self.cert_address)
-
-        assertThat(self.cert_security.get_ui_stream()).emits(
-            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.dut_address))
-
-        # 2. Lower Tester responds with a Pairing Response command, with:
-        # a. IO Capability set to “NoInputNoOutput”
-        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
-        # c. AuthReq bonding flag set to the value indicated in the IXIT [7] for ‘Bonding Flags’ and the MITM flag set to ‘0’ and all reserved bits are set to a random value.
-        self.cert.security.SendUiCallback(
-            UiCallbackMsg(
-                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.dut_address))
-
-        # 3. IUT and Lower Tester perform phase 2 of the Just Works pairing and establish an encrypted link with the generated LTK.
-        assertThat(self.dut_security.get_bond_stream()).emits(
-            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address))
-
-    @metadata(
-        pts_test_id="SM/MAS/EKS/BV-01-C",
-        pts_test_name="IUT initiator, Lower Tester Maximum Encryption Key Size = Min_Encryption_Key_Length")
-    def test_min_encryption_key_size_equal_to_max_iut_initiator(self):
-        """
-            Verify that the IUT uses correct key size during encryption as initiator.
-        """
-        self._prepare_cert_for_connection()
-
-        self.dut.security.SetLeIoCapability(KEYBOARD_DISPLAY)
-        self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
-        self.dut_security.SetLeAuthRequirements(secure_connections=1)
-        self.dut.security.SetLeMaximumEncryptionKeySize(
-            LeMaximumEncryptionKeySizeMessage(maximum_encryption_key_size=0x10))
-
-        self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
-        self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
-        self.cert_security.SetLeAuthRequirements(mitm=1, secure_connections=1)
-        self.cert.security.SetLeMaximumEncryptionKeySize(
-            LeMaximumEncryptionKeySizeMessage(maximum_encryption_key_size=0x07))
-
-        # 1. IUT transmits a Pairing Request
-        self.dut.security.CreateBondLe(self.cert_address)
-
-        assertThat(self.cert_security.get_ui_stream()).emits(
-            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.dut_address))
-
-        # 2. Lower Tester responds with Pairing Response command with Maximum Encryption Key Size field set to Min_Encryption_Key_Length’.
-        self.cert.security.SendUiCallback(
-            UiCallbackMsg(
-                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.dut_address))
-
-        # 3. IUT and Lower Tester perform phase 2 of the LE pairing and establish an encrypted link with the key generated in phase 2.
-        assertThat(self.dut_security.get_bond_stream()).emits(
-            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address))
-
-    @metadata(
-        pts_test_id="SM/SLA/EKS/BV-02-C",
-        pts_test_name="IUT Responder, Lower Tester Maximum Encryption Key Size = Min_Encryption_Key_Length")
-    def test_min_encryption_key_size_equal_to_max_iut_responder(self):
-        """
-            Verify that the IUT uses correct key size during encryption as responder.
-        """
-        self._prepare_dut_for_connection()
-
-        self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
-        self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
-        self.dut_security.SetLeAuthRequirements()
-        self.dut.security.SetLeMaximumEncryptionKeySize(
-            LeMaximumEncryptionKeySizeMessage(maximum_encryption_key_size=0x07))
-
-        self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
-        self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
-        self.cert_security.SetLeAuthRequirements()
-        self.cert.security.SetLeMaximumEncryptionKeySize(
-            LeMaximumEncryptionKeySizeMessage(maximum_encryption_key_size=0x10))
-
-        # 1. Lower Tester initiates Pairing Request command with Maximum Encryption Key Size field set to Min_Encryption_Key_Length’.
-        self.cert.security.CreateBondLe(self.dut_address)
-
-        assertThat(self.dut_security.get_ui_stream()).emits(
-            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.cert_address))
-
-        # 2. IUT responds with Pairing Response command.
-        self.dut.security.SendUiCallback(
-            UiCallbackMsg(
-                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.cert_address))
-
-        #3. IUT and Lower Tester perform phase 2 of the LE pairing and establish an encrypted link with the key generated in phase 2.
-        assertThat(self.dut_security.get_bond_stream()).emits(
-            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address))
-
-    @metadata(
-        pts_test_id="SM/MAS/EKS/BI-01-C",
-        pts_test_name="IUT initiator, Lower Tester Maximum Encryption Key Size < Min_Encryption_Key_Length")
-    def test_min_encryption_key_size_less_than_min_iut_initiator(self):
-        """
-            Verify that the IUT checks that the resultant encryption key size is not smaller than the minimum key size.
-        """
-        self._prepare_cert_for_connection()
-
-        self.dut.security.SetLeIoCapability(KEYBOARD_DISPLAY)
-        self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
-        self.dut_security.SetLeAuthRequirements(secure_connections=1)
-        self.dut.security.SetLeMaximumEncryptionKeySize(
-            LeMaximumEncryptionKeySizeMessage(maximum_encryption_key_size=0x10))
-
-        self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
-        self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
-        self.cert_security.SetLeAuthRequirements(mitm=1, secure_connections=1)
-        self.cert.security.SetLeMaximumEncryptionKeySize(
-            LeMaximumEncryptionKeySizeMessage(maximum_encryption_key_size=0x06))
-
-        # 1. IUT transmits a Pairing Request
-        self.dut.security.CreateBondLe(self.cert_address)
-
-        assertThat(self.cert_security.get_ui_stream()).emits(
-            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.dut_address))
-
-        # 2. Lower Tester responds with Pairing Response command with Maximum Encryption Key Size field set to Min_Encryption_Key_Length-1’.
-        self.cert.security.SendUiCallback(
-            UiCallbackMsg(
-                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.dut_address))
-
-        # 3. IUT transmits the Pairing Failed command.
-        assertThat(self.dut_security.get_bond_stream()).emits(
-            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BOND_FAILED, self.cert_address,
-                                     int(PairingFailedReason.ENCRYPTION_KEY_SIZE)))
-
-    @metadata(
-        pts_test_id="SM/SLA/EKS/BI-02-C",
-        pts_test_name="IUT Responder, Lower Tester Maximum Encryption Key Size < Min_Encryption_Key_Length")
-    def test_min_encryption_key_size_less_than_min_iut_responder(self):
-        """
-            Verify that the IUT uses correct key size during encryption as responder.
-        """
-        self._prepare_dut_for_connection()
-
-        self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
-        self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
-        self.dut_security.SetLeAuthRequirements()
-        self.dut.security.SetLeMaximumEncryptionKeySize(
-            LeMaximumEncryptionKeySizeMessage(maximum_encryption_key_size=0x06))
-
-        self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
-        self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
-        self.cert_security.SetLeAuthRequirements()
-        self.cert.security.SetLeMaximumEncryptionKeySize(
-            LeMaximumEncryptionKeySizeMessage(maximum_encryption_key_size=0x10))
-
-        # 1. Lower Tester initiates Pairing Request command with Maximum Encryption Key Size field set to Min_Encryption_Key_Length-1.
-        self.cert.security.CreateBondLe(self.dut_address)
-
-        assertThat(self.dut_security.get_ui_stream()).emits(
-            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.cert_address))
-
-        self.dut.security.SendUiCallback(
-            UiCallbackMsg(
-                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.cert_address))
-
-        #3. IUT transmits the Pairing Failed command.
-        assertThat(self.cert_security.get_bond_stream()).emits(
-            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BOND_FAILED, self.dut_address,
-                                     int(PairingFailedReason.ENCRYPTION_KEY_SIZE)))
-
-    @metadata(
-        pts_test_id="SM/MAS/SCPK/BV-01-C", pts_test_name="Passkey Entry, IUT Initiator, Secure Connections – Success")
-    def test_passkey_entry_iut_initiator_secure_connections(self):
-        """
-            Verify that the IUT supporting LE Secure Connections performs the Passkey Entry pairing procedure correctly as central, initiator.
-        """
-        self._prepare_cert_for_connection()
-
-        self.dut.security.SetLeIoCapability(DISPLAY_ONLY)
-        self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
-        self.dut_security.SetLeAuthRequirements(secure_connections=1)
-
-        self.cert.security.SetLeIoCapability(KEYBOARD_ONLY)
-        self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
-        self.cert_security.SetLeAuthRequirements(mitm=1, secure_connections=1)
-
-        # 1. IUT transmits Pairing Request command with:
-        # a. IO capability set to “DisplayOnly” or “KeyboardOnly”
-        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
-        # c. AuthReq bonding flag set to ‘00’, the MITM flag set to ‘0’ and Secure Connections flag set to '1'. Keypress bit is set to '1' if supported
-        self.dut.security.CreateBondLe(self.cert_address)
-
-        assertThat(self.cert_security.get_ui_stream()).emits(
-            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.dut_address))
-
-        # 2. Lower Tester responds with a Pairing Response command, with:
-        # a. IO capability set to “KeyboardOnly”
-        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
-        # c. AuthReq bonding flag set to ‘00’, the MITM flag set to ‘1’, Secure Connections flag set to '1' and all reserved bits are set to ‘0’. Keypress bit is set to '1' if supported by the IUT.
-        self.cert.security.SendUiCallback(
-            UiCallbackMsg(
-                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.dut_address))
-
-        assertThat(self.cert_security.get_ui_stream()).emits(
-            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PASSKEY_ENTRY, self.dut_address))
-
-        # 3. During the phase 2 pairing, the IUT displays the 6-digit passkey while the Lower Tester prompts user to enter the 6-digit passkey. If the IUT’s IO capabilities are “KeyboardOnly” the passkey is not displayed and both IUT and Lower Tester enter the same 6-digit passkey. If Keypress bit is set, pairing keypress notifications are sent by the Lower Tester.
-        passkey = self.dut_security.wait_for_ui_event_passkey()
-
-        if passkey == 0:
-            print("Passkey did not arrive into test")
-
-        # 4. IUT and Lower Tester use the same 6-digit passkey.
-        self.cert.security.SendUiCallback(
-            UiCallbackMsg(
-                message_type=UiCallbackType.PASSKEY, numeric_value=passkey, unique_id=1, address=self.dut_address))
-
-        # 5. IUT and Lower Tester perform phase 2 of the Passkey Entry pairing procedure and establish an encrypted link with the LTK generated in phase 2.
-        assertThat(self.dut_security.get_bond_stream()).emits(
-            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address), timeout=timedelta(seconds=10))
-
-    @metadata(
-        pts_test_id="SM/SLA/SCPK/BV-02-C", pts_test_name="Passkey Entry, IUT Responder, Secure Connections – Success")
-    def test_passkey_entry_iut_responder_secure_connections(self):
-        """
-            Verify that the IUT supporting LE Secure Connections is able to perform the Passkey Entry pairing procedure correctly when acting as peripheral, responder.
-        """
-        self._prepare_dut_for_connection()
-
-        self.dut.security.SetLeIoCapability(DISPLAY_ONLY)
-        self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
-        self.dut_security.SetLeAuthRequirements(secure_connections=1)
-
-        self.cert.security.SetLeIoCapability(KEYBOARD_DISPLAY)
-        self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
-        self.cert_security.SetLeAuthRequirements(mitm=1, secure_connections=1)
-
-        # 1. Lower Tester transmits Pairing Request command with:
-        # a. IO capability set to “KeyboardDisplay”
-        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
-        # c. AuthReq bonding flag set to the value indicated in the IXIT [7] for ‘Bonding Flags’, and the MITM flag set to ‘1’ Secure Connections flag set to '1' and all reserved bits are set to ‘0’
-        self.cert.security.CreateBondLe(self.dut_address)
-
-        assertThat(self.dut_security.get_ui_stream()).emits(
-            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.cert_address))
-
-        # 2. IUT responds with a Pairing Response command, with:
-        # a. IO capability set to “KeyboardOnly” or “KeyboardDisplay” or “DisplayYesNo” or “DisplayOnly”
-        # b. Secure Connections flag set to '1'. Keypress bit is set to '1' if supported by IUT
-        self.dut.security.SendUiCallback(
-            UiCallbackMsg(
-                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.cert_address))
-
-        # 3. During the phase 2 passkey pairing process, Lower Tester displays the 6-digit passkey while the IUT prompts user to enter the 6-digit passkey. If the IO capabilities of the IUT are “DisplayYesNo” or “DisplayOnly” the IUT displays the 6-digit passkey while the Lower Tester enters the 6-digit passkey. If Keypress bit is set, pairing keypress notifications are send by the IUT
-        passkey = self.dut_security.wait_for_ui_event_passkey()
-
-        if passkey == 0:
-            print("Passkey did not arrive into test")
-
-        assertThat(self.cert_security.get_ui_stream()).emits(
-            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PASSKEY_ENTRY, self.dut_address))
-
-        # 4. IUT and Lower Tester use the same pre-defined 6-digit passkey.
-        self.cert.security.SendUiCallback(
-            UiCallbackMsg(
-                message_type=UiCallbackType.PASSKEY, numeric_value=passkey, unique_id=1, address=self.dut_address))
-
-        # 5. IUT and Lower Tester perform phase 2 of the LE pairing and establish an encrypted link with the LTK generated in phase 2.
-        assertThat(self.dut_security.get_bond_stream()).emits(
-            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address), timeout=timedelta(seconds=10))
-
-    @metadata(
-        pts_test_id="SM/SLA/SCPK/BV-03-C",
-        pts_test_name="Passkey Entry, IUT Responder, Secure Connections – Handle AuthReq Flag RFU Correctly")
-    def test_passkey_entry_iut_responder_secure_connections_auth_req_rfu(self):
-        """
-            Verify that the IUT supporting LE Secure Connections is able to perform the Passkey Entry pairing procedure when receiving additional bits set in the AuthReq flag. Reserved For Future Use bits are correctly handled when acting as peripheral, responder.
-        """
-        self._prepare_dut_for_connection()
-
-        self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
-        self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
-        self.dut_security.SetLeAuthRequirements(secure_connections=1)
-
-        self.cert.security.SetLeIoCapability(DISPLAY_ONLY)
-        self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
-        self.cert_security.SetLeAuthRequirements(mitm=1, secure_connections=1, reserved_bits=3)
-
-        # 1. Lower Tester transmits Pairing Request command with:
-        # a. IO Capability set to ”KeyboardOnly”
-        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
-        # c. MITM set to ‘1’ and all reserved bits are set to a random value
-        self.cert.security.CreateBondLe(self.dut_address)
-
-        assertThat(self.dut_security.get_ui_stream()).emits(
-            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.cert_address))
-
-        # 2. IUT responds with a Pairing Response command, with:
-        # a. IO Capability set to “KeyboardOnly” or “DisplayOnly”
-        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
-        # c. All reserved bits are set to ‘0’
-        self.dut.security.SendUiCallback(
-            UiCallbackMsg(
-                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.cert_address))
-
-        passkey = self.cert_security.wait_for_ui_event_passkey()
-
-        if passkey == 0:
-            print("Passkey did not arrive into test")
-
-        assertThat(self.dut_security.get_ui_stream()).emits(
-            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PASSKEY_ENTRY, self.cert_address))
-
-        self.dut.security.SendUiCallback(
-            UiCallbackMsg(
-                message_type=UiCallbackType.PASSKEY, numeric_value=passkey, unique_id=1, address=self.cert_address))
-
-        # 3. IUT and Lower Tester perform phase 2 of the Passkey Entry pairing and establish an encrypted link with the generated LTK.
-        assertThat(self.dut_security.get_bond_stream()).emits(
-            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address), timeout=timedelta(seconds=10))
-
-    @metadata(
-        pts_test_id="SM/MAS/SCPK/BV-04-C",
-        pts_test_name="Passkey Entry, IUT Initiator, Secure Connections – Handle AuthReq Flag RFU Correctly")
-    def test_passkey_entry_iut_initiator_secure_connections_auth_req_rfu(self):
-        """
-            Verify that the IUT supporting LE Secure Connections is able to perform the Passkey Entry pairing procedure when receiving additional bits set in the AuthReq flag. Reserved For Future Use bits are correctly handled when acting as central, initiator.
-        """
-        self._prepare_cert_for_connection()
-
-        self.dut.security.SetLeIoCapability(KEYBOARD_DISPLAY)
-        self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
-        self.dut_security.SetLeAuthRequirements(secure_connections=1)
-
-        self.cert.security.SetLeIoCapability(KEYBOARD_ONLY)
-        self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
-        self.cert_security.SetLeAuthRequirements(mitm=1, secure_connections=1, reserved_bits=3)
-
-        # 1. IUT transmits a Pairing Request command with:
-        # a. IO Capability set to “DisplayOnly” or “DisplayYesNo” or “KeyboardOnly” or “KeyboardDisplay”
-        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
-        # c. All reserved bits are set to ‘0’.
-        self.dut.security.CreateBondLe(self.cert_address)
-
-        assertThat(self.cert_security.get_ui_stream()).emits(
-            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.dut_address))
-
-        # 2. Lower Tester responds with a Pairing Response command, with:
-        # a. IO Capability set to “KeyboardOnly”
-        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
-        # c. AuthReq bonding flag set to the value indicated in the IXIT [7] for ‘Bonding Flags’ and the MITM flag set to ‘1’ and all reserved bits are set to a random value.
-        self.cert.security.SendUiCallback(
-            UiCallbackMsg(
-                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.dut_address))
-
-        assertThat(self.cert_security.get_ui_stream()).emits(
-            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PASSKEY_ENTRY, self.dut_address))
-
-        passkey = self.dut_security.wait_for_ui_event_passkey()
-
-        self.cert.security.SendUiCallback(
-            UiCallbackMsg(
-                message_type=UiCallbackType.PASSKEY, numeric_value=passkey, unique_id=1, address=self.dut_address))
-
-        # 3.    IUT and Lower Tester perform phase 2 of the Just Works pairing and establish an encrypted link with the generated LTK.
-        assertThat(self.dut_security.get_bond_stream()).emits(
-            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address), timeout=timedelta(seconds=10))
-
-    @metadata(
-        pts_test_id="SM/MAS/SCOB/BV-01-C", pts_test_name="Out of Band, IUT Initiator, Secure Connections – Success")
-    def test_out_of_band_iut_initiator_secure_connections(self):
-        """
-            Verify that the IUT supporting LE Secure Connections performs the Out-of-Band pairing procedure correctly as central, initiator.
-        """
-
-        oob_combinations = [(OOB_NOT_PRESENT, OOB_PRESENT), (OOB_PRESENT, OOB_NOT_PRESENT), (OOB_PRESENT, OOB_PRESENT)]
-
-        for (dut_oob_flag, cert_oob_flag) in oob_combinations:
-            print("oob flag combination dut: " + str(dut_oob_flag) + ", cert: " + str(cert_oob_flag))
-
-            self._prepare_cert_for_connection()
-
-            if dut_oob_flag == LeOobDataFlag.PRESENT:
-                oobdata = self.cert.security.GetLeOutOfBandData(empty_proto.Empty())
-                self.dut.security.SetOutOfBandData(
-                    OobDataMessage(
-                        address=self.cert_address,
-                        confirmation_value=oobdata.confirmation_value,
-                        random_value=oobdata.random_value))
-
-            if cert_oob_flag == LeOobDataFlag.PRESENT:
-                oobdata = self.dut.security.GetLeOutOfBandData(empty_proto.Empty())
-                self.cert.security.SetOutOfBandData(
-                    OobDataMessage(
-                        address=self.dut_address,
-                        confirmation_value=oobdata.confirmation_value,
-                        random_value=oobdata.random_value))
-
-            self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
-            self.dut.security.SetLeOobDataPresent(dut_oob_flag)
-            self.dut_security.SetLeAuthRequirements(bond=1, mitm=1, secure_connections=1)
-
-            self.cert.security.SetLeIoCapability(DISPLAY_ONLY)
-            self.cert.security.SetLeOobDataPresent(cert_oob_flag)
-            self.cert_security.SetLeAuthRequirements(bond=1, mitm=1, secure_connections=1)
-
-            # 1. IUT transmits a Pairing Request command with OOB data flag set to either 0x00 or 0x01, and Secure Connections flag set to '1'.
-            self.dut.security.CreateBondLe(self.cert_address)
-
-            assertThat(self.cert_security.get_ui_stream()).emits(
-                SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.dut_address))
-
-            # 2. Lower Tester responds with a Pairing Response command with Secure Connections flag set to '1' and OOB data flag set to either 0x00 or 0x01.
-            self.cert.security.SendUiCallback(
-                UiCallbackMsg(
-                    message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.dut_address))
-
-            # 3. IUT uses the 128-bit value generated by the Lower Tester as the confirm value. Similarly, the Lower Tester uses the 128-bit value generated by the IUT as the confirm value.
-
-            # 4. IUT and Lower Tester perform phase 2 of the pairing process and establish an encrypted link with an LTK generated using the OOB data in phase 2.
-            assertThat(self.dut_security.get_bond_stream()).emits(
-                SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address), timeout=timedelta(seconds=10))
-
-            assertThat(self.cert_security.get_bond_stream()).emits(
-                SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.dut_address), timeout=timedelta(seconds=10))
-
-            self.dut.security.RemoveBond(self.cert_address)
-            self.cert.security.RemoveBond(self.dut_address)
-
-            assertThat(self.dut_security.get_bond_stream()).emits(
-                SecurityMatchers.BondMsg(BondMsgType.DEVICE_UNBONDED, self.cert_address))
-
-            self.dut_security.wait_device_disconnect(self.cert_address)
-            self.cert_security.wait_device_disconnect(self.dut_address)
-
-    @metadata(
-        pts_test_id="SM/SLA/SCOB/BV-02-C", pts_test_name="Out of Band, IUT Responder, Secure Connections – Success")
-    def test_out_of_band_iut_responder_secure_connections(self):
-        """
-            Verify that the IUT supporting LE Secure Connections is able to perform the Out-of-Band pairing procedure correctly when acting as peripheral, responder.
-        """
-
-        oob_combinations = [(OOB_NOT_PRESENT, OOB_PRESENT), (OOB_PRESENT, OOB_NOT_PRESENT), (OOB_PRESENT, OOB_PRESENT)]
-
-        for (dut_oob_flag, cert_oob_flag) in oob_combinations:
-            print("oob flag combination dut: " + str(dut_oob_flag) + ", cert: " + str(cert_oob_flag))
-
-            self._prepare_dut_for_connection()
-
-            if dut_oob_flag == LeOobDataFlag.PRESENT:
-                oobdata = self.cert.security.GetLeOutOfBandData(empty_proto.Empty())
-                self.dut.security.SetOutOfBandData(
-                    OobDataMessage(
-                        address=self.cert_address,
-                        confirmation_value=oobdata.confirmation_value,
-                        random_value=oobdata.random_value))
-
-            if cert_oob_flag == LeOobDataFlag.PRESENT:
-                oobdata = self.dut.security.GetLeOutOfBandData(empty_proto.Empty())
-                self.cert.security.SetOutOfBandData(
-                    OobDataMessage(
-                        address=self.dut_address,
-                        confirmation_value=oobdata.confirmation_value,
-                        random_value=oobdata.random_value))
-
-            self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
-            self.dut.security.SetLeOobDataPresent(dut_oob_flag)
-            self.dut_security.SetLeAuthRequirements(bond=1, mitm=1, secure_connections=1)
-
-            self.cert.security.SetLeIoCapability(DISPLAY_ONLY)
-            self.cert.security.SetLeOobDataPresent(cert_oob_flag)
-            self.cert_security.SetLeAuthRequirements(bond=1, mitm=1, secure_connections=1)
-
-            # 1. Lower Tester transmits a Pairing Request command with OOB data flag set to either 0x00 or 0x01, and Secure Connections flag set to '1'.
-            self.cert.security.CreateBondLe(self.dut_address)
-
-            assertThat(self.dut_security.get_ui_stream()).emits(
-                SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.cert_address))
-
-            # 2. IUT responds with a Pairing Response command with Secure Connections flag set to '1' and OOB data flag set to either 0x00 or 0x01.
-            self.dut.security.SendUiCallback(
-                UiCallbackMsg(
-                    message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.cert_address))
-
-            # 3. IUT uses the 128-bit value generated by the Lower Tester as the confirm value. Similarly, the Lower Tester uses the 128-bit value generated by the IUT as the confirm value.
-
-            # 4. IUT and Lower Tester perform phase 2 of the pairing process and establish an encrypted link with an LTK generated using the OOB data in phase 2.
-            assertThat(self.cert_security.get_bond_stream()).emits(
-                SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.dut_address), timeout=timedelta(seconds=10))
-
-            assertThat(self.dut_security.get_bond_stream()).emits(
-                SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address), timeout=timedelta(seconds=10))
-
-            self.cert.security.RemoveBond(self.dut_address)
-            self.dut.security.RemoveBond(self.cert_address)
-
-            assertThat(self.dut_security.get_bond_stream()).emits(
-                SecurityMatchers.BondMsg(BondMsgType.DEVICE_UNBONDED, self.cert_address))
-
-            self.cert_security.wait_device_disconnect(self.dut_address)
-            self.dut_security.wait_device_disconnect(self.cert_address)
-
-    @metadata(
-        pts_test_id="SM/SLA/SCOB/BV-03-C",
-        pts_test_name="Out of Band, IUT Responder, Secure Connections – Handle AuthReq Flag RFU Correctly")
-    def test_out_of_band_iut_responder_secure_connections_auth_req_rfu(self):
-        """
-            Verify that the IUT supporting LE Secure Connections is able to perform the Out-of-Band pairing procedure when receiving additional bits set in the AuthReq flag. Reserved For Future Use bits are correctly handled when acting as peripheral, responder.
-        """
-
-        reserved_bits_combinations = [1, 2, 3]
-
-        for reserved_bits in reserved_bits_combinations:
-            print("reserved bits in cert dut: " + str(reserved_bits))
-
-            self._prepare_dut_for_connection()
-
-            oobdata = self.cert.security.GetLeOutOfBandData(empty_proto.Empty())
-            self.dut.security.SetOutOfBandData(
-                OobDataMessage(
-                    address=self.cert_address,
-                    confirmation_value=oobdata.confirmation_value,
-                    random_value=oobdata.random_value))
-
-            oobdata = self.dut.security.GetLeOutOfBandData(empty_proto.Empty())
-            self.cert.security.SetOutOfBandData(
-                OobDataMessage(
-                    address=self.dut_address,
-                    confirmation_value=oobdata.confirmation_value,
-                    random_value=oobdata.random_value))
-
-            self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
-            self.dut.security.SetLeOobDataPresent(OOB_PRESENT)
-            self.dut_security.SetLeAuthRequirements(bond=1, mitm=0, secure_connections=1)
-
-            self.cert.security.SetLeIoCapability(DISPLAY_ONLY)
-            self.cert.security.SetLeOobDataPresent(OOB_PRESENT)
-            self.cert_security.SetLeAuthRequirements(bond=1, mitm=1, secure_connections=1, reserved_bits=reserved_bits)
-
-            # 1. Lower Tester transmits Pairing Request command with:
-            # a. IO Capability set to any IO capability
-            # b. OOB data flag set to 0x01 (OOB Authentication data from remote device present)
-            # c. MITM set to ‘0’, Secure Connections flag is set to '1', and all reserved bits are set to a random value.
-            self.cert.security.CreateBondLe(self.dut_address)
-
-            assertThat(self.dut_security.get_ui_stream()).emits(
-                SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.cert_address))
-
-            # 2. IUT responds with a Pairing Response command, with:
-            # a. IO Capability set to any IO capability
-            # b. OOB data flag set to 0x01 (OOB Authentication data present)
-            # c. Secure Connections flag is set to '1', All reserved bits are set to ‘0’
-            self.dut.security.SendUiCallback(
-                UiCallbackMsg(
-                    message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.cert_address))
-
-            # 3. IUT and Lower Tester perform phase 2 of the OOB authenticated pairing and establish an encrypted link with the generated LTK.
-
-            assertThat(self.cert_security.get_bond_stream()).emits(
-                SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.dut_address), timeout=timedelta(seconds=10))
-
-            assertThat(self.dut_security.get_bond_stream()).emits(
-                SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address), timeout=timedelta(seconds=10))
-
-            self.cert.security.RemoveBond(self.dut_address)
-            self.dut.security.RemoveBond(self.cert_address)
-
-            assertThat(self.dut_security.get_bond_stream()).emits(
-                SecurityMatchers.BondMsg(BondMsgType.DEVICE_UNBONDED, self.cert_address))
-
-            self.dut_security.wait_device_disconnect(self.cert_address)
-            self.cert_security.wait_device_disconnect(self.dut_address)
-
-    @metadata(
-        pts_test_id="SM/MAS/SCOB/BV-04-C",
-        pts_test_name="Out of Band, IUT Initiator, Secure Connections – Handle AuthReq Flag RFU Correctly")
-    def test_out_of_band_iut_initiator_secure_connections_auth_req_rfu(self):
-        """
-            Verify that the IUT supporting LE Secure Connections is able to perform the Out-of-Band pairing procedure when receiving additional bits set in the AuthReq flag. Reserved For Future Use bits are correctly handled when acting as central, initiator.
-        """
-
-        reserved_bits_combinations = [1, 2, 3]
-
-        for reserved_bits in reserved_bits_combinations:
-            print("reserved bits in cert dut: " + str(reserved_bits))
-
-            self._prepare_cert_for_connection()
-
-            oobdata = self.cert.security.GetLeOutOfBandData(empty_proto.Empty())
-            self.dut.security.SetOutOfBandData(
-                OobDataMessage(
-                    address=self.cert_address,
-                    confirmation_value=oobdata.confirmation_value,
-                    random_value=oobdata.random_value))
-
-            oobdata = self.dut.security.GetLeOutOfBandData(empty_proto.Empty())
-            self.cert.security.SetOutOfBandData(
-                OobDataMessage(
-                    address=self.dut_address,
-                    confirmation_value=oobdata.confirmation_value,
-                    random_value=oobdata.random_value))
-
-            self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
-            self.dut.security.SetLeOobDataPresent(OOB_PRESENT)
-            self.dut_security.SetLeAuthRequirements(bond=1, mitm=0, secure_connections=1, reserved_bits=0)
-
-            self.cert.security.SetLeIoCapability(DISPLAY_ONLY)
-            self.cert.security.SetLeOobDataPresent(OOB_PRESENT)
-            self.cert_security.SetLeAuthRequirements(bond=1, mitm=1, secure_connections=1, reserved_bits=reserved_bits)
-
-            # 1. IUT transmits Pairing Request command with:
-            # a. IO Capability set to any IO capability
-            # b. OOB data flag set to 0x01 (OOB Authentication data present)
-            # c. MITM set to ‘0’, Secure Connections flag is set to '1', and all reserved bits are set to ‘0’
-            self.dut.security.CreateBondLe(self.cert_address)
-
-            assertThat(self.cert_security.get_ui_stream()).emits(
-                SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.dut_address))
-
-            # 2. Lower Tester responds with a Pairing Response command, with:
-            # a. IO Capability set to any IO capability
-            # b. OOB data flag set to 0x01 (OOB Authentication data present)
-            # c. Secure Connections flag is set to '1', and all reserved bits are set to a random value.
-            self.cert.security.SendUiCallback(
-                UiCallbackMsg(
-                    message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.dut_address))
-
-            # 3. IUT and Lower Tester perform phase 2 of the OOB authenticated pairing and establish an encrypted link with the generated LTK.
-
-            assertThat(self.dut_security.get_bond_stream()).emits(
-                SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address), timeout=timedelta(seconds=10))
-
-            assertThat(self.cert_security.get_bond_stream()).emits(
-                SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.dut_address), timeout=timedelta(seconds=10))
-
-            self.dut.security.RemoveBond(self.cert_address)
-            self.cert.security.RemoveBond(self.dut_address)
-
-            assertThat(self.dut_security.get_bond_stream()).emits(
-                SecurityMatchers.BondMsg(BondMsgType.DEVICE_UNBONDED, self.cert_address))
-
-            self.dut_security.wait_device_disconnect(self.cert_address)
-            self.cert_security.wait_device_disconnect(self.dut_address)
+        LeSecurityTestBase.teardown_test(self)
+        GdBaseTestClass.teardown_test(self)
diff --git a/gd/security/cert/le_security_test_lib.py b/gd/security/cert/le_security_test_lib.py
new file mode 100644
index 0000000..789963b
--- /dev/null
+++ b/gd/security/cert/le_security_test_lib.py
@@ -0,0 +1,1092 @@
+#
+#   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.
+
+import time
+
+from bluetooth_packets_python3 import hci_packets
+from bluetooth_packets_python3 import security_packets
+from cert.event_stream import EventStream
+from cert.matchers import HciMatchers
+from cert.matchers import SecurityMatchers
+from cert.metadata import metadata
+from cert.py_hci import PyHci
+from cert.py_le_security import PyLeSecurity
+from cert.truth import assertThat
+from datetime import timedelta
+from facade import common_pb2 as common
+from hci.facade import controller_facade_pb2 as controller_facade
+from hci.facade import le_advertising_manager_facade_pb2 as le_advertising_facade
+from hci.facade import le_initiator_address_facade_pb2 as le_initiator_address_facade
+from google.protobuf import empty_pb2 as empty_proto
+from neighbor.facade import facade_pb2 as neighbor_facade
+from security.cert.cert_security import CertSecurity
+from security.facade_pb2 import AuthenticationRequirements
+from security.facade_pb2 import BondMsgType
+from security.facade_pb2 import OobDataMessage
+from security.facade_pb2 import UiCallbackMsg
+from security.facade_pb2 import UiCallbackType
+from security.facade_pb2 import UiMsgType
+from security.facade_pb2 import LeAuthRequirementsMessage
+from security.facade_pb2 import LeIoCapabilityMessage
+from security.facade_pb2 import LeOobDataPresentMessage
+from security.facade_pb2 import LeMaximumEncryptionKeySizeMessage
+
+from bluetooth_packets_python3.hci_packets import OpCode
+from bluetooth_packets_python3.security_packets import PairingFailedReason
+
+from mobly import asserts
+
+LeIoCapabilities = LeIoCapabilityMessage.LeIoCapabilities
+LeOobDataFlag = LeOobDataPresentMessage.LeOobDataFlag
+
+DISPLAY_ONLY = LeIoCapabilityMessage(capabilities=LeIoCapabilities.DISPLAY_ONLY)
+KEYBOARD_ONLY = LeIoCapabilityMessage(capabilities=LeIoCapabilities.KEYBOARD_ONLY)
+NO_INPUT_NO_OUTPUT = LeIoCapabilityMessage(capabilities=LeIoCapabilities.NO_INPUT_NO_OUTPUT)
+KEYBOARD_DISPLAY = LeIoCapabilityMessage(capabilities=LeIoCapabilities.KEYBOARD_DISPLAY)
+
+OOB_NOT_PRESENT = LeOobDataPresentMessage(data_present=LeOobDataFlag.NOT_PRESENT)
+OOB_PRESENT = LeOobDataPresentMessage(data_present=LeOobDataFlag.PRESENT)
+
+
+class LeSecurityTestBase():
+    """
+        Collection of tests that each sample results from
+        different (unique) combinations of io capabilities, authentication requirements, and oob data.
+    """
+
+    def setup_test(self, dut, cert):
+        self.dut = dut
+        self.cert = cert
+
+        self.dut_security = PyLeSecurity(self.dut)
+        self.cert_security = PyLeSecurity(self.cert)
+        self.dut_hci = PyHci(self.dut)
+
+        raw_addr = self.dut.hci_controller.GetMacAddress(empty_proto.Empty()).address
+
+        self.dut_address = common.BluetoothAddressWithType(
+            address=common.BluetoothAddress(address=raw_addr), type=common.PUBLIC_DEVICE_ADDRESS)
+        privacy_policy = le_initiator_address_facade.PrivacyPolicy(
+            address_policy=le_initiator_address_facade.AddressPolicy.USE_PUBLIC_ADDRESS,
+            address_with_type=self.dut_address)
+        self.dut.security.SetLeInitiatorAddressPolicy(privacy_policy)
+        self.cert_address = common.BluetoothAddressWithType(
+            address=common.BluetoothAddress(
+                address=self.cert.hci_controller.GetMacAddress(empty_proto.Empty()).address),
+            type=common.PUBLIC_DEVICE_ADDRESS)
+        cert_privacy_policy = le_initiator_address_facade.PrivacyPolicy(
+            address_policy=le_initiator_address_facade.AddressPolicy.USE_PUBLIC_ADDRESS,
+            address_with_type=self.cert_address)
+        self.cert.security.SetLeInitiatorAddressPolicy(cert_privacy_policy)
+
+        asserts.skip("Unhandled race condition - Flaky test")
+
+    def teardown_test(self):
+        self.dut_hci.close()
+        self.dut_security.close()
+        self.cert_security.close()
+
+    def _prepare_cert_for_connection(self):
+        # DUT Advertises
+        gap_name = hci_packets.GapData()
+        gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+        gap_name.data = list(bytes(b'Im_The_CERT'))
+        gap_data = le_advertising_facade.GapDataMsg(data=bytes(gap_name.Serialize()))
+        config = le_advertising_facade.AdvertisingConfig(
+            advertisement=[gap_data],
+            interval_min=512,
+            interval_max=768,
+            advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
+            own_address_type=common.USE_PUBLIC_DEVICE_ADDRESS,
+            channel_map=7,
+            filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES)
+        request = le_advertising_facade.CreateAdvertiserRequest(config=config)
+        create_response = self.cert.hci_le_advertising_manager.CreateAdvertiser(request)
+
+    def _prepare_dut_for_connection(self):
+        # DUT Advertises
+        gap_name = hci_packets.GapData()
+        gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+        gap_name.data = list(bytes(b'Im_The_DUT'))
+        gap_data = le_advertising_facade.GapDataMsg(data=bytes(gap_name.Serialize()))
+        config = le_advertising_facade.AdvertisingConfig(
+            advertisement=[gap_data],
+            interval_min=512,
+            interval_max=768,
+            advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
+            own_address_type=common.USE_PUBLIC_DEVICE_ADDRESS,
+            channel_map=7,
+            filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES)
+        request = le_advertising_facade.CreateAdvertiserRequest(config=config)
+        create_response = self.dut.hci_le_advertising_manager.CreateAdvertiser(request)
+
+    @metadata(pts_test_id="SM/MAS/PROT/BV-01-C", pts_test_name="SMP Time Out – IUT Initiator")
+    def test_le_smp_timeout_iut_initiator(self):
+        """
+            Verify that the IUT handles the lack of pairing response after 30 seconds when acting as initiator.
+        """
+        self._prepare_cert_for_connection()
+        self.dut.security.CreateBondLe(self.cert_address)
+        assertThat(self.dut_security.get_bond_stream()).emits(
+            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BOND_FAILED, self.cert_address), timeout=timedelta(seconds=35))
+
+    @metadata(pts_test_id="SM/SLA/PROT/BV-02-C", pts_test_name="SMP Time Out – IUT Responder")
+    def test_le_smp_timeout_iut_responder(self):
+        """
+            Verify that the IUT handles the lack of pairing response after 30 seconds when acting as initiator.
+        """
+        self.cert.security.SetLeIoCapability(KEYBOARD_ONLY)
+        self.dut.security.SetLeIoCapability(DISPLAY_ONLY)
+
+        self._prepare_dut_for_connection()
+
+        # 1. Lower Tester transmits Pairing Request.
+        self.cert.security.CreateBondLe(self.dut_address)
+
+        assertThat(self.dut_security.get_ui_stream()).emits(
+            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.cert_address), timeout=timedelta(seconds=35))
+
+        # 2. IUT responds with Pairing Response.
+        self.dut.security.SendUiCallback(
+            UiCallbackMsg(
+                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.cert_address))
+
+        # 3. In phase 2, Lower Tester does not issue the expected Pairing Confirm.
+
+        # Here the cert receives DISPLAY_PASSKEY_ENTRY. By not replying to it we make sure Pairing Confirm is never sent
+        assertThat(self.cert_security.get_ui_stream()).emits(
+            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PASSKEY_ENTRY, self.dut_address), timeout=timedelta(seconds=5))
+
+        # 4. IUT times out 30 seconds after issued Pairing Response and reports the failure to the Upper Tester.
+        assertThat(self.dut_security.get_bond_stream()).emits(
+            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BOND_FAILED, self.cert_address), timeout=timedelta(seconds=35))
+
+        # 5. After additionally (at least) 10 seconds the Lower Tester issues the expected Pairing Confirm.
+        # 6. The IUT closes the connection before receiving the delayed response or does not respond to it when it is received.
+        #TODO:
+        #assertThat(self.dut_hci.get_event_stream()).emits(HciMatchers.Disconnect())
+
+    @metadata(pts_test_id="SM/MAS/JW/BV-01-C", pts_test_name="Just Works IUT Initiator – Success")
+    def test_just_works_iut_initiator(self):
+        """
+            Verify that the IUT performs the Just Works pairing procedure correctly as central, initiator when both sides do not require MITM protection.
+        """
+        self._prepare_cert_for_connection()
+
+        self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
+        self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
+        self.dut_security.SetLeAuthRequirements()
+
+        self.cert.security.SetLeIoCapability(DISPLAY_ONLY)
+        self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
+        self.cert_security.SetLeAuthRequirements()
+
+        # 1. IUT transmits Pairing Request command with:
+        # a. IO capability set to any IO capability
+        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
+        # c. AuthReq Bonding Flags set to ‘00’ and the MITM flag set to ‘0’ and all the reserved bits are set to ‘0’
+        self.dut.security.CreateBondLe(self.cert_address)
+
+        assertThat(self.cert_security.get_ui_stream()).emits(
+            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.dut_address))
+
+        # 2. Lower Tester responds with a Pairing Response command, with:
+        # a. IO capability set to “KeyboardDisplay”
+        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
+        # c. AuthReq Bonding Flags set to ‘00’, and the MITM flag set to ‘0’ and all the reserved bits are set to ‘0’
+        self.cert.security.SendUiCallback(
+            UiCallbackMsg(
+                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.dut_address))
+
+        # 3. IUT and Lower Tester perform phase 2 of the just works pairing procedure and establish an encrypted link with the key generated in phase 2.
+        assertThat(self.dut_security.get_bond_stream()).emits(
+            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address))
+
+    @metadata(pts_test_id="SM/SLA/JW/BV-02-C", pts_test_name="Just Works IUT Responder – Success")
+    def test_just_works_iut_responder(self):
+        """
+            Verify that the IUT is able to perform the Just Works pairing procedure correctly when acting as peripheral, responder.
+        """
+        self._prepare_dut_for_connection()
+
+        self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
+        self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
+        self.dut_security.SetLeAuthRequirements()
+
+        self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
+        self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
+        self.cert_security.SetLeAuthRequirements()
+
+        # 1. Lower Tester transmits Pairing Request command with:
+        # a. IO capability set to “NoInputNoOutput”
+        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
+        # c. MITM flag set to ‘0’ and all reserved bits are set to ‘0’
+        self.cert.security.CreateBondLe(self.dut_address)
+
+        assertThat(self.dut_security.get_ui_stream()).emits(
+            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.cert_address))
+
+        # 2. IUT responds with a Pairing Response command, with:
+        # a. IO capability set to any IO capability
+        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
+        self.dut.security.SendUiCallback(
+            UiCallbackMsg(
+                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.cert_address))
+
+        # IUT and Lower Tester perform phase 2 of the just works pairing and establish an encrypted link with the generated STK.
+        assertThat(self.dut_security.get_bond_stream()).emits(
+            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address))
+
+    @metadata(
+        pts_test_id="SM/SLA/JW/BI-03-C", pts_test_name="Just Works IUT Responder – Handle AuthReq flag RFU correctly")
+    def test_just_works_iut_responder_auth_req_rfu(self):
+        """
+            Verify that the IUT is able to perform the Just Works pairing procedure when receiving additional bits set in the AuthReq flag. Reserved For Future Use bits are correctly handled when acting as peripheral, responder.
+        """
+        self._prepare_dut_for_connection()
+
+        self.dut.security.SetLeIoCapability(KEYBOARD_DISPLAY)
+        self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
+        self.dut_security.SetLeAuthRequirements()
+
+        self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
+        self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
+        self.cert_security.SetLeAuthRequirements(mitm=1, secure_connections=1, reserved_bits=2)
+
+        # 1. Lower Tester transmits Pairing Request command with:
+        # a. IO Capability set to ”NoInputNoOutput”
+        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
+        # c. MITM set to ‘0’ and all reserved bits are set to ‘1’
+        self.cert.security.CreateBondLe(self.dut_address)
+
+        assertThat(self.dut_security.get_ui_stream()).emits(
+            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.cert_address))
+
+        # 2. IUT responds with a Pairing Response command, with:
+        # a. IO capability set to any IO capability
+        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
+        # c. All reserved bits are set to ‘0’
+        self.dut.security.SendUiCallback(
+            UiCallbackMsg(
+                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.cert_address))
+
+        # 3. IUT and Lower Tester perform phase 2 of the just works pairing and establish an encrypted link with the generated STK.
+        assertThat(self.dut_security.get_bond_stream()).emits(
+            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address))
+
+    @metadata(
+        pts_test_id="SM/MAS/JW/BI-04-C", pts_test_name="Just Works IUT Initiator – Handle AuthReq flag RFU correctly")
+    def test_just_works_iut_initiator_auth_req_rfu(self):
+        """
+            Verify that the IUT is able to perform the Just Works pairing procedure when receiving additional bits set in the AuthReq flag. Reserved For Future Use bits are correctly handled when acting as central, initiator.
+        """
+        self._prepare_cert_for_connection()
+
+        self.dut.security.SetLeIoCapability(KEYBOARD_DISPLAY)
+        self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
+        self.dut_security.SetLeAuthRequirements()
+
+        self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
+        self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
+        self.cert_security.SetLeAuthRequirements(mitm=1, secure_connections=1, reserved_bits=3)
+
+        # 1. IUT transmits a Pairing Request command with:
+        # a. IO Capability set to any IO Capability
+        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
+        # c. All reserved bits are set to ‘0’. For the purposes of this test the Secure Connections bit and the Keypress bits in the AuthReq bonding flag set by the IUT are ignored by the Lower Tester.
+        self.dut.security.CreateBondLe(self.cert_address)
+
+        assertThat(self.cert_security.get_ui_stream()).emits(
+            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.dut_address))
+
+        # 2. Lower Tester responds with a Pairing Response command, with:
+        # a. IO Capability set to “NoInputNoOutput”
+        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
+        # c. AuthReq bonding flag set to the value indicated in the IXIT [7] for ‘Bonding Flags’ and the MITM flag set to ‘0’ and all reserved bits are set to ‘1’. The SC and Keypress bits in the AuthReq bonding flag are set to 0 by the Lower Tester for this test.
+        self.cert.security.SendUiCallback(
+            UiCallbackMsg(
+                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.dut_address))
+
+        # 3. IUT and Lower Tester perform phase 2 of the just works pairing and establish an encrypted link with the generated STK.
+        assertThat(self.dut_security.get_bond_stream()).emits(
+            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address))
+
+    @metadata(
+        pts_test_id="SM/MAS/SCJW/BV-01-C", pts_test_name="Just Works, IUT Initiator, Secure Connections – Success")
+    def test_just_works_iut_initiator_secure_connections(self):
+        """
+            Verify that the IUT supporting LE Secure Connections performs the Just Works or Numeric Comparison pairing procedure correctly as initiator.
+        """
+        self._prepare_cert_for_connection()
+
+        self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
+        self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
+        self.dut_security.SetLeAuthRequirements(secure_connections=1)
+
+        self.cert.security.SetLeIoCapability(DISPLAY_ONLY)
+        self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
+        self.cert_security.SetLeAuthRequirements(secure_connections=1)
+
+        # 1. IUT transmits Pairing Request command with:
+        # a. IO capability set to any IO capability
+        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
+        # c. AuthReq Bonding Flags set to ‘00’, the MITM flag set to either ‘0’ for Just Works or '1' for Numeric Comparison, Secure Connections flag set to '1' and all the reserved bits are set to ‘0’
+        self.dut.security.CreateBondLe(self.cert_address)
+
+        assertThat(self.cert_security.get_ui_stream()).emits(
+            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.dut_address))
+
+        # 2. Lower Tester responds with a Pairing Response command, with:
+        # a. IO capability set to “KeyboardDisplay”
+        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
+        # c. AuthReq Bonding Flags set to ‘00’, the MITM flag set to ‘0’, Secure Connections flag set to '1' and all the reserved bits are set to ‘0’
+        self.cert.security.SendUiCallback(
+            UiCallbackMsg(
+                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.dut_address))
+
+        # 3. IUT and Lower Tester perform phase 2 of the Just Works or Numeric Comparison pairing procedure according to the MITM flag and IO capabilities, and establish an encrypted link with the LTK generated in phase 2.
+        assertThat(self.dut_security.get_bond_stream()).emits(
+            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address))
+
+    @metadata(
+        pts_test_id="SM/SLA/SCJW/BV-02-C", pts_test_name="Just Works, IUT Responder, Secure Connections – Success")
+    def test_just_works_iut_responder_secure_connections(self):
+        """
+            Verify that the IUT supporting LE Secure Connections is able to perform the Just Works or Numeric Comparison pairing procedure correctly when acting as responder.
+        """
+        self._prepare_dut_for_connection()
+
+        self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
+        self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
+        self.dut_security.SetLeAuthRequirements(secure_connections=1)
+
+        self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
+        self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
+        self.cert_security.SetLeAuthRequirements(secure_connections=1)
+
+        # 1. Lower Tester transmits Pairing Request command with:
+        # a. IO capability set to “NoInputNoOutput”
+        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
+        # c. AuthReq Bonding Flags set to ‘00’, MITM flag set to ‘0’, Secure Connections flag set to '1' and all reserved bits are set to ‘0’
+        self.cert.security.CreateBondLe(self.dut_address)
+
+        assertThat(self.dut_security.get_ui_stream()).emits(
+            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.cert_address))
+
+        # 2. IUT responds with a Pairing Response command, with:
+        # a. IO capability set to any IO capability
+        # b. AuthReq Bonding Flags set to ‘00’, MITM flag set to either ‘0’ for Just Works or '1' for Numeric Comparison, Secure Connections flag set to '1' and all reserved bits are set to ‘0’
+        self.dut.security.SendUiCallback(
+            UiCallbackMsg(
+                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.cert_address))
+
+        # 3. UT and Lower Tester perform phase 2 of the Just Works or Numeric Comparison pairing procedure according to the MITM flag and IO capabilities, and establish an encrypted link with the LTK generated in phase 2.
+        assertThat(self.dut_security.get_bond_stream()).emits(
+            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address))
+
+    @metadata(
+        pts_test_id="SM/SLA/SCJW/BV-03-C",
+        pts_test_name="Just Works, IUT Responder, Secure Connections – Handle AuthReq Flag RFU Correctly")
+    def test_just_works_iut_responder_secure_connections_auth_req_rfu(self):
+        """
+            Verify that the IUT is able to perform the Just Works pairing procedure when receiving additional bits set in the AuthReq flag. Reserved For Future Use bits are correctly handled when acting as peripheral, responder.
+        """
+        self._prepare_dut_for_connection()
+
+        self.dut.security.SetLeIoCapability(KEYBOARD_DISPLAY)
+        self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
+        self.dut_security.SetLeAuthRequirements(secure_connections=1)
+
+        self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
+        self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
+        self.cert_security.SetLeAuthRequirements(mitm=1, secure_connections=1, reserved_bits=3)
+
+        # 1. Lower Tester transmits Pairing Request command with:
+        # a. IO Capability set to ”NoInputNoOutput”
+        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
+        # c. MITM set to ‘0’ and all reserved bits are set to a random value.
+        self.cert.security.CreateBondLe(self.dut_address)
+
+        assertThat(self.dut_security.get_ui_stream()).emits(
+            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.cert_address))
+
+        # 2. IUT responds with a Pairing Response command, with:
+        # a. IO capability set to any IO capability
+        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
+        # c. All reserved bits are set to ‘0’
+        self.dut.security.SendUiCallback(
+            UiCallbackMsg(
+                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.cert_address))
+
+        # 3. IUT and Lower Tester perform phase 2 of the Just Works pairing and establish an encrypted link with the generated LTK.
+        assertThat(self.dut_security.get_bond_stream()).emits(
+            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address))
+
+    @metadata(
+        pts_test_id="SM/MAS/SCJW/BV-04-C",
+        pts_test_name="Just Works, IUT Initiator, Secure Connections – Handle AuthReq Flag RFU Correctly")
+    def test_just_works_iut_initiator_secure_connections_auth_req_rfu(self):
+        """
+            Verify that the IUT is able to perform the Just Works pairing procedure when receiving additional bits set in the AuthReq flag. Reserved For Future Use bits are correctly handled when acting as central, initiator.
+        """
+        self._prepare_cert_for_connection()
+
+        self.dut.security.SetLeIoCapability(KEYBOARD_DISPLAY)
+        self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
+        self.dut_security.SetLeAuthRequirements(secure_connections=1)
+
+        self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
+        self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
+        self.cert_security.SetLeAuthRequirements(mitm=1, secure_connections=1, reserved_bits=3)
+
+        # 1. IUT transmits a Pairing Request command with:
+        # a. IO Capability set to any IO Capability
+        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
+        # c. All reserved bits are set to ‘0’.
+        self.dut.security.CreateBondLe(self.cert_address)
+
+        assertThat(self.cert_security.get_ui_stream()).emits(
+            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.dut_address))
+
+        # 2. Lower Tester responds with a Pairing Response command, with:
+        # a. IO Capability set to “NoInputNoOutput”
+        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
+        # c. AuthReq bonding flag set to the value indicated in the IXIT [7] for ‘Bonding Flags’ and the MITM flag set to ‘0’ and all reserved bits are set to a random value.
+        self.cert.security.SendUiCallback(
+            UiCallbackMsg(
+                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.dut_address))
+
+        # 3. IUT and Lower Tester perform phase 2 of the Just Works pairing and establish an encrypted link with the generated LTK.
+        assertThat(self.dut_security.get_bond_stream()).emits(
+            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address))
+
+    @metadata(
+        pts_test_id="SM/MAS/EKS/BV-01-C",
+        pts_test_name="IUT initiator, Lower Tester Maximum Encryption Key Size = Min_Encryption_Key_Length")
+    def test_min_encryption_key_size_equal_to_max_iut_initiator(self):
+        """
+            Verify that the IUT uses correct key size during encryption as initiator.
+        """
+        self._prepare_cert_for_connection()
+
+        self.dut.security.SetLeIoCapability(KEYBOARD_DISPLAY)
+        self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
+        self.dut_security.SetLeAuthRequirements(secure_connections=1)
+        self.dut.security.SetLeMaximumEncryptionKeySize(
+            LeMaximumEncryptionKeySizeMessage(maximum_encryption_key_size=0x10))
+
+        self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
+        self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
+        self.cert_security.SetLeAuthRequirements(mitm=1, secure_connections=1)
+        self.cert.security.SetLeMaximumEncryptionKeySize(
+            LeMaximumEncryptionKeySizeMessage(maximum_encryption_key_size=0x07))
+
+        # 1. IUT transmits a Pairing Request
+        self.dut.security.CreateBondLe(self.cert_address)
+
+        assertThat(self.cert_security.get_ui_stream()).emits(
+            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.dut_address))
+
+        # 2. Lower Tester responds with Pairing Response command with Maximum Encryption Key Size field set to Min_Encryption_Key_Length’.
+        self.cert.security.SendUiCallback(
+            UiCallbackMsg(
+                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.dut_address))
+
+        # 3. IUT and Lower Tester perform phase 2 of the LE pairing and establish an encrypted link with the key generated in phase 2.
+        assertThat(self.dut_security.get_bond_stream()).emits(
+            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address))
+
+    @metadata(
+        pts_test_id="SM/SLA/EKS/BV-02-C",
+        pts_test_name="IUT Responder, Lower Tester Maximum Encryption Key Size = Min_Encryption_Key_Length")
+    def test_min_encryption_key_size_equal_to_max_iut_responder(self):
+        """
+            Verify that the IUT uses correct key size during encryption as responder.
+        """
+        self._prepare_dut_for_connection()
+
+        self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
+        self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
+        self.dut_security.SetLeAuthRequirements()
+        self.dut.security.SetLeMaximumEncryptionKeySize(
+            LeMaximumEncryptionKeySizeMessage(maximum_encryption_key_size=0x07))
+
+        self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
+        self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
+        self.cert_security.SetLeAuthRequirements()
+        self.cert.security.SetLeMaximumEncryptionKeySize(
+            LeMaximumEncryptionKeySizeMessage(maximum_encryption_key_size=0x10))
+
+        # 1. Lower Tester initiates Pairing Request command with Maximum Encryption Key Size field set to Min_Encryption_Key_Length’.
+        self.cert.security.CreateBondLe(self.dut_address)
+
+        assertThat(self.dut_security.get_ui_stream()).emits(
+            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.cert_address))
+
+        # 2. IUT responds with Pairing Response command.
+        self.dut.security.SendUiCallback(
+            UiCallbackMsg(
+                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.cert_address))
+
+        #3. IUT and Lower Tester perform phase 2 of the LE pairing and establish an encrypted link with the key generated in phase 2.
+        assertThat(self.dut_security.get_bond_stream()).emits(
+            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address))
+
+    @metadata(
+        pts_test_id="SM/MAS/EKS/BI-01-C",
+        pts_test_name="IUT initiator, Lower Tester Maximum Encryption Key Size < Min_Encryption_Key_Length")
+    def test_min_encryption_key_size_less_than_min_iut_initiator(self):
+        """
+            Verify that the IUT checks that the resultant encryption key size is not smaller than the minimum key size.
+        """
+        self._prepare_cert_for_connection()
+
+        self.dut.security.SetLeIoCapability(KEYBOARD_DISPLAY)
+        self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
+        self.dut_security.SetLeAuthRequirements(secure_connections=1)
+        self.dut.security.SetLeMaximumEncryptionKeySize(
+            LeMaximumEncryptionKeySizeMessage(maximum_encryption_key_size=0x10))
+
+        self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
+        self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
+        self.cert_security.SetLeAuthRequirements(mitm=1, secure_connections=1)
+        self.cert.security.SetLeMaximumEncryptionKeySize(
+            LeMaximumEncryptionKeySizeMessage(maximum_encryption_key_size=0x06))
+
+        # 1. IUT transmits a Pairing Request
+        self.dut.security.CreateBondLe(self.cert_address)
+
+        assertThat(self.cert_security.get_ui_stream()).emits(
+            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.dut_address))
+
+        # 2. Lower Tester responds with Pairing Response command with Maximum Encryption Key Size field set to Min_Encryption_Key_Length-1’.
+        self.cert.security.SendUiCallback(
+            UiCallbackMsg(
+                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.dut_address))
+
+        # 3. IUT transmits the Pairing Failed command.
+        assertThat(self.dut_security.get_bond_stream()).emits(
+            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BOND_FAILED, self.cert_address,
+                                     int(PairingFailedReason.ENCRYPTION_KEY_SIZE)))
+
+    @metadata(
+        pts_test_id="SM/SLA/EKS/BI-02-C",
+        pts_test_name="IUT Responder, Lower Tester Maximum Encryption Key Size < Min_Encryption_Key_Length")
+    def test_min_encryption_key_size_less_than_min_iut_responder(self):
+        """
+            Verify that the IUT uses correct key size during encryption as responder.
+        """
+        self._prepare_dut_for_connection()
+
+        self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
+        self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
+        self.dut_security.SetLeAuthRequirements()
+        self.dut.security.SetLeMaximumEncryptionKeySize(
+            LeMaximumEncryptionKeySizeMessage(maximum_encryption_key_size=0x06))
+
+        self.cert.security.SetLeIoCapability(NO_INPUT_NO_OUTPUT)
+        self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
+        self.cert_security.SetLeAuthRequirements()
+        self.cert.security.SetLeMaximumEncryptionKeySize(
+            LeMaximumEncryptionKeySizeMessage(maximum_encryption_key_size=0x10))
+
+        # 1. Lower Tester initiates Pairing Request command with Maximum Encryption Key Size field set to Min_Encryption_Key_Length-1.
+        self.cert.security.CreateBondLe(self.dut_address)
+
+        assertThat(self.dut_security.get_ui_stream()).emits(
+            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.cert_address))
+
+        self.dut.security.SendUiCallback(
+            UiCallbackMsg(
+                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.cert_address))
+
+        #3. IUT transmits the Pairing Failed command.
+        assertThat(self.cert_security.get_bond_stream()).emits(
+            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BOND_FAILED, self.dut_address,
+                                     int(PairingFailedReason.ENCRYPTION_KEY_SIZE)))
+
+    @metadata(
+        pts_test_id="SM/MAS/SCPK/BV-01-C", pts_test_name="Passkey Entry, IUT Initiator, Secure Connections – Success")
+    def test_passkey_entry_iut_initiator_secure_connections(self):
+        """
+            Verify that the IUT supporting LE Secure Connections performs the Passkey Entry pairing procedure correctly as central, initiator.
+        """
+        self._prepare_cert_for_connection()
+
+        self.dut.security.SetLeIoCapability(DISPLAY_ONLY)
+        self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
+        self.dut_security.SetLeAuthRequirements(secure_connections=1)
+
+        self.cert.security.SetLeIoCapability(KEYBOARD_ONLY)
+        self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
+        self.cert_security.SetLeAuthRequirements(mitm=1, secure_connections=1)
+
+        # 1. IUT transmits Pairing Request command with:
+        # a. IO capability set to “DisplayOnly” or “KeyboardOnly”
+        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
+        # c. AuthReq bonding flag set to ‘00’, the MITM flag set to ‘0’ and Secure Connections flag set to '1'. Keypress bit is set to '1' if supported
+        self.dut.security.CreateBondLe(self.cert_address)
+
+        assertThat(self.cert_security.get_ui_stream()).emits(
+            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.dut_address))
+
+        # 2. Lower Tester responds with a Pairing Response command, with:
+        # a. IO capability set to “KeyboardOnly”
+        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
+        # c. AuthReq bonding flag set to ‘00’, the MITM flag set to ‘1’, Secure Connections flag set to '1' and all reserved bits are set to ‘0’. Keypress bit is set to '1' if supported by the IUT.
+        self.cert.security.SendUiCallback(
+            UiCallbackMsg(
+                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.dut_address))
+
+        assertThat(self.cert_security.get_ui_stream()).emits(
+            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PASSKEY_ENTRY, self.dut_address))
+
+        # 3. During the phase 2 pairing, the IUT displays the 6-digit passkey while the Lower Tester prompts user to enter the 6-digit passkey. If the IUT’s IO capabilities are “KeyboardOnly” the passkey is not displayed and both IUT and Lower Tester enter the same 6-digit passkey. If Keypress bit is set, pairing keypress notifications are sent by the Lower Tester.
+        passkey = self.dut_security.wait_for_ui_event_passkey()
+
+        if passkey == 0:
+            print("Passkey did not arrive into test")
+
+        # 4. IUT and Lower Tester use the same 6-digit passkey.
+        self.cert.security.SendUiCallback(
+            UiCallbackMsg(
+                message_type=UiCallbackType.PASSKEY, numeric_value=passkey, unique_id=1, address=self.dut_address))
+
+        # 5. IUT and Lower Tester perform phase 2 of the Passkey Entry pairing procedure and establish an encrypted link with the LTK generated in phase 2.
+        assertThat(self.dut_security.get_bond_stream()).emits(
+            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address), timeout=timedelta(seconds=10))
+
+    @metadata(
+        pts_test_id="SM/SLA/SCPK/BV-02-C", pts_test_name="Passkey Entry, IUT Responder, Secure Connections – Success")
+    def test_passkey_entry_iut_responder_secure_connections(self):
+        """
+            Verify that the IUT supporting LE Secure Connections is able to perform the Passkey Entry pairing procedure correctly when acting as peripheral, responder.
+        """
+        self._prepare_dut_for_connection()
+
+        self.dut.security.SetLeIoCapability(DISPLAY_ONLY)
+        self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
+        self.dut_security.SetLeAuthRequirements(secure_connections=1)
+
+        self.cert.security.SetLeIoCapability(KEYBOARD_DISPLAY)
+        self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
+        self.cert_security.SetLeAuthRequirements(mitm=1, secure_connections=1)
+
+        # 1. Lower Tester transmits Pairing Request command with:
+        # a. IO capability set to “KeyboardDisplay”
+        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
+        # c. AuthReq bonding flag set to the value indicated in the IXIT [7] for ‘Bonding Flags’, and the MITM flag set to ‘1’ Secure Connections flag set to '1' and all reserved bits are set to ‘0’
+        self.cert.security.CreateBondLe(self.dut_address)
+
+        assertThat(self.dut_security.get_ui_stream()).emits(
+            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.cert_address))
+
+        # 2. IUT responds with a Pairing Response command, with:
+        # a. IO capability set to “KeyboardOnly” or “KeyboardDisplay” or “DisplayYesNo” or “DisplayOnly”
+        # b. Secure Connections flag set to '1'. Keypress bit is set to '1' if supported by IUT
+        self.dut.security.SendUiCallback(
+            UiCallbackMsg(
+                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.cert_address))
+
+        # 3. During the phase 2 passkey pairing process, Lower Tester displays the 6-digit passkey while the IUT prompts user to enter the 6-digit passkey. If the IO capabilities of the IUT are “DisplayYesNo” or “DisplayOnly” the IUT displays the 6-digit passkey while the Lower Tester enters the 6-digit passkey. If Keypress bit is set, pairing keypress notifications are send by the IUT
+        passkey = self.dut_security.wait_for_ui_event_passkey()
+
+        if passkey == 0:
+            print("Passkey did not arrive into test")
+
+        assertThat(self.cert_security.get_ui_stream()).emits(
+            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PASSKEY_ENTRY, self.dut_address))
+
+        # 4. IUT and Lower Tester use the same pre-defined 6-digit passkey.
+        self.cert.security.SendUiCallback(
+            UiCallbackMsg(
+                message_type=UiCallbackType.PASSKEY, numeric_value=passkey, unique_id=1, address=self.dut_address))
+
+        # 5. IUT and Lower Tester perform phase 2 of the LE pairing and establish an encrypted link with the LTK generated in phase 2.
+        assertThat(self.dut_security.get_bond_stream()).emits(
+            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address), timeout=timedelta(seconds=10))
+
+    @metadata(
+        pts_test_id="SM/SLA/SCPK/BV-03-C",
+        pts_test_name="Passkey Entry, IUT Responder, Secure Connections – Handle AuthReq Flag RFU Correctly")
+    def test_passkey_entry_iut_responder_secure_connections_auth_req_rfu(self):
+        """
+            Verify that the IUT supporting LE Secure Connections is able to perform the Passkey Entry pairing procedure when receiving additional bits set in the AuthReq flag. Reserved For Future Use bits are correctly handled when acting as peripheral, responder.
+        """
+        self._prepare_dut_for_connection()
+
+        self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
+        self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
+        self.dut_security.SetLeAuthRequirements(secure_connections=1)
+
+        self.cert.security.SetLeIoCapability(DISPLAY_ONLY)
+        self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
+        self.cert_security.SetLeAuthRequirements(mitm=1, secure_connections=1, reserved_bits=3)
+
+        # 1. Lower Tester transmits Pairing Request command with:
+        # a. IO Capability set to ”KeyboardOnly”
+        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
+        # c. MITM set to ‘1’ and all reserved bits are set to a random value
+        self.cert.security.CreateBondLe(self.dut_address)
+
+        assertThat(self.dut_security.get_ui_stream()).emits(
+            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.cert_address))
+
+        # 2. IUT responds with a Pairing Response command, with:
+        # a. IO Capability set to “KeyboardOnly” or “DisplayOnly”
+        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
+        # c. All reserved bits are set to ‘0’
+        self.dut.security.SendUiCallback(
+            UiCallbackMsg(
+                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.cert_address))
+
+        passkey = self.cert_security.wait_for_ui_event_passkey()
+
+        if passkey == 0:
+            print("Passkey did not arrive into test")
+
+        assertThat(self.dut_security.get_ui_stream()).emits(
+            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PASSKEY_ENTRY, self.cert_address))
+
+        self.dut.security.SendUiCallback(
+            UiCallbackMsg(
+                message_type=UiCallbackType.PASSKEY, numeric_value=passkey, unique_id=1, address=self.cert_address))
+
+        # 3. IUT and Lower Tester perform phase 2 of the Passkey Entry pairing and establish an encrypted link with the generated LTK.
+        assertThat(self.dut_security.get_bond_stream()).emits(
+            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address), timeout=timedelta(seconds=10))
+
+    @metadata(
+        pts_test_id="SM/MAS/SCPK/BV-04-C",
+        pts_test_name="Passkey Entry, IUT Initiator, Secure Connections – Handle AuthReq Flag RFU Correctly")
+    def test_passkey_entry_iut_initiator_secure_connections_auth_req_rfu(self):
+        """
+            Verify that the IUT supporting LE Secure Connections is able to perform the Passkey Entry pairing procedure when receiving additional bits set in the AuthReq flag. Reserved For Future Use bits are correctly handled when acting as central, initiator.
+        """
+        self._prepare_cert_for_connection()
+
+        self.dut.security.SetLeIoCapability(KEYBOARD_DISPLAY)
+        self.dut.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
+        self.dut_security.SetLeAuthRequirements(secure_connections=1)
+
+        self.cert.security.SetLeIoCapability(KEYBOARD_ONLY)
+        self.cert.security.SetLeOobDataPresent(OOB_NOT_PRESENT)
+        self.cert_security.SetLeAuthRequirements(mitm=1, secure_connections=1, reserved_bits=3)
+
+        # 1. IUT transmits a Pairing Request command with:
+        # a. IO Capability set to “DisplayOnly” or “DisplayYesNo” or “KeyboardOnly” or “KeyboardDisplay”
+        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
+        # c. All reserved bits are set to ‘0’.
+        self.dut.security.CreateBondLe(self.cert_address)
+
+        assertThat(self.cert_security.get_ui_stream()).emits(
+            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.dut_address))
+
+        # 2. Lower Tester responds with a Pairing Response command, with:
+        # a. IO Capability set to “KeyboardOnly”
+        # b. OOB data flag set to 0x00 (OOB Authentication data not present)
+        # c. AuthReq bonding flag set to the value indicated in the IXIT [7] for ‘Bonding Flags’ and the MITM flag set to ‘1’ and all reserved bits are set to a random value.
+        self.cert.security.SendUiCallback(
+            UiCallbackMsg(
+                message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.dut_address))
+
+        assertThat(self.cert_security.get_ui_stream()).emits(
+            SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PASSKEY_ENTRY, self.dut_address))
+
+        passkey = self.dut_security.wait_for_ui_event_passkey()
+
+        self.cert.security.SendUiCallback(
+            UiCallbackMsg(
+                message_type=UiCallbackType.PASSKEY, numeric_value=passkey, unique_id=1, address=self.dut_address))
+
+        # 3.    IUT and Lower Tester perform phase 2 of the Just Works pairing and establish an encrypted link with the generated LTK.
+        assertThat(self.dut_security.get_bond_stream()).emits(
+            SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address), timeout=timedelta(seconds=10))
+
+    @metadata(
+        pts_test_id="SM/MAS/SCOB/BV-01-C", pts_test_name="Out of Band, IUT Initiator, Secure Connections – Success")
+    def test_out_of_band_iut_initiator_secure_connections(self):
+        """
+            Verify that the IUT supporting LE Secure Connections performs the Out-of-Band pairing procedure correctly as central, initiator.
+        """
+
+        oob_combinations = [(OOB_NOT_PRESENT, OOB_PRESENT), (OOB_PRESENT, OOB_NOT_PRESENT), (OOB_PRESENT, OOB_PRESENT)]
+
+        for (dut_oob_flag, cert_oob_flag) in oob_combinations:
+            print("oob flag combination dut: " + str(dut_oob_flag) + ", cert: " + str(cert_oob_flag))
+
+            self._prepare_cert_for_connection()
+
+            if dut_oob_flag == LeOobDataFlag.PRESENT:
+                oobdata = self.cert.security.GetLeOutOfBandData(empty_proto.Empty())
+                self.dut.security.SetOutOfBandData(
+                    OobDataMessage(
+                        address=self.cert_address,
+                        confirmation_value=oobdata.confirmation_value,
+                        random_value=oobdata.random_value))
+
+            if cert_oob_flag == LeOobDataFlag.PRESENT:
+                oobdata = self.dut.security.GetLeOutOfBandData(empty_proto.Empty())
+                self.cert.security.SetOutOfBandData(
+                    OobDataMessage(
+                        address=self.dut_address,
+                        confirmation_value=oobdata.confirmation_value,
+                        random_value=oobdata.random_value))
+
+            self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
+            self.dut.security.SetLeOobDataPresent(dut_oob_flag)
+            self.dut_security.SetLeAuthRequirements(bond=1, mitm=1, secure_connections=1)
+
+            self.cert.security.SetLeIoCapability(DISPLAY_ONLY)
+            self.cert.security.SetLeOobDataPresent(cert_oob_flag)
+            self.cert_security.SetLeAuthRequirements(bond=1, mitm=1, secure_connections=1)
+
+            # 1. IUT transmits a Pairing Request command with OOB data flag set to either 0x00 or 0x01, and Secure Connections flag set to '1'.
+            self.dut.security.CreateBondLe(self.cert_address)
+
+            assertThat(self.cert_security.get_ui_stream()).emits(
+                SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.dut_address))
+
+            # 2. Lower Tester responds with a Pairing Response command with Secure Connections flag set to '1' and OOB data flag set to either 0x00 or 0x01.
+            self.cert.security.SendUiCallback(
+                UiCallbackMsg(
+                    message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.dut_address))
+
+            # 3. IUT uses the 128-bit value generated by the Lower Tester as the confirm value. Similarly, the Lower Tester uses the 128-bit value generated by the IUT as the confirm value.
+
+            # 4. IUT and Lower Tester perform phase 2 of the pairing process and establish an encrypted link with an LTK generated using the OOB data in phase 2.
+            assertThat(self.dut_security.get_bond_stream()).emits(
+                SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address), timeout=timedelta(seconds=10))
+
+            assertThat(self.cert_security.get_bond_stream()).emits(
+                SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.dut_address), timeout=timedelta(seconds=10))
+
+            self.dut.security.RemoveBond(self.cert_address)
+            self.cert.security.RemoveBond(self.dut_address)
+
+            assertThat(self.dut_security.get_bond_stream()).emits(
+                SecurityMatchers.BondMsg(BondMsgType.DEVICE_UNBONDED, self.cert_address))
+
+            self.dut_security.wait_device_disconnect(self.cert_address)
+            self.cert_security.wait_device_disconnect(self.dut_address)
+
+    @metadata(
+        pts_test_id="SM/SLA/SCOB/BV-02-C", pts_test_name="Out of Band, IUT Responder, Secure Connections – Success")
+    def test_out_of_band_iut_responder_secure_connections(self):
+        """
+            Verify that the IUT supporting LE Secure Connections is able to perform the Out-of-Band pairing procedure correctly when acting as peripheral, responder.
+        """
+
+        oob_combinations = [(OOB_NOT_PRESENT, OOB_PRESENT), (OOB_PRESENT, OOB_NOT_PRESENT), (OOB_PRESENT, OOB_PRESENT)]
+
+        for (dut_oob_flag, cert_oob_flag) in oob_combinations:
+            print("oob flag combination dut: " + str(dut_oob_flag) + ", cert: " + str(cert_oob_flag))
+
+            self._prepare_dut_for_connection()
+
+            if dut_oob_flag == LeOobDataFlag.PRESENT:
+                oobdata = self.cert.security.GetLeOutOfBandData(empty_proto.Empty())
+                self.dut.security.SetOutOfBandData(
+                    OobDataMessage(
+                        address=self.cert_address,
+                        confirmation_value=oobdata.confirmation_value,
+                        random_value=oobdata.random_value))
+
+            if cert_oob_flag == LeOobDataFlag.PRESENT:
+                oobdata = self.dut.security.GetLeOutOfBandData(empty_proto.Empty())
+                self.cert.security.SetOutOfBandData(
+                    OobDataMessage(
+                        address=self.dut_address,
+                        confirmation_value=oobdata.confirmation_value,
+                        random_value=oobdata.random_value))
+
+            self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
+            self.dut.security.SetLeOobDataPresent(dut_oob_flag)
+            self.dut_security.SetLeAuthRequirements(bond=1, mitm=1, secure_connections=1)
+
+            self.cert.security.SetLeIoCapability(DISPLAY_ONLY)
+            self.cert.security.SetLeOobDataPresent(cert_oob_flag)
+            self.cert_security.SetLeAuthRequirements(bond=1, mitm=1, secure_connections=1)
+
+            # 1. Lower Tester transmits a Pairing Request command with OOB data flag set to either 0x00 or 0x01, and Secure Connections flag set to '1'.
+            self.cert.security.CreateBondLe(self.dut_address)
+
+            assertThat(self.dut_security.get_ui_stream()).emits(
+                SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.cert_address))
+
+            # 2. IUT responds with a Pairing Response command with Secure Connections flag set to '1' and OOB data flag set to either 0x00 or 0x01.
+            self.dut.security.SendUiCallback(
+                UiCallbackMsg(
+                    message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.cert_address))
+
+            # 3. IUT uses the 128-bit value generated by the Lower Tester as the confirm value. Similarly, the Lower Tester uses the 128-bit value generated by the IUT as the confirm value.
+
+            # 4. IUT and Lower Tester perform phase 2 of the pairing process and establish an encrypted link with an LTK generated using the OOB data in phase 2.
+            assertThat(self.cert_security.get_bond_stream()).emits(
+                SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.dut_address), timeout=timedelta(seconds=10))
+
+            assertThat(self.dut_security.get_bond_stream()).emits(
+                SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address), timeout=timedelta(seconds=10))
+
+            self.cert.security.RemoveBond(self.dut_address)
+            self.dut.security.RemoveBond(self.cert_address)
+
+            assertThat(self.dut_security.get_bond_stream()).emits(
+                SecurityMatchers.BondMsg(BondMsgType.DEVICE_UNBONDED, self.cert_address))
+
+            self.cert_security.wait_device_disconnect(self.dut_address)
+            self.dut_security.wait_device_disconnect(self.cert_address)
+
+    @metadata(
+        pts_test_id="SM/SLA/SCOB/BV-03-C",
+        pts_test_name="Out of Band, IUT Responder, Secure Connections – Handle AuthReq Flag RFU Correctly")
+    def test_out_of_band_iut_responder_secure_connections_auth_req_rfu(self):
+        """
+            Verify that the IUT supporting LE Secure Connections is able to perform the Out-of-Band pairing procedure when receiving additional bits set in the AuthReq flag. Reserved For Future Use bits are correctly handled when acting as peripheral, responder.
+        """
+
+        reserved_bits_combinations = [1, 2, 3]
+
+        for reserved_bits in reserved_bits_combinations:
+            print("reserved bits in cert dut: " + str(reserved_bits))
+
+            self._prepare_dut_for_connection()
+
+            oobdata = self.cert.security.GetLeOutOfBandData(empty_proto.Empty())
+            self.dut.security.SetOutOfBandData(
+                OobDataMessage(
+                    address=self.cert_address,
+                    confirmation_value=oobdata.confirmation_value,
+                    random_value=oobdata.random_value))
+
+            oobdata = self.dut.security.GetLeOutOfBandData(empty_proto.Empty())
+            self.cert.security.SetOutOfBandData(
+                OobDataMessage(
+                    address=self.dut_address,
+                    confirmation_value=oobdata.confirmation_value,
+                    random_value=oobdata.random_value))
+
+            self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
+            self.dut.security.SetLeOobDataPresent(OOB_PRESENT)
+            self.dut_security.SetLeAuthRequirements(bond=1, mitm=0, secure_connections=1)
+
+            self.cert.security.SetLeIoCapability(DISPLAY_ONLY)
+            self.cert.security.SetLeOobDataPresent(OOB_PRESENT)
+            self.cert_security.SetLeAuthRequirements(bond=1, mitm=1, secure_connections=1, reserved_bits=reserved_bits)
+
+            # 1. Lower Tester transmits Pairing Request command with:
+            # a. IO Capability set to any IO capability
+            # b. OOB data flag set to 0x01 (OOB Authentication data from remote device present)
+            # c. MITM set to ‘0’, Secure Connections flag is set to '1', and all reserved bits are set to a random value.
+            self.cert.security.CreateBondLe(self.dut_address)
+
+            assertThat(self.dut_security.get_ui_stream()).emits(
+                SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.cert_address))
+
+            # 2. IUT responds with a Pairing Response command, with:
+            # a. IO Capability set to any IO capability
+            # b. OOB data flag set to 0x01 (OOB Authentication data present)
+            # c. Secure Connections flag is set to '1', All reserved bits are set to ‘0’
+            self.dut.security.SendUiCallback(
+                UiCallbackMsg(
+                    message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.cert_address))
+
+            # 3. IUT and Lower Tester perform phase 2 of the OOB authenticated pairing and establish an encrypted link with the generated LTK.
+
+            assertThat(self.cert_security.get_bond_stream()).emits(
+                SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.dut_address), timeout=timedelta(seconds=10))
+
+            assertThat(self.dut_security.get_bond_stream()).emits(
+                SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address), timeout=timedelta(seconds=10))
+
+            self.cert.security.RemoveBond(self.dut_address)
+            self.dut.security.RemoveBond(self.cert_address)
+
+            assertThat(self.dut_security.get_bond_stream()).emits(
+                SecurityMatchers.BondMsg(BondMsgType.DEVICE_UNBONDED, self.cert_address))
+
+            self.dut_security.wait_device_disconnect(self.cert_address)
+            self.cert_security.wait_device_disconnect(self.dut_address)
+
+    @metadata(
+        pts_test_id="SM/MAS/SCOB/BV-04-C",
+        pts_test_name="Out of Band, IUT Initiator, Secure Connections – Handle AuthReq Flag RFU Correctly")
+    def test_out_of_band_iut_initiator_secure_connections_auth_req_rfu(self):
+        """
+            Verify that the IUT supporting LE Secure Connections is able to perform the Out-of-Band pairing procedure when receiving additional bits set in the AuthReq flag. Reserved For Future Use bits are correctly handled when acting as central, initiator.
+        """
+
+        reserved_bits_combinations = [1, 2, 3]
+
+        for reserved_bits in reserved_bits_combinations:
+            print("reserved bits in cert dut: " + str(reserved_bits))
+
+            self._prepare_cert_for_connection()
+
+            oobdata = self.cert.security.GetLeOutOfBandData(empty_proto.Empty())
+            self.dut.security.SetOutOfBandData(
+                OobDataMessage(
+                    address=self.cert_address,
+                    confirmation_value=oobdata.confirmation_value,
+                    random_value=oobdata.random_value))
+
+            oobdata = self.dut.security.GetLeOutOfBandData(empty_proto.Empty())
+            self.cert.security.SetOutOfBandData(
+                OobDataMessage(
+                    address=self.dut_address,
+                    confirmation_value=oobdata.confirmation_value,
+                    random_value=oobdata.random_value))
+
+            self.dut.security.SetLeIoCapability(KEYBOARD_ONLY)
+            self.dut.security.SetLeOobDataPresent(OOB_PRESENT)
+            self.dut_security.SetLeAuthRequirements(bond=1, mitm=0, secure_connections=1, reserved_bits=0)
+
+            self.cert.security.SetLeIoCapability(DISPLAY_ONLY)
+            self.cert.security.SetLeOobDataPresent(OOB_PRESENT)
+            self.cert_security.SetLeAuthRequirements(bond=1, mitm=1, secure_connections=1, reserved_bits=reserved_bits)
+
+            # 1. IUT transmits Pairing Request command with:
+            # a. IO Capability set to any IO capability
+            # b. OOB data flag set to 0x01 (OOB Authentication data present)
+            # c. MITM set to ‘0’, Secure Connections flag is set to '1', and all reserved bits are set to ‘0’
+            self.dut.security.CreateBondLe(self.cert_address)
+
+            assertThat(self.cert_security.get_ui_stream()).emits(
+                SecurityMatchers.UiMsg(UiMsgType.DISPLAY_PAIRING_PROMPT, self.dut_address))
+
+            # 2. Lower Tester responds with a Pairing Response command, with:
+            # a. IO Capability set to any IO capability
+            # b. OOB data flag set to 0x01 (OOB Authentication data present)
+            # c. Secure Connections flag is set to '1', and all reserved bits are set to a random value.
+            self.cert.security.SendUiCallback(
+                UiCallbackMsg(
+                    message_type=UiCallbackType.PAIRING_PROMPT, boolean=True, unique_id=1, address=self.dut_address))
+
+            # 3. IUT and Lower Tester perform phase 2 of the OOB authenticated pairing and establish an encrypted link with the generated LTK.
+
+            assertThat(self.dut_security.get_bond_stream()).emits(
+                SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.cert_address), timeout=timedelta(seconds=10))
+
+            assertThat(self.cert_security.get_bond_stream()).emits(
+                SecurityMatchers.BondMsg(BondMsgType.DEVICE_BONDED, self.dut_address), timeout=timedelta(seconds=10))
+
+            self.dut.security.RemoveBond(self.cert_address)
+            self.cert.security.RemoveBond(self.dut_address)
+
+            assertThat(self.dut_security.get_bond_stream()).emits(
+                SecurityMatchers.BondMsg(BondMsgType.DEVICE_UNBONDED, self.cert_address))
+
+            self.dut_security.wait_device_disconnect(self.cert_address)
+            self.cert_security.wait_device_disconnect(self.dut_address)
diff --git a/gd/security/cert/security_test.py b/gd/security/cert/security_test.py
index 28e04c9..93337ba 100644
--- a/gd/security/cert/security_test.py
+++ b/gd/security/cert/security_test.py
@@ -13,535 +13,19 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import logging
-import time
-
-from bluetooth_packets_python3 import hci_packets
-from cert.event_stream import EventStream
 from cert.gd_base_test import GdBaseTestClass
-from cert.py_security import PySecurity
-from cert.truth import assertThat
-from facade import common_pb2 as common
-from google.protobuf import empty_pb2 as empty_proto
-from hci.facade import controller_facade_pb2 as controller_facade
-from hci.facade import le_initiator_address_facade_pb2 as le_initiator_address_facade
-from l2cap.classic.facade_pb2 import ClassicSecurityPolicy
-from neighbor.facade import facade_pb2 as neighbor_facade
-from security.cert.cert_security import CertSecurity
-from security.facade_pb2 import AuthenticationRequirements
-from security.facade_pb2 import BondMsgType
-from security.facade_pb2 import IoCapabilities
-from security.facade_pb2 import OobDataPresent
-from security.facade_pb2 import UiMsgType
+from security.cert.security_test_lib import SecurityTestBase
 
 
-class SecurityTest(GdBaseTestClass):
-    """
-        Collection of tests that each sample results from 
-        different (unique) combinations of io capabilities, authentication requirements, and oob data.
-    """
-
-    _io_capabilities_name_lookup = {
-        IoCapabilities.DISPLAY_ONLY: "DISPLAY_ONLY",
-        IoCapabilities.DISPLAY_YES_NO_IO_CAP: "DISPLAY_YES_NO_IO_CAP",
-        #IoCapabilities.KEYBOARD_ONLY:"KEYBOARD_ONLY",
-        IoCapabilities.NO_INPUT_NO_OUTPUT: "NO_INPUT_NO_OUTPUT",
-    }
-
-    _auth_reqs_name_lookup = {
-        AuthenticationRequirements.NO_BONDING: "NO_BONDING",
-        AuthenticationRequirements.NO_BONDING_MITM_PROTECTION: "NO_BONDING_MITM_PROTECTION",
-        AuthenticationRequirements.DEDICATED_BONDING: "DEDICATED_BONDING",
-        AuthenticationRequirements.DEDICATED_BONDING_MITM_PROTECTION: "DEDICATED_BONDING_MITM_PROTECTION",
-        AuthenticationRequirements.GENERAL_BONDING: "GENERAL_BONDING",
-        AuthenticationRequirements.GENERAL_BONDING_MITM_PROTECTION: "GENERAL_BONDING_MITM_PROTECTION",
-    }
-
-    # Possible IO Capabilities
-    io_capabilities = (
-        IoCapabilities.DISPLAY_ONLY,
-        IoCapabilities.DISPLAY_YES_NO_IO_CAP,
-        # TODO(optedoblivion): Uncomment when Passkey Entry is implemented in ClassicPairingHandler
-        #IoCapabilities.KEYBOARD_ONLY,
-        IoCapabilities.NO_INPUT_NO_OUTPUT)
-
-    # Possible Authentication Requirements
-    auth_reqs = (AuthenticationRequirements.NO_BONDING, AuthenticationRequirements.NO_BONDING_MITM_PROTECTION,
-                 AuthenticationRequirements.DEDICATED_BONDING,
-                 AuthenticationRequirements.DEDICATED_BONDING_MITM_PROTECTION,
-                 AuthenticationRequirements.GENERAL_BONDING, AuthenticationRequirements.GENERAL_BONDING_MITM_PROTECTION)
-
-    # Possible Out-of-Band data options
-    oob_present = (
-        OobDataPresent.NOT_PRESENT,
-        # TODO(optedoblivion): Uncomment when OOB is implemented in root canal
-        #"P192_PRESENT",
-        #"P256_PRESENT",
-        #"P192_AND_256_PRESENT"
-    )
-
-    mitm_auth_reqs = (AuthenticationRequirements.DEDICATED_BONDING_MITM_PROTECTION,
-                      AuthenticationRequirements.GENERAL_BONDING_MITM_PROTECTION,
-                      AuthenticationRequirements.NO_BONDING_MITM_PROTECTION)
+class SecurityTest(GdBaseTestClass, SecurityTestBase):
 
     def setup_class(self):
-        super().setup_class(dut_module='SECURITY', cert_module='L2CAP')
+        GdBaseTestClass.setup_class(self, dut_module='SECURITY', cert_module='L2CAP')
 
     def setup_test(self):
-        super().setup_test()
-
-        self.dut.neighbor.EnablePageScan(neighbor_facade.EnableMsg(enabled=True))
-        self.cert.neighbor.EnablePageScan(neighbor_facade.EnableMsg(enabled=True))
-
-        self.dut.name = b'DUT Device'
-        self.dut.address = self.dut.hci_controller.GetMacAddress(empty_proto.Empty()).address
-        self.cert.name = b'Cert Device'
-        self.cert.address = self.cert.hci_controller.GetMacAddress(empty_proto.Empty()).address
-
-        # TODO(optedoblivion): Make this happen in PySecurity or GdDevice
-        self.dut.hci_controller.WriteLocalName(controller_facade.NameMsg(name=self.dut.name))
-        self.cert.hci_controller.WriteLocalName(controller_facade.NameMsg(name=self.cert.name))
-
-        self.dut_security = PySecurity(self.dut)
-        self.cert_security = CertSecurity(self.cert)
-
-        self.dut_address = common.BluetoothAddressWithType(
-            address=common.BluetoothAddress(address=bytes(b'DD:05:04:03:02:01')), type=common.RANDOM_DEVICE_ADDRESS)
-        privacy_policy = le_initiator_address_facade.PrivacyPolicy(
-            address_policy=le_initiator_address_facade.AddressPolicy.USE_STATIC_ADDRESS,
-            address_with_type=self.dut_address)
-        self.dut.security.SetLeInitiatorAddressPolicy(privacy_policy)
+        GdBaseTestClass.setup_test(self)
+        SecurityTestBase.setup_test(self, self.dut, self.cert)
 
     def teardown_test(self):
-        self.dut_security.close()
-        self.cert_security.close()
-        super().teardown_test()
-
-    # Initiates the numeric comparison test
-    def _run_ssp_numeric_comparison(self, initiator, responder, init_ui_response, resp_ui_response,
-                                    expected_init_ui_event, expected_resp_ui_event, expected_init_bond_event,
-                                    expected_resp_bond_event):
-        initiator.enable_secure_simple_pairing()
-        responder.enable_secure_simple_pairing()
-        initiator.create_bond(responder.get_address(), common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS)
-        self._verify_ssp_numeric_comparison(initiator, responder, init_ui_response, resp_ui_response,
-                                            expected_init_ui_event, expected_resp_ui_event, expected_init_bond_event,
-                                            expected_resp_bond_event)
-
-    # Verifies the events for the numeric comparion test
-    def _verify_ssp_numeric_comparison(self, initiator, responder, init_ui_response, resp_ui_response,
-                                       expected_init_ui_event, expected_resp_ui_event, expected_init_bond_event,
-                                       expected_resp_bond_event):
-        responder.accept_pairing(initiator.get_address(), resp_ui_response)
-        initiator.on_user_input(responder.get_address(), init_ui_response, expected_init_ui_event)
-        initiator.wait_for_bond_event(expected_init_bond_event)
-        responder.wait_for_bond_event(expected_resp_bond_event)
-
-    def _run_ssp_oob(self, initiator, responder, init_ui_response, resp_ui_response, expected_init_ui_event,
-                     expected_resp_ui_event, expected_init_bond_event, expected_resp_bond_event, p192_oob_data,
-                     p256_oob_data):
-        initiator.enable_secure_simple_pairing()
-        responder.enable_secure_simple_pairing()
-        initiator.create_bond_out_of_band(responder.get_address(),
-                                          common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS, p192_oob_data,
-                                          p256_oob_data)
-        self._verify_ssp_oob(initiator, responder, init_ui_response, resp_ui_response, expected_init_ui_event,
-                             expected_resp_ui_event, expected_init_bond_event, expected_resp_bond_event, p192_oob_data,
-                             p256_oob_data)
-
-    # Verifies the events for the numeric comparion test
-    def _verify_ssp_oob(self, initiator, responder, init_ui_response, resp_ui_response, expected_init_ui_event,
-                        expected_resp_ui_event, expected_init_bond_event, expected_resp_bond_event, p192_oob_data,
-                        p256_oob_data):
-        responder.accept_oob_pairing(initiator.get_address())
-        initiator.on_user_input(responder.get_address(), init_ui_response, expected_init_ui_event)
-        initiator.wait_for_bond_event(expected_init_bond_event)
-        responder.wait_for_bond_event(expected_resp_bond_event)
-
-    def _run_ssp_passkey(self, initiator, responder, expected_init_bond_event, expected_resp_bond_event):
-        initiator.enable_secure_simple_pairing()
-        responder.enable_secure_simple_pairing()
-        initiator.create_bond(responder.get_address(), common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS)
-        self._verify_ssp_passkey(initiator, responder, expected_init_bond_event, expected_resp_bond_event)
-
-    def _verify_ssp_passkey(self, initiator, responder, expected_init_bond_event, expected_resp_bond_event):
-        responder.send_io_caps(initiator.get_address())
-        passkey = initiator.wait_for_passkey(responder.get_address())
-        responder.input_passkey(initiator.get_address(), passkey)
-        initiator.wait_for_bond_event(expected_init_bond_event)
-        responder.wait_for_bond_event(expected_resp_bond_event)
-
-    def _run_pin(self, initiator, responder, expected_init_bond_event, expected_resp_bond_event):
-        initiator.create_bond(responder.get_address(), common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS)
-        self._verify_pin(initiator, responder, expected_init_bond_event, expected_resp_bond_event)
-
-    def _verify_pin(self, initiator, responder, expected_init_bond_event, expected_resp_bond_event):
-        pin = b'123456789A'
-        logging.info("pin: %s" % pin)
-        initiator.input_pin(responder.get_address(), pin)
-        responder.input_pin(initiator.get_address(), pin)
-        initiator.wait_for_bond_event(expected_init_bond_event)
-        responder.wait_for_bond_event(expected_resp_bond_event)
-
-    def test_setup_teardown(self):
-        """
-            Make sure our setup and teardown is sane
-        """
-        pass
-
-    # no_input_no_output + no_input_no_output is JustWorks no confirmation
-    def test_dut_initiated_no_input_no_output_no_input_no_output_twice_bond_and_enforce(self):
-        # Arrange
-        self.dut_security.set_io_capabilities(IoCapabilities.NO_INPUT_NO_OUTPUT)
-        self.dut_security.set_authentication_requirements(AuthenticationRequirements.DEDICATED_BONDING)
-        self.cert_security.set_io_capabilities(IoCapabilities.NO_INPUT_NO_OUTPUT)
-        self.cert_security.set_authentication_requirements(AuthenticationRequirements.DEDICATED_BONDING)
-
-        # Act and Assert
-        self._run_ssp_numeric_comparison(
-            initiator=self.dut_security,
-            responder=self.cert_security,
-            init_ui_response=True,
-            resp_ui_response=True,
-            expected_init_ui_event=None,
-            expected_resp_ui_event=None,
-            expected_init_bond_event=BondMsgType.DEVICE_BONDED,
-            expected_resp_bond_event=None)
-
-        self.dut_security.enforce_security_policy(self.cert.address,
-                                                  common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS,
-                                                  ClassicSecurityPolicy.ENCRYPTED_TRANSPORT)
-
-        # TODO: We verify enforcement when we make sure EncryptionChange is received on DUT
-
-    # no_input_no_output + no_input_no_output is JustWorks no confirmation
-    def test_dut_initiated_no_input_no_output_no_input_no_output_twice_with_remove_bond(self):
-        # Arrange
-        self.dut_security.set_io_capabilities(IoCapabilities.NO_INPUT_NO_OUTPUT)
-        self.dut_security.set_authentication_requirements(AuthenticationRequirements.DEDICATED_BONDING)
-        self.cert_security.set_io_capabilities(IoCapabilities.NO_INPUT_NO_OUTPUT)
-        self.cert_security.set_authentication_requirements(AuthenticationRequirements.DEDICATED_BONDING)
-
-        # Act and Assert
-        self._run_ssp_numeric_comparison(
-            initiator=self.dut_security,
-            responder=self.cert_security,
-            init_ui_response=True,
-            resp_ui_response=True,
-            expected_init_ui_event=None,
-            expected_resp_ui_event=None,
-            expected_init_bond_event=BondMsgType.DEVICE_BONDED,
-            expected_resp_bond_event=None)
-
-        self.dut_security.remove_bond(self.cert.address, common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS)
-        self.cert_security.remove_bond(self.cert.address, common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS)
-        self.dut_security.wait_for_bond_event(BondMsgType.DEVICE_UNBONDED)
-        self.cert_security.wait_for_bond_event(BondMsgType.DEVICE_UNBONDED)
-
-        self.dut_security.wait_for_disconnect_event()
-        self.cert_security.wait_for_disconnect_event()
-
-        # Act and Assert
-        self._run_ssp_numeric_comparison(
-            initiator=self.dut_security,
-            responder=self.cert_security,
-            init_ui_response=True,
-            resp_ui_response=True,
-            expected_init_ui_event=None,
-            expected_resp_ui_event=None,
-            expected_init_bond_event=BondMsgType.DEVICE_BONDED,
-            expected_resp_bond_event=None)
-
-        self.dut_security.remove_bond(self.cert.address, common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS)
-        self.cert_security.remove_bond(self.cert.address, common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS)
-        self.dut_security.wait_for_bond_event(BondMsgType.DEVICE_UNBONDED)
-        self.cert_security.wait_for_bond_event(BondMsgType.DEVICE_UNBONDED)
-
-        self.dut_security.wait_for_disconnect_event()
-        self.cert_security.wait_for_disconnect_event()
-
-    def test_successful_dut_initiated_ssp_numeric_comparison(self):
-        test_count = len(self.io_capabilities) * len(self.auth_reqs) * len(self.oob_present) * len(
-            self.io_capabilities) * len(self.auth_reqs) * len(self.oob_present)
-        logging.info("Loading %d test combinations" % test_count)
-        i = 0
-        for dut_io_capability in self.io_capabilities:
-            for dut_auth_reqs in self.auth_reqs:
-                for dut_oob_present in self.oob_present:
-                    for cert_io_capability in self.io_capabilities:
-                        for cert_auth_reqs in self.auth_reqs:
-                            for cert_oob_present in self.oob_present:
-                                i = i + 1
-                                logging.info("")
-                                logging.info("===================================================")
-                                logging.info("Running test %d of %d" % (i, test_count))
-                                logging.info("DUT Test Config: %s ; %s ; %s " % (self._io_capabilities_name_lookup.get(
-                                    dut_io_capability, "ERROR"), self._auth_reqs_name_lookup.get(
-                                        dut_auth_reqs, "ERROR"), dut_oob_present))
-                                logging.info(
-                                    "CERT Test Config: %s ; %s ; %s " %
-                                    (self._io_capabilities_name_lookup.get(cert_io_capability, "ERROR"),
-                                     self._auth_reqs_name_lookup.get(cert_auth_reqs, "ERROR"), cert_oob_present))
-                                logging.info("===================================================")
-                                logging.info("")
-                                self.dut_security.set_io_capabilities(dut_io_capability)
-                                self.dut_security.set_authentication_requirements(dut_auth_reqs)
-                                self.cert_security.set_io_capabilities(cert_io_capability)
-                                self.cert_security.set_authentication_requirements(cert_auth_reqs)
-                                init_ui_response = True
-                                resp_ui_response = True
-                                expected_init_ui_event = None  # None is auto accept
-                                expected_resp_ui_event = None  # None is auto accept
-                                expected_init_bond_event = BondMsgType.DEVICE_BONDED
-                                expected_resp_bond_event = None
-                                if dut_io_capability == IoCapabilities.DISPLAY_ONLY:
-                                    if cert_io_capability == IoCapabilities.DISPLAY_YES_NO_IO_CAP:
-                                        expected_resp_ui_event = UiMsgType.DISPLAY_YES_NO_WITH_VALUE
-                                        if dut_auth_reqs in self.mitm_auth_reqs or cert_auth_reqs in self.mitm_auth_reqs:
-                                            expected_init_bond_event = BondMsgType.DEVICE_BOND_FAILED
-                                    elif cert_io_capability == IoCapabilities.KEYBOARD_ONLY:
-                                        expected_resp_ui_event = UiMsgType.DISPLAY_PASSKEY_ENTRY
-                                    elif cert_io_capability == IoCapabilities.DISPLAY_ONLY:
-                                        if dut_auth_reqs in self.mitm_auth_reqs or cert_auth_reqs in self.mitm_auth_reqs:
-                                            expected_init_bond_event = BondMsgType.DEVICE_BOND_FAILED
-                                    elif cert_io_capability == IoCapabilities.NO_INPUT_NO_OUTPUT:
-                                        if dut_auth_reqs in self.mitm_auth_reqs or cert_auth_reqs in self.mitm_auth_reqs:
-                                            expected_init_bond_event = BondMsgType.DEVICE_BOND_FAILED
-                                elif dut_io_capability == IoCapabilities.DISPLAY_YES_NO_IO_CAP:
-                                    expected_init_ui_event = UiMsgType.DISPLAY_YES_NO_WITH_VALUE
-                                    if cert_io_capability == IoCapabilities.DISPLAY_YES_NO_IO_CAP:
-                                        expected_resp_ui_event = UiMsgType.DISPLAY_YES_NO_WITH_VALUE
-                                    elif cert_io_capability == IoCapabilities.KEYBOARD_ONLY:
-                                        expected_init_ui_event = UiMsgType.DISPLAY_PASSKEY
-                                        expected_resp_ui_event = UiMsgType.DISPLAY_PASSKEY_ENTRY
-                                    elif cert_io_capability == IoCapabilities.NO_INPUT_NO_OUTPUT:
-                                        expected_init_ui_event = UiMsgType.DISPLAY_YES_NO  # No value
-                                elif dut_io_capability == IoCapabilities.KEYBOARD_ONLY:
-                                    expected_init_ui_event = UiMsgType.DISPLAY_PASSKEY_ENTRY
-                                    if cert_io_capability == IoCapabilities.DISPLAY_ONLY:
-                                        expected_resp_ui_event = UiMsgType.DISPLAY_PASSKEY
-                                    elif cert_io_capability == IoCapabilities.DISPLAY_YES_NO_IO_CAP:
-                                        expected_resp_ui_event = UiMsgType.DISPLAY_PASSKEY_ENTRY
-                                    elif cert_io_capability == IoCapabilities.KEYBOARD_ONLY:
-                                        expected_resp_ui_event = UiMsgType.DISPLAY_PASSKEY_ENTRY
-                                    elif cert_io_capability == IoCapabilities.NO_INPUT_NO_OUTPUT:
-                                        if dut_auth_reqs in self.mitm_auth_reqs or cert_auth_reqs in self.mitm_auth_reqs:
-                                            expected_init_bond_event = BondMsgType.DEVICE_BOND_FAILED
-                                elif dut_io_capability == IoCapabilities.NO_INPUT_NO_OUTPUT:
-                                    if cert_io_capability == IoCapabilities.DISPLAY_YES_NO_IO_CAP:
-                                        expected_resp_ui_event = UiMsgType.DISPLAY_YES_NO  # No value
-
-                                    if dut_auth_reqs in self.mitm_auth_reqs or cert_auth_reqs in self.mitm_auth_reqs:
-                                        expected_init_bond_event = BondMsgType.DEVICE_BOND_FAILED
-
-                                if cert_oob_present == OobDataPresent.NOT_PRESENT:
-                                    self._run_ssp_numeric_comparison(
-                                        initiator=self.dut_security,
-                                        responder=self.cert_security,
-                                        init_ui_response=init_ui_response,
-                                        resp_ui_response=resp_ui_response,
-                                        expected_init_ui_event=expected_init_ui_event,
-                                        expected_resp_ui_event=expected_resp_ui_event,
-                                        expected_init_bond_event=expected_init_bond_event,
-                                        expected_resp_bond_event=expected_resp_bond_event)
-                                else:
-                                    logging.error("Code path not yet implemented")
-                                    assertThat(False).isTrue()
-
-                                self.dut_security.remove_bond(self.cert_security.get_address(),
-                                                              common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS)
-                                self.cert_security.remove_bond(self.dut_security.get_address(),
-                                                               common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS)
-
-                                self.dut_security.wait_for_bond_event(BondMsgType.DEVICE_UNBONDED)
-                                self.cert_security.wait_for_bond_event(BondMsgType.DEVICE_UNBONDED)
-
-                                self.dut_security.wait_for_disconnect_event()
-                                self.cert_security.wait_for_disconnect_event()
-
-    def test_enable_secure_simple_pairing(self):
-        self.dut_security.enable_secure_simple_pairing()
-        self.cert_security.enable_secure_simple_pairing()
-
-    def test_enable_secure_connections(self):
-        self.dut_security.enable_secure_simple_pairing()
-        self.cert_security.enable_secure_simple_pairing()
-        self.dut_security.enable_secure_connections()
-        self.cert_security.enable_secure_connections()
-
-    def test_get_oob_data_from_dut_controller_p192_present(self):
-        oob_data = self.dut_security.get_oob_data_from_controller(OobDataPresent.P192_PRESENT)
-        assertThat(len(oob_data)).isEqualTo(4)
-        has192C = not all([i == 0 for i in oob_data[0]])
-        has192R = not all([i == 0 for i in oob_data[1]])
-        has256C = not all([i == 0 for i in oob_data[2]])
-        has256R = not all([i == 0 for i in oob_data[3]])
-        assertThat(has192C).isTrue()
-        assertThat(has192R).isTrue()
-        assertThat(has256C).isFalse()
-        assertThat(has256R).isFalse()
-
-    def test_get_oob_data_from_cert_controller_not_present(self):
-        oob_data = self.cert_security.get_oob_data_from_controller(OobDataPresent.NOT_PRESENT)
-        assertThat(len(oob_data)).isEqualTo(4)
-        has192C = not all([i == 0 for i in oob_data[0]])
-        has192R = not all([i == 0 for i in oob_data[1]])
-        has256C = not all([i == 0 for i in oob_data[2]])
-        has256R = not all([i == 0 for i in oob_data[3]])
-        assertThat(has192C).isFalse()
-        assertThat(has192R).isFalse()
-        assertThat(has256C).isFalse()
-        assertThat(has256R).isFalse()
-
-    def test_get_oob_data_from_cert_controller_p192_present_no_secure_connections(self):
-        oob_data = self.cert_security.get_oob_data_from_controller(OobDataPresent.P192_PRESENT)
-        assertThat(len(oob_data)).isEqualTo(4)
-        has192C = not all([i == 0 for i in oob_data[0]])
-        has192R = not all([i == 0 for i in oob_data[1]])
-        has256C = not all([i == 0 for i in oob_data[2]])
-        has256R = not all([i == 0 for i in oob_data[3]])
-        assertThat(has192C).isTrue()
-        assertThat(has192R).isTrue()
-        assertThat(has256C).isFalse()
-        assertThat(has256R).isFalse()
-
-    def test_get_oob_data_from_cert_controller_p192_present(self):
-        self.cert_security.enable_secure_simple_pairing()
-        self.cert_security.enable_secure_connections()
-        oob_data = self.cert_security.get_oob_data_from_controller(OobDataPresent.P192_PRESENT)
-        assertThat(len(oob_data)).isEqualTo(4)
-        has192C = not all([i == 0 for i in oob_data[0]])
-        has192R = not all([i == 0 for i in oob_data[1]])
-        has256C = not all([i == 0 for i in oob_data[2]])
-        has256R = not all([i == 0 for i in oob_data[3]])
-        assertThat(has192C).isTrue()
-        assertThat(has192R).isTrue()
-        assertThat(has256C).isFalse()
-        assertThat(has256R).isFalse()
-
-    def test_get_oob_data_from_cert_controller_p256_present(self):
-        self.cert_security.enable_secure_simple_pairing()
-        self.cert_security.enable_secure_connections()
-        oob_data = self.cert_security.get_oob_data_from_controller(OobDataPresent.P256_PRESENT)
-        assertThat(len(oob_data)).isEqualTo(4)
-        has192C = not all([i == 0 for i in oob_data[0]])
-        has192R = not all([i == 0 for i in oob_data[1]])
-        has256C = not all([i == 0 for i in oob_data[2]])
-        has256R = not all([i == 0 for i in oob_data[3]])
-        assertThat(has192C).isFalse()
-        assertThat(has192R).isFalse()
-        assertThat(has256C).isTrue()
-        assertThat(has256R).isTrue()
-
-    def test_get_oob_data_from_cert_controller_p192_and_p256_present(self):
-        self.cert_security.enable_secure_simple_pairing()
-        self.cert_security.enable_secure_connections()
-        oob_data = self.cert_security.get_oob_data_from_controller(OobDataPresent.P192_AND_256_PRESENT)
-        assertThat(len(oob_data)).isEqualTo(4)
-        has192C = not all([i == 0 for i in oob_data[0]])
-        has192R = not all([i == 0 for i in oob_data[1]])
-        has256C = not all([i == 0 for i in oob_data[2]])
-        has256R = not all([i == 0 for i in oob_data[3]])
-        assertThat(has192C).isTrue()
-        assertThat(has192R).isTrue()
-        assertThat(has256C).isTrue()
-        assertThat(has256R).isTrue()
-
-    def test_successful_dut_initiated_ssp_oob(self):
-        dut_io_capability = IoCapabilities.NO_INPUT_NO_OUTPUT
-        cert_io_capability = IoCapabilities.NO_INPUT_NO_OUTPUT
-        dut_auth_reqs = AuthenticationRequirements.DEDICATED_BONDING_MITM_PROTECTION
-        cert_auth_reqs = AuthenticationRequirements.DEDICATED_BONDING_MITM_PROTECTION
-        cert_oob_present = OobDataPresent.P192_PRESENT
-        self.dut_security.enable_secure_simple_pairing()
-        self.dut_security.enable_secure_connections()
-        self.cert_security.enable_secure_simple_pairing()
-        self.cert_security.enable_secure_connections()
-        self.dut_security.set_io_capabilities(dut_io_capability)
-        self.dut_security.set_authentication_requirements(dut_auth_reqs)
-        self.cert_security.set_io_capabilities(cert_io_capability)
-        self.cert_security.set_authentication_requirements(cert_auth_reqs)
-        init_ui_response = True
-        resp_ui_response = True
-        expected_init_ui_event = None  # None is auto accept
-        expected_resp_ui_event = None  # None is auto accept
-        expected_init_bond_event = BondMsgType.DEVICE_BONDED
-        expected_resp_bond_event = None
-        # get_oob_data returns a tuple of bytes (p192c,p192r,p256c,p256r)
-        local_oob_data = self.cert_security.get_oob_data_from_controller(cert_oob_present)
-        p192_oob_data = local_oob_data[0:2]
-        p256_oob_data = local_oob_data[2:4]
-        self._run_ssp_oob(
-            initiator=self.dut_security,
-            responder=self.cert_security,
-            init_ui_response=init_ui_response,
-            resp_ui_response=resp_ui_response,
-            expected_init_ui_event=expected_init_ui_event,
-            expected_resp_ui_event=expected_resp_ui_event,
-            expected_init_bond_event=expected_init_bond_event,
-            expected_resp_bond_event=expected_resp_bond_event,
-            p192_oob_data=p192_oob_data,
-            p256_oob_data=p256_oob_data)
-        self.dut_security.remove_bond(self.cert_security.get_address(),
-                                      common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS)
-        self.cert_security.remove_bond(self.dut_security.get_address(),
-                                       common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS)
-        self.dut_security.wait_for_bond_event(BondMsgType.DEVICE_UNBONDED)
-        self.cert_security.wait_for_bond_event(BondMsgType.DEVICE_UNBONDED)
-        self.dut_security.wait_for_disconnect_event()
-        self.cert_security.wait_for_disconnect_event()
-
-    def test_successful_dut_initiated_ssp_keyboard(self):
-        dut_io_capability = IoCapabilities.DISPLAY_YES_NO_IO_CAP
-        dut_auth_reqs = AuthenticationRequirements.DEDICATED_BONDING_MITM_PROTECTION
-        dut_oob_present = OobDataPresent.NOT_PRESENT
-        cert_io_capability = IoCapabilities.KEYBOARD_ONLY
-        cert_auth_reqs = AuthenticationRequirements.DEDICATED_BONDING_MITM_PROTECTION
-        cert_oob_present = OobDataPresent.NOT_PRESENT
-        self.dut_security.set_io_capabilities(dut_io_capability)
-        self.dut_security.set_authentication_requirements(dut_auth_reqs)
-        self.cert_security.set_io_capabilities(cert_io_capability)
-        self.cert_security.set_authentication_requirements(cert_auth_reqs)
-
-        self._run_ssp_passkey(
-            initiator=self.dut_security,
-            responder=self.cert_security,
-            expected_init_bond_event=BondMsgType.DEVICE_BONDED,
-            expected_resp_bond_event=BondMsgType.DEVICE_BONDED)
-
-        self.dut_security.remove_bond(self.cert_security.get_address(),
-                                      common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS)
-        self.cert_security.remove_bond(self.dut_security.get_address(),
-                                       common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS)
-
-        self.dut_security.wait_for_bond_event(BondMsgType.DEVICE_UNBONDED)
-        self.cert_security.wait_for_bond_event(BondMsgType.DEVICE_UNBONDED)
-
-        self.dut_security.wait_for_disconnect_event()
-        self.cert_security.wait_for_disconnect_event()
-
-    def test_successful_dut_initiated_pin(self):
-        self.dut_security.set_io_capabilities(IoCapabilities.DISPLAY_YES_NO_IO_CAP)
-        self.dut_security.set_authentication_requirements(AuthenticationRequirements.DEDICATED_BONDING)
-
-        self._run_pin(
-            initiator=self.dut_security,
-            responder=self.cert_security,
-            expected_init_bond_event=BondMsgType.DEVICE_BONDED,
-            expected_resp_bond_event=BondMsgType.DEVICE_BONDED)
-
-        self.dut_security.remove_bond(self.cert_security.get_address(),
-                                      common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS)
-        self.cert_security.remove_bond(self.dut_security.get_address(),
-                                       common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS)
-
-        self.dut_security.wait_for_bond_event(BondMsgType.DEVICE_UNBONDED)
-        self.cert_security.wait_for_bond_event(BondMsgType.DEVICE_UNBONDED)
-
-        self.dut_security.wait_for_disconnect_event()
-        self.cert_security.wait_for_disconnect_event()
-
-    def test_make_sure_oob_data_different(self):
-        oob_data = self.dut_security.get_oob_data_from_controller(OobDataPresent.P192_PRESENT)
-        oob_data2 = self.dut_security.get_oob_data_from_controller(OobDataPresent.P192_PRESENT)
-        assertThat(oob_data).isNotEqualTo(oob_data2)
+        SecurityTestBase.teardown_test(self)
+        GdBaseTestClass.teardown_test(self)
diff --git a/gd/security/cert/security_test_lib.py b/gd/security/cert/security_test_lib.py
new file mode 100644
index 0000000..c9ed926
--- /dev/null
+++ b/gd/security/cert/security_test_lib.py
@@ -0,0 +1,543 @@
+#
+#   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.
+
+import logging
+import time
+
+from bluetooth_packets_python3 import hci_packets
+from cert.event_stream import EventStream
+from cert.py_security import PySecurity
+from cert.truth import assertThat
+from facade import common_pb2 as common
+from google.protobuf import empty_pb2 as empty_proto
+from hci.facade import controller_facade_pb2 as controller_facade
+from hci.facade import le_initiator_address_facade_pb2 as le_initiator_address_facade
+from l2cap.classic.facade_pb2 import ClassicSecurityPolicy
+from neighbor.facade import facade_pb2 as neighbor_facade
+from security.cert.cert_security import CertSecurity
+from security.facade_pb2 import AuthenticationRequirements
+from security.facade_pb2 import BondMsgType
+from security.facade_pb2 import IoCapabilities
+from security.facade_pb2 import OobDataPresent
+from security.facade_pb2 import UiMsgType
+
+
+class SecurityTestBase():
+    """
+        Collection of tests that each sample results from 
+        different (unique) combinations of io capabilities, authentication requirements, and oob data.
+    """
+
+    _io_capabilities_name_lookup = {
+        IoCapabilities.DISPLAY_ONLY: "DISPLAY_ONLY",
+        IoCapabilities.DISPLAY_YES_NO_IO_CAP: "DISPLAY_YES_NO_IO_CAP",
+        #IoCapabilities.KEYBOARD_ONLY:"KEYBOARD_ONLY",
+        IoCapabilities.NO_INPUT_NO_OUTPUT: "NO_INPUT_NO_OUTPUT",
+    }
+
+    _auth_reqs_name_lookup = {
+        AuthenticationRequirements.NO_BONDING: "NO_BONDING",
+        AuthenticationRequirements.NO_BONDING_MITM_PROTECTION: "NO_BONDING_MITM_PROTECTION",
+        AuthenticationRequirements.DEDICATED_BONDING: "DEDICATED_BONDING",
+        AuthenticationRequirements.DEDICATED_BONDING_MITM_PROTECTION: "DEDICATED_BONDING_MITM_PROTECTION",
+        AuthenticationRequirements.GENERAL_BONDING: "GENERAL_BONDING",
+        AuthenticationRequirements.GENERAL_BONDING_MITM_PROTECTION: "GENERAL_BONDING_MITM_PROTECTION",
+    }
+
+    # Possible IO Capabilities
+    io_capabilities = (
+        IoCapabilities.DISPLAY_ONLY,
+        IoCapabilities.DISPLAY_YES_NO_IO_CAP,
+        # TODO(optedoblivion): Uncomment when Passkey Entry is implemented in ClassicPairingHandler
+        #IoCapabilities.KEYBOARD_ONLY,
+        IoCapabilities.NO_INPUT_NO_OUTPUT)
+
+    # Possible Authentication Requirements
+    auth_reqs = (AuthenticationRequirements.NO_BONDING, AuthenticationRequirements.NO_BONDING_MITM_PROTECTION,
+                 AuthenticationRequirements.DEDICATED_BONDING,
+                 AuthenticationRequirements.DEDICATED_BONDING_MITM_PROTECTION,
+                 AuthenticationRequirements.GENERAL_BONDING, AuthenticationRequirements.GENERAL_BONDING_MITM_PROTECTION)
+
+    # Possible Out-of-Band data options
+    oob_present = (
+        OobDataPresent.NOT_PRESENT,
+        # TODO(optedoblivion): Uncomment when OOB is implemented in root canal
+        #"P192_PRESENT",
+        #"P256_PRESENT",
+        #"P192_AND_256_PRESENT"
+    )
+
+    mitm_auth_reqs = (AuthenticationRequirements.DEDICATED_BONDING_MITM_PROTECTION,
+                      AuthenticationRequirements.GENERAL_BONDING_MITM_PROTECTION,
+                      AuthenticationRequirements.NO_BONDING_MITM_PROTECTION)
+
+    def setup_test(self, dut, cert):
+        self.dut = dut
+        self.cert = cert
+
+        self.dut.neighbor.EnablePageScan(neighbor_facade.EnableMsg(enabled=True))
+        self.cert.neighbor.EnablePageScan(neighbor_facade.EnableMsg(enabled=True))
+
+        self.dut.name = b'DUT Device'
+        self.dut.address = self.dut.hci_controller.GetMacAddress(empty_proto.Empty()).address
+        self.cert.name = b'Cert Device'
+        self.cert.address = self.cert.hci_controller.GetMacAddress(empty_proto.Empty()).address
+
+        # TODO(optedoblivion): Make this happen in PySecurity or GdDevice
+        self.dut.hci_controller.WriteLocalName(controller_facade.NameMsg(name=self.dut.name))
+        self.cert.hci_controller.WriteLocalName(controller_facade.NameMsg(name=self.cert.name))
+
+        self.dut_security = PySecurity(self.dut)
+        self.cert_security = CertSecurity(self.cert)
+
+        self.dut_address = common.BluetoothAddressWithType(
+            address=common.BluetoothAddress(address=bytes(b'DD:05:04:03:02:01')), type=common.RANDOM_DEVICE_ADDRESS)
+        privacy_policy = le_initiator_address_facade.PrivacyPolicy(
+            address_policy=le_initiator_address_facade.AddressPolicy.USE_STATIC_ADDRESS,
+            address_with_type=self.dut_address)
+        self.dut.security.SetLeInitiatorAddressPolicy(privacy_policy)
+
+    def teardown_test(self):
+        self.dut_security.close()
+        self.cert_security.close()
+
+    # Initiates the numeric comparison test
+    def _run_ssp_numeric_comparison(self, initiator, responder, init_ui_response, resp_ui_response,
+                                    expected_init_ui_event, expected_resp_ui_event, expected_init_bond_event,
+                                    expected_resp_bond_event):
+        initiator.enable_secure_simple_pairing()
+        responder.enable_secure_simple_pairing()
+        initiator.create_bond(responder.get_address(), common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS)
+        self._verify_ssp_numeric_comparison(initiator, responder, init_ui_response, resp_ui_response,
+                                            expected_init_ui_event, expected_resp_ui_event, expected_init_bond_event,
+                                            expected_resp_bond_event)
+
+    # Verifies the events for the numeric comparion test
+    def _verify_ssp_numeric_comparison(self, initiator, responder, init_ui_response, resp_ui_response,
+                                       expected_init_ui_event, expected_resp_ui_event, expected_init_bond_event,
+                                       expected_resp_bond_event):
+        responder.accept_pairing(initiator.get_address(), resp_ui_response)
+        initiator.on_user_input(responder.get_address(), init_ui_response, expected_init_ui_event)
+        initiator.wait_for_bond_event(expected_init_bond_event)
+        responder.wait_for_bond_event(expected_resp_bond_event)
+
+    def _run_ssp_oob(self, initiator, responder, init_ui_response, resp_ui_response, expected_init_ui_event,
+                     expected_resp_ui_event, expected_init_bond_event, expected_resp_bond_event, p192_oob_data,
+                     p256_oob_data):
+        initiator.enable_secure_simple_pairing()
+        responder.enable_secure_simple_pairing()
+        initiator.create_bond_out_of_band(responder.get_address(),
+                                          common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS, p192_oob_data,
+                                          p256_oob_data)
+        self._verify_ssp_oob(initiator, responder, init_ui_response, resp_ui_response, expected_init_ui_event,
+                             expected_resp_ui_event, expected_init_bond_event, expected_resp_bond_event, p192_oob_data,
+                             p256_oob_data)
+
+    # Verifies the events for the numeric comparion test
+    def _verify_ssp_oob(self, initiator, responder, init_ui_response, resp_ui_response, expected_init_ui_event,
+                        expected_resp_ui_event, expected_init_bond_event, expected_resp_bond_event, p192_oob_data,
+                        p256_oob_data):
+        responder.accept_oob_pairing(initiator.get_address())
+        initiator.on_user_input(responder.get_address(), init_ui_response, expected_init_ui_event)
+        initiator.wait_for_bond_event(expected_init_bond_event)
+        responder.wait_for_bond_event(expected_resp_bond_event)
+
+    def _run_ssp_passkey(self, initiator, responder, expected_init_bond_event, expected_resp_bond_event):
+        initiator.enable_secure_simple_pairing()
+        responder.enable_secure_simple_pairing()
+        initiator.create_bond(responder.get_address(), common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS)
+        self._verify_ssp_passkey(initiator, responder, expected_init_bond_event, expected_resp_bond_event)
+
+    def _verify_ssp_passkey(self, initiator, responder, expected_init_bond_event, expected_resp_bond_event):
+        responder.send_io_caps(initiator.get_address())
+        passkey = initiator.wait_for_passkey(responder.get_address())
+        responder.input_passkey(initiator.get_address(), passkey)
+        initiator.wait_for_bond_event(expected_init_bond_event)
+        responder.wait_for_bond_event(expected_resp_bond_event)
+
+    def _run_pin(self, initiator, responder, expected_init_bond_event, expected_resp_bond_event):
+        initiator.create_bond(responder.get_address(), common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS)
+        self._verify_pin(initiator, responder, expected_init_bond_event, expected_resp_bond_event)
+
+    def _verify_pin(self, initiator, responder, expected_init_bond_event, expected_resp_bond_event):
+        pin = b'123456789A'
+        logging.info("pin: %s" % pin)
+        initiator.input_pin(responder.get_address(), pin)
+        responder.input_pin(initiator.get_address(), pin)
+        initiator.wait_for_bond_event(expected_init_bond_event)
+        responder.wait_for_bond_event(expected_resp_bond_event)
+
+    def test_setup_teardown(self):
+        """
+            Make sure our setup and teardown is sane
+        """
+        pass
+
+    # no_input_no_output + no_input_no_output is JustWorks no confirmation
+    def test_dut_initiated_no_input_no_output_no_input_no_output_twice_bond_and_enforce(self):
+        # Arrange
+        self.dut_security.set_io_capabilities(IoCapabilities.NO_INPUT_NO_OUTPUT)
+        self.dut_security.set_authentication_requirements(AuthenticationRequirements.DEDICATED_BONDING)
+        self.cert_security.set_io_capabilities(IoCapabilities.NO_INPUT_NO_OUTPUT)
+        self.cert_security.set_authentication_requirements(AuthenticationRequirements.DEDICATED_BONDING)
+
+        # Act and Assert
+        self._run_ssp_numeric_comparison(
+            initiator=self.dut_security,
+            responder=self.cert_security,
+            init_ui_response=True,
+            resp_ui_response=True,
+            expected_init_ui_event=None,
+            expected_resp_ui_event=None,
+            expected_init_bond_event=BondMsgType.DEVICE_BONDED,
+            expected_resp_bond_event=None)
+
+        self.dut_security.enforce_security_policy(self.cert.address,
+                                                  common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS,
+                                                  ClassicSecurityPolicy.ENCRYPTED_TRANSPORT)
+
+        # TODO: We verify enforcement when we make sure EncryptionChange is received on DUT
+
+    # no_input_no_output + no_input_no_output is JustWorks no confirmation
+    def test_dut_initiated_no_input_no_output_no_input_no_output_twice_with_remove_bond(self):
+        # Arrange
+        self.dut_security.set_io_capabilities(IoCapabilities.NO_INPUT_NO_OUTPUT)
+        self.dut_security.set_authentication_requirements(AuthenticationRequirements.DEDICATED_BONDING)
+        self.cert_security.set_io_capabilities(IoCapabilities.NO_INPUT_NO_OUTPUT)
+        self.cert_security.set_authentication_requirements(AuthenticationRequirements.DEDICATED_BONDING)
+
+        # Act and Assert
+        self._run_ssp_numeric_comparison(
+            initiator=self.dut_security,
+            responder=self.cert_security,
+            init_ui_response=True,
+            resp_ui_response=True,
+            expected_init_ui_event=None,
+            expected_resp_ui_event=None,
+            expected_init_bond_event=BondMsgType.DEVICE_BONDED,
+            expected_resp_bond_event=None)
+
+        self.dut_security.remove_bond(self.cert.address, common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS)
+        self.cert_security.remove_bond(self.cert.address, common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS)
+        self.dut_security.wait_for_bond_event(BondMsgType.DEVICE_UNBONDED)
+        self.cert_security.wait_for_bond_event(BondMsgType.DEVICE_UNBONDED)
+
+        self.dut_security.wait_for_disconnect_event()
+        self.cert_security.wait_for_disconnect_event()
+
+        # Act and Assert
+        self._run_ssp_numeric_comparison(
+            initiator=self.dut_security,
+            responder=self.cert_security,
+            init_ui_response=True,
+            resp_ui_response=True,
+            expected_init_ui_event=None,
+            expected_resp_ui_event=None,
+            expected_init_bond_event=BondMsgType.DEVICE_BONDED,
+            expected_resp_bond_event=None)
+
+        self.dut_security.remove_bond(self.cert.address, common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS)
+        self.cert_security.remove_bond(self.cert.address, common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS)
+        self.dut_security.wait_for_bond_event(BondMsgType.DEVICE_UNBONDED)
+        self.cert_security.wait_for_bond_event(BondMsgType.DEVICE_UNBONDED)
+
+        self.dut_security.wait_for_disconnect_event()
+        self.cert_security.wait_for_disconnect_event()
+
+    def test_successful_dut_initiated_ssp_numeric_comparison(self):
+        test_count = len(self.io_capabilities) * len(self.auth_reqs) * len(self.oob_present) * len(
+            self.io_capabilities) * len(self.auth_reqs) * len(self.oob_present)
+        logging.info("Loading %d test combinations" % test_count)
+        i = 0
+        for dut_io_capability in self.io_capabilities:
+            for dut_auth_reqs in self.auth_reqs:
+                for dut_oob_present in self.oob_present:
+                    for cert_io_capability in self.io_capabilities:
+                        for cert_auth_reqs in self.auth_reqs:
+                            for cert_oob_present in self.oob_present:
+                                i = i + 1
+                                logging.info("")
+                                logging.info("===================================================")
+                                logging.info("Running test %d of %d" % (i, test_count))
+                                logging.info("DUT Test Config: %s ; %s ; %s " % (self._io_capabilities_name_lookup.get(
+                                    dut_io_capability, "ERROR"), self._auth_reqs_name_lookup.get(
+                                        dut_auth_reqs, "ERROR"), dut_oob_present))
+                                logging.info(
+                                    "CERT Test Config: %s ; %s ; %s " %
+                                    (self._io_capabilities_name_lookup.get(cert_io_capability, "ERROR"),
+                                     self._auth_reqs_name_lookup.get(cert_auth_reqs, "ERROR"), cert_oob_present))
+                                logging.info("===================================================")
+                                logging.info("")
+                                self.dut_security.set_io_capabilities(dut_io_capability)
+                                self.dut_security.set_authentication_requirements(dut_auth_reqs)
+                                self.cert_security.set_io_capabilities(cert_io_capability)
+                                self.cert_security.set_authentication_requirements(cert_auth_reqs)
+                                init_ui_response = True
+                                resp_ui_response = True
+                                expected_init_ui_event = None  # None is auto accept
+                                expected_resp_ui_event = None  # None is auto accept
+                                expected_init_bond_event = BondMsgType.DEVICE_BONDED
+                                expected_resp_bond_event = None
+                                if dut_io_capability == IoCapabilities.DISPLAY_ONLY:
+                                    if cert_io_capability == IoCapabilities.DISPLAY_YES_NO_IO_CAP:
+                                        expected_resp_ui_event = UiMsgType.DISPLAY_YES_NO_WITH_VALUE
+                                        if dut_auth_reqs in self.mitm_auth_reqs or cert_auth_reqs in self.mitm_auth_reqs:
+                                            expected_init_bond_event = BondMsgType.DEVICE_BOND_FAILED
+                                    elif cert_io_capability == IoCapabilities.KEYBOARD_ONLY:
+                                        expected_resp_ui_event = UiMsgType.DISPLAY_PASSKEY_ENTRY
+                                    elif cert_io_capability == IoCapabilities.DISPLAY_ONLY:
+                                        if dut_auth_reqs in self.mitm_auth_reqs or cert_auth_reqs in self.mitm_auth_reqs:
+                                            expected_init_bond_event = BondMsgType.DEVICE_BOND_FAILED
+                                    elif cert_io_capability == IoCapabilities.NO_INPUT_NO_OUTPUT:
+                                        if dut_auth_reqs in self.mitm_auth_reqs or cert_auth_reqs in self.mitm_auth_reqs:
+                                            expected_init_bond_event = BondMsgType.DEVICE_BOND_FAILED
+                                elif dut_io_capability == IoCapabilities.DISPLAY_YES_NO_IO_CAP:
+                                    expected_init_ui_event = UiMsgType.DISPLAY_YES_NO_WITH_VALUE
+                                    if cert_io_capability == IoCapabilities.DISPLAY_YES_NO_IO_CAP:
+                                        expected_resp_ui_event = UiMsgType.DISPLAY_YES_NO_WITH_VALUE
+                                    elif cert_io_capability == IoCapabilities.KEYBOARD_ONLY:
+                                        expected_init_ui_event = UiMsgType.DISPLAY_PASSKEY
+                                        expected_resp_ui_event = UiMsgType.DISPLAY_PASSKEY_ENTRY
+                                    elif cert_io_capability == IoCapabilities.NO_INPUT_NO_OUTPUT:
+                                        expected_init_ui_event = UiMsgType.DISPLAY_YES_NO  # No value
+                                elif dut_io_capability == IoCapabilities.KEYBOARD_ONLY:
+                                    expected_init_ui_event = UiMsgType.DISPLAY_PASSKEY_ENTRY
+                                    if cert_io_capability == IoCapabilities.DISPLAY_ONLY:
+                                        expected_resp_ui_event = UiMsgType.DISPLAY_PASSKEY
+                                    elif cert_io_capability == IoCapabilities.DISPLAY_YES_NO_IO_CAP:
+                                        expected_resp_ui_event = UiMsgType.DISPLAY_PASSKEY_ENTRY
+                                    elif cert_io_capability == IoCapabilities.KEYBOARD_ONLY:
+                                        expected_resp_ui_event = UiMsgType.DISPLAY_PASSKEY_ENTRY
+                                    elif cert_io_capability == IoCapabilities.NO_INPUT_NO_OUTPUT:
+                                        if dut_auth_reqs in self.mitm_auth_reqs or cert_auth_reqs in self.mitm_auth_reqs:
+                                            expected_init_bond_event = BondMsgType.DEVICE_BOND_FAILED
+                                elif dut_io_capability == IoCapabilities.NO_INPUT_NO_OUTPUT:
+                                    if cert_io_capability == IoCapabilities.DISPLAY_YES_NO_IO_CAP:
+                                        expected_resp_ui_event = UiMsgType.DISPLAY_YES_NO  # No value
+
+                                    if dut_auth_reqs in self.mitm_auth_reqs or cert_auth_reqs in self.mitm_auth_reqs:
+                                        expected_init_bond_event = BondMsgType.DEVICE_BOND_FAILED
+
+                                if cert_oob_present == OobDataPresent.NOT_PRESENT:
+                                    self._run_ssp_numeric_comparison(
+                                        initiator=self.dut_security,
+                                        responder=self.cert_security,
+                                        init_ui_response=init_ui_response,
+                                        resp_ui_response=resp_ui_response,
+                                        expected_init_ui_event=expected_init_ui_event,
+                                        expected_resp_ui_event=expected_resp_ui_event,
+                                        expected_init_bond_event=expected_init_bond_event,
+                                        expected_resp_bond_event=expected_resp_bond_event)
+                                else:
+                                    logging.error("Code path not yet implemented")
+                                    assertThat(False).isTrue()
+
+                                self.dut_security.remove_bond(self.cert_security.get_address(),
+                                                              common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS)
+                                self.cert_security.remove_bond(self.dut_security.get_address(),
+                                                               common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS)
+
+                                self.dut_security.wait_for_bond_event(BondMsgType.DEVICE_UNBONDED)
+                                self.cert_security.wait_for_bond_event(BondMsgType.DEVICE_UNBONDED)
+
+                                self.dut_security.wait_for_disconnect_event()
+                                self.cert_security.wait_for_disconnect_event()
+
+    def test_enable_secure_simple_pairing(self):
+        self.dut_security.enable_secure_simple_pairing()
+        self.cert_security.enable_secure_simple_pairing()
+
+    def test_enable_secure_connections(self):
+        self.dut_security.enable_secure_simple_pairing()
+        self.cert_security.enable_secure_simple_pairing()
+        self.dut_security.enable_secure_connections()
+        self.cert_security.enable_secure_connections()
+
+    def test_get_oob_data_from_dut_controller_p192_present(self):
+        oob_data = self.dut_security.get_oob_data_from_controller(OobDataPresent.P192_PRESENT)
+        assertThat(len(oob_data)).isEqualTo(4)
+        has192C = not all([i == 0 for i in oob_data[0]])
+        has192R = not all([i == 0 for i in oob_data[1]])
+        has256C = not all([i == 0 for i in oob_data[2]])
+        has256R = not all([i == 0 for i in oob_data[3]])
+        assertThat(has192C).isTrue()
+        assertThat(has192R).isTrue()
+        assertThat(has256C).isFalse()
+        assertThat(has256R).isFalse()
+
+    def test_get_oob_data_from_cert_controller_not_present(self):
+        oob_data = self.cert_security.get_oob_data_from_controller(OobDataPresent.NOT_PRESENT)
+        assertThat(len(oob_data)).isEqualTo(4)
+        has192C = not all([i == 0 for i in oob_data[0]])
+        has192R = not all([i == 0 for i in oob_data[1]])
+        has256C = not all([i == 0 for i in oob_data[2]])
+        has256R = not all([i == 0 for i in oob_data[3]])
+        assertThat(has192C).isFalse()
+        assertThat(has192R).isFalse()
+        assertThat(has256C).isFalse()
+        assertThat(has256R).isFalse()
+
+    def test_get_oob_data_from_cert_controller_p192_present_no_secure_connections(self):
+        oob_data = self.cert_security.get_oob_data_from_controller(OobDataPresent.P192_PRESENT)
+        assertThat(len(oob_data)).isEqualTo(4)
+        has192C = not all([i == 0 for i in oob_data[0]])
+        has192R = not all([i == 0 for i in oob_data[1]])
+        has256C = not all([i == 0 for i in oob_data[2]])
+        has256R = not all([i == 0 for i in oob_data[3]])
+        assertThat(has192C).isTrue()
+        assertThat(has192R).isTrue()
+        assertThat(has256C).isFalse()
+        assertThat(has256R).isFalse()
+
+    def test_get_oob_data_from_cert_controller_p192_present(self):
+        self.cert_security.enable_secure_simple_pairing()
+        self.cert_security.enable_secure_connections()
+        oob_data = self.cert_security.get_oob_data_from_controller(OobDataPresent.P192_PRESENT)
+        assertThat(len(oob_data)).isEqualTo(4)
+        has192C = not all([i == 0 for i in oob_data[0]])
+        has192R = not all([i == 0 for i in oob_data[1]])
+        has256C = not all([i == 0 for i in oob_data[2]])
+        has256R = not all([i == 0 for i in oob_data[3]])
+        assertThat(has192C).isTrue()
+        assertThat(has192R).isTrue()
+        assertThat(has256C).isFalse()
+        assertThat(has256R).isFalse()
+
+    def test_get_oob_data_from_cert_controller_p256_present(self):
+        self.cert_security.enable_secure_simple_pairing()
+        self.cert_security.enable_secure_connections()
+        oob_data = self.cert_security.get_oob_data_from_controller(OobDataPresent.P256_PRESENT)
+        assertThat(len(oob_data)).isEqualTo(4)
+        has192C = not all([i == 0 for i in oob_data[0]])
+        has192R = not all([i == 0 for i in oob_data[1]])
+        has256C = not all([i == 0 for i in oob_data[2]])
+        has256R = not all([i == 0 for i in oob_data[3]])
+        assertThat(has192C).isFalse()
+        assertThat(has192R).isFalse()
+        assertThat(has256C).isTrue()
+        assertThat(has256R).isTrue()
+
+    def test_get_oob_data_from_cert_controller_p192_and_p256_present(self):
+        self.cert_security.enable_secure_simple_pairing()
+        self.cert_security.enable_secure_connections()
+        oob_data = self.cert_security.get_oob_data_from_controller(OobDataPresent.P192_AND_256_PRESENT)
+        assertThat(len(oob_data)).isEqualTo(4)
+        has192C = not all([i == 0 for i in oob_data[0]])
+        has192R = not all([i == 0 for i in oob_data[1]])
+        has256C = not all([i == 0 for i in oob_data[2]])
+        has256R = not all([i == 0 for i in oob_data[3]])
+        assertThat(has192C).isTrue()
+        assertThat(has192R).isTrue()
+        assertThat(has256C).isTrue()
+        assertThat(has256R).isTrue()
+
+    def test_successful_dut_initiated_ssp_oob(self):
+        dut_io_capability = IoCapabilities.NO_INPUT_NO_OUTPUT
+        cert_io_capability = IoCapabilities.NO_INPUT_NO_OUTPUT
+        dut_auth_reqs = AuthenticationRequirements.DEDICATED_BONDING_MITM_PROTECTION
+        cert_auth_reqs = AuthenticationRequirements.DEDICATED_BONDING_MITM_PROTECTION
+        cert_oob_present = OobDataPresent.P192_PRESENT
+        self.dut_security.enable_secure_simple_pairing()
+        self.dut_security.enable_secure_connections()
+        self.cert_security.enable_secure_simple_pairing()
+        self.cert_security.enable_secure_connections()
+        self.dut_security.set_io_capabilities(dut_io_capability)
+        self.dut_security.set_authentication_requirements(dut_auth_reqs)
+        self.cert_security.set_io_capabilities(cert_io_capability)
+        self.cert_security.set_authentication_requirements(cert_auth_reqs)
+        init_ui_response = True
+        resp_ui_response = True
+        expected_init_ui_event = None  # None is auto accept
+        expected_resp_ui_event = None  # None is auto accept
+        expected_init_bond_event = BondMsgType.DEVICE_BONDED
+        expected_resp_bond_event = None
+        # get_oob_data returns a tuple of bytes (p192c,p192r,p256c,p256r)
+        local_oob_data = self.cert_security.get_oob_data_from_controller(cert_oob_present)
+        p192_oob_data = local_oob_data[0:2]
+        p256_oob_data = local_oob_data[2:4]
+        self._run_ssp_oob(
+            initiator=self.dut_security,
+            responder=self.cert_security,
+            init_ui_response=init_ui_response,
+            resp_ui_response=resp_ui_response,
+            expected_init_ui_event=expected_init_ui_event,
+            expected_resp_ui_event=expected_resp_ui_event,
+            expected_init_bond_event=expected_init_bond_event,
+            expected_resp_bond_event=expected_resp_bond_event,
+            p192_oob_data=p192_oob_data,
+            p256_oob_data=p256_oob_data)
+        self.dut_security.remove_bond(self.cert_security.get_address(),
+                                      common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS)
+        self.cert_security.remove_bond(self.dut_security.get_address(),
+                                       common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS)
+        self.dut_security.wait_for_bond_event(BondMsgType.DEVICE_UNBONDED)
+        self.cert_security.wait_for_bond_event(BondMsgType.DEVICE_UNBONDED)
+        self.dut_security.wait_for_disconnect_event()
+        self.cert_security.wait_for_disconnect_event()
+
+    def test_successful_dut_initiated_ssp_keyboard(self):
+        dut_io_capability = IoCapabilities.DISPLAY_YES_NO_IO_CAP
+        dut_auth_reqs = AuthenticationRequirements.DEDICATED_BONDING_MITM_PROTECTION
+        dut_oob_present = OobDataPresent.NOT_PRESENT
+        cert_io_capability = IoCapabilities.KEYBOARD_ONLY
+        cert_auth_reqs = AuthenticationRequirements.DEDICATED_BONDING_MITM_PROTECTION
+        cert_oob_present = OobDataPresent.NOT_PRESENT
+        self.dut_security.set_io_capabilities(dut_io_capability)
+        self.dut_security.set_authentication_requirements(dut_auth_reqs)
+        self.cert_security.set_io_capabilities(cert_io_capability)
+        self.cert_security.set_authentication_requirements(cert_auth_reqs)
+
+        self._run_ssp_passkey(
+            initiator=self.dut_security,
+            responder=self.cert_security,
+            expected_init_bond_event=BondMsgType.DEVICE_BONDED,
+            expected_resp_bond_event=BondMsgType.DEVICE_BONDED)
+
+        self.dut_security.remove_bond(self.cert_security.get_address(),
+                                      common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS)
+        self.cert_security.remove_bond(self.dut_security.get_address(),
+                                       common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS)
+
+        self.dut_security.wait_for_bond_event(BondMsgType.DEVICE_UNBONDED)
+        self.cert_security.wait_for_bond_event(BondMsgType.DEVICE_UNBONDED)
+
+        self.dut_security.wait_for_disconnect_event()
+        self.cert_security.wait_for_disconnect_event()
+
+    def test_successful_dut_initiated_pin(self):
+        self.dut_security.set_io_capabilities(IoCapabilities.DISPLAY_YES_NO_IO_CAP)
+        self.dut_security.set_authentication_requirements(AuthenticationRequirements.DEDICATED_BONDING)
+
+        self._run_pin(
+            initiator=self.dut_security,
+            responder=self.cert_security,
+            expected_init_bond_event=BondMsgType.DEVICE_BONDED,
+            expected_resp_bond_event=BondMsgType.DEVICE_BONDED)
+
+        self.dut_security.remove_bond(self.cert_security.get_address(),
+                                      common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS)
+        self.cert_security.remove_bond(self.dut_security.get_address(),
+                                       common.BluetoothAddressTypeEnum.PUBLIC_DEVICE_ADDRESS)
+
+        self.dut_security.wait_for_bond_event(BondMsgType.DEVICE_UNBONDED)
+        self.cert_security.wait_for_bond_event(BondMsgType.DEVICE_UNBONDED)
+
+        self.dut_security.wait_for_disconnect_event()
+        self.cert_security.wait_for_disconnect_event()
+
+    def test_make_sure_oob_data_different(self):
+        oob_data = self.dut_security.get_oob_data_from_controller(OobDataPresent.P192_PRESENT)
+        oob_data2 = self.dut_security.get_oob_data_from_controller(OobDataPresent.P192_PRESENT)
+        assertThat(oob_data).isNotEqualTo(oob_data2)
diff --git a/gd/shim/cert/shim_test.py b/gd/shim/cert/shim_test.py
index bc2fca6..f0679ab 100644
--- a/gd/shim/cert/shim_test.py
+++ b/gd/shim/cert/shim_test.py
@@ -14,24 +14,14 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import os
-import sys
-import logging
-
-from cert.event_stream import EventStream
 from cert.gd_base_test import GdBaseTestClass
-from cert.truth import assertThat
-from facade import common_pb2 as common
-from facade import rootservice_pb2 as facade_rootservice
-from google.protobuf import empty_pb2 as empty_proto
-from shim.facade import facade_pb2 as shim_facade
+from shim.cert.shim_test_lib import ShimTestBase
 
 
-class ShimTest(GdBaseTestClass):
+class ShimTest(GdBaseTestClass, ShimTestBase):
 
     def setup_class(self):
-        super().setup_class(dut_module='SHIM', cert_module='SHIM')
+        GdBaseTestClass.setup_class(self, dut_module='SHIM', cert_module='SHIM')
 
     def test_dumpsys(self):
-        result = self.cert.shim.Dump(empty_proto.Empty())
-        result = self.dut.shim.Dump(empty_proto.Empty())
+        ShimTestBase.test_dumpsys(self, self.dut, self.cert)
diff --git a/gd/shim/cert/shim_test_lib.py b/gd/shim/cert/shim_test_lib.py
new file mode 100644
index 0000000..024dc39
--- /dev/null
+++ b/gd/shim/cert/shim_test_lib.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2020 - 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.
+
+import os
+import sys
+import logging
+
+from cert.event_stream import EventStream
+from cert.truth import assertThat
+from facade import common_pb2 as common
+from facade import rootservice_pb2 as facade_rootservice
+from google.protobuf import empty_pb2 as empty_proto
+from shim.facade import facade_pb2 as shim_facade
+
+
+class ShimTestBase():
+
+    def test_dumpsys(self, dut, cert):
+        result = cert.shim.Dump(empty_proto.Empty())
+        result = dut.shim.Dump(empty_proto.Empty())
diff --git a/gd/shim/cert/stack_test.py b/gd/shim/cert/stack_test.py
index 3daecc0..a43f091 100644
--- a/gd/shim/cert/stack_test.py
+++ b/gd/shim/cert/stack_test.py
@@ -14,16 +14,11 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-import os
-import sys
-
 from cert.gd_base_test import GdBaseTestClass
+from shim.cert.stack_test_lib import StackTestBase
 
 
-class StackTest(GdBaseTestClass):
+class StackTest(GdBaseTestClass, StackTestBase):
 
     def setup_class(self):
         super().setup_class(dut_module='SHIM', cert_module='SHIM')
-
-    def test_test(self):
-        return True
diff --git a/gd/shim/cert/stack_test_lib.py b/gd/shim/cert/stack_test_lib.py
new file mode 100644
index 0000000..485cda4
--- /dev/null
+++ b/gd/shim/cert/stack_test_lib.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2020 - 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.
+
+import os
+import sys
+
+
+class StackTestBase():
+
+    def test_test(self):
+        return True
diff --git a/hci/Android.bp b/hci/Android.bp
index 1ec3014..1dd872c 100644
--- a/hci/Android.bp
+++ b/hci/Android.bp
@@ -18,7 +18,6 @@
 }
 
 // HCI static library for target
-// ========================================================
 cc_library_static {
     name: "libbt-hci",
     defaults: ["libbt-hci_defaults"],
@@ -59,7 +58,6 @@
 }
 
 // HCI unit tests for target
-// ========================================================
 cc_test {
     name: "net_test_hci",
     test_suites: ["device-tests"],
@@ -98,7 +96,6 @@
 }
 
 // HCI native unit tests for target
-// ========================================================
 cc_test {
     name: "net_test_hci_native",
     test_suites: ["device-tests"],
diff --git a/main/Android.bp b/main/Android.bp
index 45c3a9b..629f7d2 100644
--- a/main/Android.bp
+++ b/main/Android.bp
@@ -1,5 +1,4 @@
 // Bluetooth main HW module / shared library for target
-// ========================================================
 package {
     // See: http://go/android-license-faq
     // A large-scale-change added 'default_applicable_licenses' to import
diff --git a/osi/Android.bp b/osi/Android.bp
index 7a979cf..e017bd6 100644
--- a/osi/Android.bp
+++ b/osi/Android.bp
@@ -20,7 +20,6 @@
 }
 
 // Static libraries required by other modules
-// ========================================================
 cc_test_library {
     name: "libosi-AllocationTestHarness",
     defaults: ["fluoride_osi_defaults"],
@@ -46,7 +45,6 @@
 }
 
 // libosi static library for target
-// ========================================================
 cc_library_static {
     name: "libosi",
     defaults: ["fluoride_osi_defaults"],
@@ -95,7 +93,6 @@
 }
 
 // libosi unit tests for target and host
-// ========================================================
 cc_test {
     name: "net_test_osi",
     test_suites: ["device-tests"],
diff --git a/service/Android.bp b/service/Android.bp
index 130b653..b2f23ed 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -21,7 +21,6 @@
 }
 
 // Source variables
-// ========================================================
 btserviceDaemonSrc = [
     "a2dp_sink.cc",
     "a2dp_source.cc",
@@ -66,7 +65,6 @@
 ]
 
 // Main unit test sources. These get built for host and target.
-// ========================================================
 btserviceBaseTestSrc = [
     "hal/fake_bluetooth_av_interface.cc",
     "hal/fake_bluetooth_gatt_interface.cc",
@@ -85,7 +83,6 @@
 ]
 
 // Native system service for target
-// ========================================================
 cc_binary {
     name: "bluetoothtbd",
     defaults: ["fluoride_service_defaults"],
@@ -112,7 +109,6 @@
 
 // Native system service unit tests for target and host
 
-// ========================================================
 cc_test {
     name: "bluetoothtbd_test",
     test_suites: ["device-tests"],
@@ -175,7 +171,6 @@
 }
 
 // Native system service CLI for target
-// ========================================================
 cc_binary {
     name: "bluetooth-cli",
     defaults: ["fluoride_defaults"],
@@ -190,7 +185,6 @@
 }
 
 // Heart Rate GATT service example for target
-// ========================================================
 cc_binary {
     name: "bt-example-hr-server",
     defaults: ["fluoride_defaults"],
diff --git a/service/a2dp_sink.cc b/service/a2dp_sink.cc
index 4d59c7c..e23c588 100644
--- a/service/a2dp_sink.cc
+++ b/service/a2dp_sink.cc
@@ -138,7 +138,6 @@
 }
 
 // A2dpSinkFactory implementation
-// ========================================================
 A2dpSinkFactory::A2dpSinkFactory() = default;
 A2dpSinkFactory::~A2dpSinkFactory() = default;
 
diff --git a/service/a2dp_source.cc b/service/a2dp_source.cc
index d39f202..827cf51 100644
--- a/service/a2dp_source.cc
+++ b/service/a2dp_source.cc
@@ -207,7 +207,6 @@
 }
 
 // A2dpSourceFactory implementation
-// ========================================================
 A2dpSourceFactory::A2dpSourceFactory() = default;
 A2dpSourceFactory::~A2dpSourceFactory() = default;
 
diff --git a/service/avrcp_control.cc b/service/avrcp_control.cc
index 5a2be47..96460ce 100644
--- a/service/avrcp_control.cc
+++ b/service/avrcp_control.cc
@@ -214,7 +214,6 @@
 }
 
 // AvrcpControlFactory implementation
-// ========================================================
 
 AvrcpControlFactory::AvrcpControlFactory() = default;
 AvrcpControlFactory::~AvrcpControlFactory() = default;
diff --git a/service/avrcp_target.cc b/service/avrcp_target.cc
index 264945a..1ba9b21 100644
--- a/service/avrcp_target.cc
+++ b/service/avrcp_target.cc
@@ -365,7 +365,6 @@
 }
 
 // AvrcpTargetFactory implementation
-// ========================================================
 
 AvrcpTargetFactory::AvrcpTargetFactory() = default;
 AvrcpTargetFactory::~AvrcpTargetFactory() = default;
diff --git a/service/gatt_client.cc b/service/gatt_client.cc
index 2c09fa5..e091948 100644
--- a/service/gatt_client.cc
+++ b/service/gatt_client.cc
@@ -24,7 +24,6 @@
 namespace bluetooth {
 
 // GattClient implementation
-// ========================================================
 
 GattClient::GattClient(const Uuid& uuid, int client_id)
     : app_identifier_(uuid), client_id_(client_id) {}
@@ -43,7 +42,6 @@
 int GattClient::GetInstanceId() const { return client_id_; }
 
 // GattClientFactory implementation
-// ========================================================
 
 GattClientFactory::GattClientFactory() {
   hal::BluetoothGattInterface::Get()->AddClientObserver(this);
diff --git a/service/gatt_server.cc b/service/gatt_server.cc
index ac2996b..701679f 100644
--- a/service/gatt_server.cc
+++ b/service/gatt_server.cc
@@ -27,7 +27,6 @@
 namespace bluetooth {
 
 // GattServer implementation
-// ========================================================
 
 GattServer::GattServer(const Uuid& uuid, int server_id)
     : app_identifier_(uuid), server_id_(server_id), delegate_(nullptr) {}
@@ -566,7 +565,6 @@
 }
 
 // GattServerFactory implementation
-// ========================================================
 
 GattServerFactory::GattServerFactory() {
   hal::BluetoothGattInterface::Get()->AddServerObserver(this);
diff --git a/service/ipc/binder/remote_callback_list.h b/service/ipc/binder/remote_callback_list.h
index 8c1f6ac..e25e550 100644
--- a/service/ipc/binder/remote_callback_list.h
+++ b/service/ipc/binder/remote_callback_list.h
@@ -96,7 +96,6 @@
 };
 
 // Template Implementation details below
-// ========================================================
 
 using android::IBinder;
 using android::IInterface;
diff --git a/service/ipc/binder/remote_callback_map.h b/service/ipc/binder/remote_callback_map.h
index b07de44..7f31b53 100644
--- a/service/ipc/binder/remote_callback_map.h
+++ b/service/ipc/binder/remote_callback_map.h
@@ -109,7 +109,6 @@
 };
 
 // Template Implementation details below
-// ========================================================
 
 using android::IBinder;
 using android::IInterface;
diff --git a/service/low_energy_advertiser.cc b/service/low_energy_advertiser.cc
index 57912b7..7b0e22a 100644
--- a/service/low_energy_advertiser.cc
+++ b/service/low_energy_advertiser.cc
@@ -119,7 +119,6 @@
 }  // namespace
 
 // LowEnergyAdvertiser implementation
-// ========================================================
 
 LowEnergyAdvertiser::LowEnergyAdvertiser(const Uuid& uuid, int advertiser_id)
     : app_identifier_(uuid),
@@ -287,7 +286,6 @@
 }
 
 // LowEnergyAdvertiserFactory implementation
-// ========================================================
 
 LowEnergyAdvertiserFactory::LowEnergyAdvertiserFactory() {}
 
diff --git a/service/low_energy_client.cc b/service/low_energy_client.cc
index bbc130f..bbed569 100644
--- a/service/low_energy_client.cc
+++ b/service/low_energy_client.cc
@@ -32,7 +32,6 @@
 namespace bluetooth {
 
 // LowEnergyClient implementation
-// ========================================================
 
 LowEnergyClient::LowEnergyClient(Adapter& adapter, const Uuid& uuid,
                                  int client_id)
@@ -196,7 +195,6 @@
 }
 
 // LowEnergyClientFactory implementation
-// ========================================================
 
 LowEnergyClientFactory::LowEnergyClientFactory(Adapter& adapter)
     : adapter_(adapter) {
diff --git a/service/low_energy_scanner.cc b/service/low_energy_scanner.cc
index fe65a7a..1fcf3a2 100644
--- a/service/low_energy_scanner.cc
+++ b/service/low_energy_scanner.cc
@@ -64,7 +64,6 @@
 }  // namespace
 
 // LowEnergyScanner implementation
-// ========================================================
 
 LowEnergyScanner::LowEnergyScanner(Adapter& adapter, const Uuid& uuid,
                                    int scanner_id)
@@ -159,7 +158,6 @@
 }
 
 // LowEnergyScannerFactory implementation
-// ========================================================
 
 LowEnergyScannerFactory::LowEnergyScannerFactory(Adapter& adapter)
     : adapter_(adapter) {
diff --git a/stack/Android.bp b/stack/Android.bp
index c3008d8..b157d4c 100644
--- a/stack/Android.bp
+++ b/stack/Android.bp
@@ -25,7 +25,6 @@
 }
 
 // Bluetooth stack static library for target
-// ========================================================
 cc_library_static {
     name: "libbt-stack",
     defaults: ["fluoride_defaults"],
@@ -217,7 +216,6 @@
 }
 
 // Bluetooth stack unit tests for target
-// ========================================================
 cc_test {
     name: "net_test_stack",
     defaults: ["fluoride_defaults"],
@@ -335,7 +333,6 @@
 }
 
 // Bluetooth stack smp unit tests for target
-// ========================================================
 cc_test {
     name: "net_test_stack_smp",
     defaults: ["fluoride_defaults"],
@@ -376,7 +373,6 @@
 }
 
 // Bluetooth stack multi-advertising unit tests for target
-// ========================================================
 cc_test {
     name: "net_test_stack_multi_adv",
     defaults: ["fluoride_defaults"],
@@ -411,7 +407,6 @@
 }
 
 // Bluetooth stack advertise data parsing unit tests for target
-// =============================================================
 cc_test {
     name: "net_test_stack_ad_parser",
     defaults: ["fluoride_defaults"],
@@ -430,7 +425,6 @@
 }
 
 // Bluetooth stack connection multiplexing
-// ========================================================
 cc_test {
     name: "net_test_gatt_conn_multiplexing",
     defaults: ["fluoride_defaults"],
diff --git a/stack/test/fuzzers/a2dp/codec/a2dpCodecHelperFunctions.h b/stack/test/fuzzers/a2dp/codec/a2dpCodecHelperFunctions.h
index 5b3848a..dec0e49 100644
--- a/stack/test/fuzzers/a2dp/codec/a2dpCodecHelperFunctions.h
+++ b/stack/test/fuzzers/a2dp/codec/a2dpCodecHelperFunctions.h
@@ -20,7 +20,6 @@
 #include <fuzzer/FuzzedDataProvider.h>
 #include <vector>
 
-// =============================================================================
 static const std::vector<const btav_a2dp_codec_index_t> CODEC_INDEX_ENUM_VALS =
     {BTAV_A2DP_CODEC_INDEX_SOURCE_MIN,
      BTAV_A2DP_CODEC_INDEX_SOURCE_SBC,
@@ -107,7 +106,7 @@
 
   return config;
 }
-// =============================================================================
+
 tA2DP_ENCODER_INIT_PEER_PARAMS getArbitraryA2dpEncoderInitPeerParams(
     FuzzedDataProvider* fdp) {
   tA2DP_ENCODER_INIT_PEER_PARAMS params;
@@ -118,7 +117,7 @@
 
   return params;
 }
-// =============================================================================
+
 #include "bt_types.h"
 #define MAX_BTHDR_SIZE 1024
 std::shared_ptr<BT_HDR> getArbitraryBtHdr(FuzzedDataProvider* fdp) {
@@ -143,7 +142,7 @@
 
   return bt_hdr;
 }
-// =============================================================================
+
 #include "bta/av/bta_av_int.h"
 tBT_A2DP_OFFLOAD generateArbitrarytA2dpOffload(FuzzedDataProvider* fdp) {
   tBT_A2DP_OFFLOAD retval;
@@ -166,6 +165,5 @@
 
   return retval;
 }
-// =============================================================================
 
 #endif  // A2DP_CODEC_HELPERFUNCTIONS_H_
diff --git a/test/suite/Android.bp b/test/suite/Android.bp
index 25b6e38..18050bc 100644
--- a/test/suite/Android.bp
+++ b/test/suite/Android.bp
@@ -1,5 +1,4 @@
 // Bluetooth test suite for target
-// ========================================================
 package {
     // See: http://go/android-license-faq
     // A large-scale-change added 'default_applicable_licenses' to import
@@ -37,7 +36,6 @@
 }
 
 // Bluetooth test suite for target
-// ========================================================
 cc_test {
     name: "net_test_rfcomm_suite",
     defaults: ["fluoride_defaults"],
diff --git a/tools/bdtool/Android.mk.disabled b/tools/bdtool/Android.mk.disabled
index 4c8321e..76b8dce 100644
--- a/tools/bdtool/Android.mk.disabled
+++ b/tools/bdtool/Android.mk.disabled
@@ -17,7 +17,6 @@
 LOCAL_PATH := $(call my-dir)
 
 # Bluetooth tools for target
-# ========================================================
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := optional
diff --git a/tools/hci/Android.mk.disabled b/tools/hci/Android.mk.disabled
index 6439dfc..9244f45 100644
--- a/tools/hci/Android.mk.disabled
+++ b/tools/hci/Android.mk.disabled
@@ -17,7 +17,6 @@
 LOCAL_PATH := $(call my-dir)
 
 # Bluetooth HCI tools for target
-# ========================================================
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := optional
diff --git a/types/Android.bp b/types/Android.bp
index a1fea28..2a6e0dd 100644
--- a/types/Android.bp
+++ b/types/Android.bp
@@ -43,7 +43,6 @@
     export_header_lib_headers: ["libbluetooth-types-header"],
 }
 
-// ========================================================
 cc_test {
     name: "net_test_types",
     test_suites: ["device-tests"],
diff --git a/utils/Android.bp b/utils/Android.bp
index befc5cf..725335f 100644
--- a/utils/Android.bp
+++ b/utils/Android.bp
@@ -1,5 +1,4 @@
 // Utils static library for target
-// ========================================================
 package {
     // See: http://go/android-license-faq
     // A large-scale-change added 'default_applicable_licenses' to import
diff --git a/vendor_libs/test_vendor_lib/Android.bp b/vendor_libs/test_vendor_lib/Android.bp
index 160dca8..e45b6ea 100644
--- a/vendor_libs/test_vendor_lib/Android.bp
+++ b/vendor_libs/test_vendor_lib/Android.bp
@@ -1,5 +1,4 @@
 // simulation library for testing virtual devices
-// ========================================================
 package {
     // See: http://go/android-license-faq
     // A large-scale-change added 'default_applicable_licenses' to import
@@ -94,7 +93,6 @@
 }
 
 // test-vendor unit tests for host
-// ========================================================
 cc_test_host {
     name: "test-vendor_test_host",
     defaults: [
@@ -130,7 +128,6 @@
 }
 
 // Linux RootCanal Executable
-// ========================================================
 cc_binary_host {
     name: "root-canal",
     defaults: [
diff --git a/vendor_libs/test_vendor_lib/types/Android.bp b/vendor_libs/test_vendor_lib/types/Android.bp
index 028538a..e68af2a 100644
--- a/vendor_libs/test_vendor_lib/types/Android.bp
+++ b/vendor_libs/test_vendor_lib/types/Android.bp
@@ -31,7 +31,6 @@
     export_header_lib_headers: ["libbt-rootcanal-types-header"],
 }
 
-// ========================================================
 cc_test {
     name: "rootcanal-test_types",
     test_suites: ["device-tests"],